I want to write System.out messages to another OutputStream, but I still want to have the standard output too.
I found an answer on this similar matter Copy and Redirecting System.err Stream:
In short what you need to do is define a PrintStream which can duplicate its output, assign this using:
System.setErr(doubleLoggingPrintStream)
This is what I made so far:
public class DoublerPrintStream extends PrintStream {
private OutputStream forwarder;
public DoublerPrintStream(OutputStream target, OutputStream forward) {
super(target, true);
this.forwarder = forward;
}
#Override
public void write(byte[] b) throws IOException {
try {
synchronized (this) {
super.write(b);
forwarder.write(b);
forwarder.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
}
}
#Override
public void write(byte[] buf, int off, int len) {
try {
synchronized (this) {
super.write(buf, off, len);
forwarder.write(buf, off, len);
forwarder.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
}
}
#Override
public void write(int b) {
try {
synchronized (this) {
super.write(b);
forwarder.write(b);
forwarder.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
}
}
#Override
public void flush() {
super.flush();
try { forwarder.flush(); } catch (IOException e) { }
}
#Override
public void close() {
super.close();
if (forwarder != null) {
try {
forwarder.close();
} catch (Exception e) {}
}
}
}
}
This is just a draft, but is this a good approach? I'm kind of clueless whether there is a better solution, so I'm looking for confirmation, ideas and suggestions.
I think there is an Apache library that does that (TeeOutputStream , thanks #Thilo) but your implementation looks good to me.
Related
public class Sample {
public static void main(String[] args) {
method();
}
public static void method()
{
try {
System.out.println("function");
throw new StaleElementReferenceException("thih sexception occured");
}
catch (StaleElementReferenceException e) {
method();
}
catch (Exception e) {
System.out.println("AssertFail");
}
}
}
how to avoid Infinite Recursion in a non-return method with Try catch...For Example this code below...when the StaleElementException Occurs only once i want to execute "functions after Exception , if the Stale Element occurs the second time i want it to go to Exception catch and print Assert fail..how?
public class Sample {
public static void main(String[] args) {
method(false);
}
public static void method(boolean calledFromCatchBlock)
{
try {
System.out.println("function");
if(!calledFromCatchBlock) {
throw new StaleElementReferenceException("thih sexception occured");
} else {
throw new Exception();
}
} catch (StaleElementReferenceException e) {
method(true);
} catch (Exception e) {
System.out.println("AssertFail");
}
}
}
You should store somehow the state when you throw an exception (e.g. a boolean flag) outside method(), check this state and throw modified exception next time:
private static boolean alreadyThrown = false;
public static void method()
{
try {
System.out.println("function");
if (alreadyThrown) {
throw new RuntimeException("another exception occured");
} else {
alreadyThrown = true;
throw new StaleElementReferenceException("this exception occured");
}
}
catch (StaleElementReferenceException e) {
method();
}
catch (Exception e) {
System.out.println("AssertFail");
}
}
Or you could provide some argument to the method(int arg) and check its value in a similar way:
public static void main(String[] args) {
method(1);
}
public static void method(int arg)
{
try {
System.out.println("function");
if (arg > 1) {
throw new RuntimeException("another exception occured");
} else {
throw new StaleElementReferenceException("this exception occured");
}
}
catch (StaleElementReferenceException e) {
method(arg + 1);
}
catch (Exception e) {
System.out.println("AssertFail");
}
}
I have just read, that in Java the classes PrintStream and PrintWriter don't throw checked exceptions. Instead they are using a kind of an error flag which I can read invoking the method boolean checkError() (API link).
Now, I am asking myself how to find out the reason why the exception occurred. The information that there was an exception is sometimes maybe not enough, or?
Based on the source code, it looks like they discard the exception. All of the catch blocks look like this:
try {
...
}
catch (IOException x) {
trouble = true; // (x is ignored)
}
So the most straightforward solution is probably to not use PrintStream, if possible.
One workaround could be to extend PrintStream and wrap the output in another OutputStream which captures the exception before PrintStream catches (and discards) it. Something like this:
package mcve.util;
import java.io.*;
public class PrintStreamEx extends PrintStream {
public PrintStreamEx(OutputStream out) {
super(new HelperOutputStream(out));
}
/**
* #return the last IOException thrown by the output,
* or null if there isn't one
*/
public IOException getLastException() {
return ((HelperOutputStream) out).lastException;
}
#Override
protected void clearError() {
super.clearError();
((HelperOutputStream) out).setLastException(null);
}
private static class HelperOutputStream extends FilterOutputStream {
private IOException lastException;
private HelperOutputStream(OutputStream out) {
super(out);
}
private IOException setLastException(IOException e) {
return (lastException = e);
}
#Override
public void write(int b) throws IOException {
try {
super.write(b);
} catch (IOException e) {
throw setLastException(e);
}
}
#Override
public void write(byte[] b) throws IOException {
try {
super.write(b);
} catch (IOException e) {
throw setLastException(e);
}
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
try {
super.write(b, off, len);
} catch (IOException e) {
throw setLastException(e);
}
}
#Override
public void flush() throws IOException {
try {
super.flush();
} catch (IOException e) {
throw setLastException(e);
}
}
#Override
public void close() throws IOException {
try {
super.close();
} catch (IOException e) {
throw setLastException(e);
}
}
}
}
While it is qute clear how to configure multi-threaded jBehave run,
it is not qute clear for me how to deal with logging mess.
What are the options here?
Rederect application's output to std out (JBehave's one is already there). Notice follow=true
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.follow=true
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
log4j.rootLogger=error, stdout
log4j.logger.com.company.app.interesting.module=debug
...
Per thread file output
#SuppressWarnings("resource")
public class ThreadFileOutput extends PrintStream {
private static ThreadLocal<FileOutputStream> threadOutput = new ThreadLocal<>();
private static PrintStream stdout = System.out;
private static PrintStream stderr = System.err;
static {
System.setOut(new ThreadFileOutput(stdout));
System.setErr(new ThreadFileOutput(stderr));
}
public ThreadFileOutput(OutputStream out) {
super(out);
}
public static void startThreadOutputRedirect(FileOutputStream stream) {
threadOutput.set(stream);
}
public static void stopThreadOutputRedirect() {
FileOutputStream stream = threadOutput.get();
if (stream != null) {
threadOutput.set(null);
try {
stream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void forceOut(String line) {
stdout.println(line);
}
public static void forceErr(String line) {
stderr.println(line);
}
#Override
public void write(byte[] b) throws IOException {
FileOutputStream stream = threadOutput.get();
if (stream != null) {
try {
stream.write(b);
} catch (IOException e) {
threadOutput.set(null);
throw new RuntimeException(e);
}
} else {
super.write(b);
}
}
#Override
public void write(int b) {
FileOutputStream stream = threadOutput.get();
if (stream != null) {
try {
stream.write(b);
} catch (IOException e) {
threadOutput.set(null);
throw new RuntimeException(e);
}
} else {
super.write(b);
}
}
#Override
public void write(byte[] buf, int off, int len) {
FileOutputStream stream = threadOutput.get();
if (stream != null) {
try {
stream.write(buf, off, len);
} catch (IOException e) {
threadOutput.set(null);
throw new RuntimeException(e);
}
} else {
super.write(buf, off, len);
}
}
#Override
public void flush() {
FileOutputStream stream = threadOutput.get();
if (stream != null) {
try {
stream.flush();
} catch (IOException e) {
threadOutput.set(null);
throw new RuntimeException(e);
}
} else {
super.flush();
}
}
}
Start redirecting thread output to the file befor the test and stop it after the test
startThreadOutputRedirect(new FileOutputStream(new File(workDirRelative(story.getPath()))));
stopThreadOutputRedirect();
in
/**
* JBehave to TC integration.
*/
public class TeamCityReporter extends NullStoryReporter {
private static final LookupTranslator ESCAPE_TABLE = new LookupTranslator(new String[][] {
{ "'", "|'" },
{ "\n", "|n" },
{ "\r", "|r" },
{ "\\u", "|0x" },
{ "|", "||" },
{ "[", "|[" },
{ "]", "|]" }
});
private ThreadLocal<Story> story = new ThreadLocal<>();
private ThreadLocal<String> scenario = new ThreadLocal<>();
#Override
#SuppressWarnings("resource")
public void beforeStory(Story story, boolean givenStory) {
this.story.set(story);
this.scenario.set(null);
try {
startThreadOutputRedirect(new FileOutputStream(new File(workDirRelative(story.getPath()))));
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
forceOut(format("##teamcity[testSuiteStarted name='%s']", escape(story.getPath())));
out.println(story.getPath());
super.beforeStory(story, givenStory);
}
#Override
public void afterStory(boolean givenStory) {
forceOut(format("##teamcity[testSuiteFinished name='%s']", escape(story.get().getPath())));
stopThreadOutputRedirect();
super.afterStory(givenStory);
}
#Override
public void beforeScenario(String scenario) {
this.scenario.set(scenario);
forceOut(format("##teamcity[testStarted name='%s']", escape(scenario)));
out.println(scenario);
super.beforeScenario(scenario);
}
#Override
public void afterScenario() {
forceOut(format("##teamcity[testFinished name='%s']", escape(scenario.get())));
this.scenario.set(null);
super.afterScenario();
}
#Override
public void beforeStep(String step) {
out.println(format("\n%s\n", step));
super.beforeStep(step);
}
#Override
public void storyNotAllowed(Story story, String filter) {
forceOut(format("##teamcity[message text='story not allowed %s' status='WARNING']", escape(story.getName())));
out.println(format("\n(Not allowed) %s\n", story.getPath()));
super.storyNotAllowed(story, filter);
}
#Override
public void failed(String step, Throwable cause) {
forceOut(format("##teamcity[testFailed name='%s' message='%s' details='%s']", new String[] { escape(scenario.get()), escape(getRootCauseMessage(cause)), escape(getStackTrace(cause)) }));
out.println(format("\n(Failed) %s\n", step));
cause.printStackTrace();
super.failed(step, cause);
}
#Override
public void pending(String step) {
forceOut(format("##teamcity[testFailed name='%s' message='Step in PENDING state: %s']", escape(scenario.get()), escape(step)));
out.println(format("\n(Pending) %s\n", step));
super.pending(step);
}
#Override
public void notPerformed(String step) {
out.println(format("\n(Not performed) %s\n", step));
super.notPerformed(step);
}
private static String escape(String string) {
return ESCAPE_TABLE.translate(string);
}
}
Turn on parallel JBehave execution
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {
...
})
public class Stories extends JUnitStories {
#Before
public void setUp() throws Exception {
configuredEmbedder()
// turn on parallel test execution
.useExecutorService(newFixedThreadPool(30, new ThreadFactoryBuilder()
.setDaemon(true)
.build()));
configuredEmbedder()
.embedderControls()
...
// don't use it this way not to produce multiThreading = true and delayed StoryReporter callbacks
// and you will see your application logging 'for each jbehave step'
// .useThreads(30);
}
#Override
public Configuration configuration() {
return new MostUsefulConfiguration()
...
.useStoryReporterBuilder(new StoryReporterBuilder()
...
.withFormats(HTML)
.withReporters(teamCityReporter));
}
}
As a result, there will be a log file for each parallel test having both test output and application output (only the code being executed by test runner thread).
Bonus - TeamCityReporter (JBehave to TC integration) will successfully count running parallel tests in real time and report any test failures on TC GUI. Configure test output directory as TC artifact path to access each test output.
I'm trying to consolidate 2 methods into 1, because they handle exceptions the same way. I know in C# you can pass functions/actions as parameters into other functions. I tried creating a generic method to invoke a function, but can't seem to figure it out.
public String getTheStuff(String client) {
try {
return extService.getProduct(client);
} catch (UIException e) {
notHealthy();
} catch (HostException e) {
notHealthy();
} catch (Exception e) {
Throwables.propagate(e);
}
}
public CustomType getsomeMoreStuff(String source, int offset) {
try {
return extService.getMetrics(source, offset);
} catch (UIException e) {
notHealthy();
} catch (HostException e) {
notHealthy();
} catch (Exception e) {
Throwables.propagate(e);
}
}
What I'm looking for is something like
public T invokeExtService(Function functionToInvoke, Parameters[] params){
try {
return functionToInvoke.Invoke(params);
} catch (UIException e) {
notHealthy();
} catch (HostException e) {
notHealthy();
} catch (Exception e) {
Throwables.propagate(e);
}
}
As #LouisWasserman said, this would be much nicer in Java 8, but how about something like this (untested):
public <T> T invoke(Callable<T> function) {
try {
return function.call();
} catch (UIException e) {
notHealthy();
} catch (HostException e) {
notHealthy();
} catch (Exception e) {
Throwables.propagate(e);
}
}
public String getTheStuff(final String client) {
return invoke(new Callable<String>() {
#Override
public String call() {
return extService.getProduct(client);
}
});
}
public CustomType getsomeMoreStuff(final String source, final int offset) {
return invoke(new Callable<CustomType>() {
#Override
public CustomType call() {
return extService.getMetrics(source, offset);
}
});
}
To be honest, I'm not sure how worthwhile this is considering how short your methods are (and they could be even shorter with multi-catch).
For one of my projects, I implement a Java 7 FileSystem over the Box API Java SDK (the new one).
However, for downloading files, when you want to have a stream to the content, it only provides methods taking OutputStream as an argument; specifically, I am using this one at the moment.
But this doesn't sit well with the JDK API; I need to be able to implement FileSystemProvider#newInputStream()... Therefore I elected to use Pipe{Input,Output}Stream.
Moreover, since the Box SDK API methods are synchronous (not that it matters here), I wrap them in a Future. My code is as follows (imports ommitted for brevity):
#ParametersAreNonnullByDefault
public final class BoxFileInputStream
extends InputStream
{
private final Future<Void> future;
private final PipedInputStream in;
public BoxFileInputStream(final ExecutorService executor,
final BoxFile file)
{
in = new PipedInputStream(16384);
future = executor.submit(new Callable<Void>()
{
#Override
public Void call()
throws IOException
{
try {
file.download(new PipedOutputStream(in));
return null;
} catch (BoxAPIException e) {
throw BoxIOException.wrap(e);
}
}
});
}
#Override
public int read()
throws IOException
{
try {
return in.read();
} catch (IOException e) {
future.cancel(true);
throw new BoxIOException("download failure", e);
}
}
#Override
public int read(final byte[] b)
throws IOException
{
try {
return in.read(b);
} catch (IOException e) {
future.cancel(true);
throw new BoxIOException("download failure", e);
}
}
#Override
public int read(final byte[] b, final int off, final int len)
throws IOException
{
try {
return in.read(b, off, len);
} catch (IOException e) {
future.cancel(true);
throw new BoxIOException("download failure", e);
}
}
#Override
public long skip(final long n)
throws IOException
{
try {
return in.skip(n);
} catch (IOException e) {
future.cancel(true);
throw new BoxIOException("download failure", e);
}
}
#Override
public int available()
throws IOException
{
try {
return in.available();
} catch (IOException e) {
future.cancel(true);
throw new BoxIOException("download failure", e);
}
}
#Override
public void close()
throws IOException
{
IOException streamException = null;
IOException futureException = null;
try {
in.close();
} catch (IOException e) {
streamException = e;
}
try {
future.get(5L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
futureException = new BoxIOException("donwload interrupted", e);
} catch (ExecutionException e) {
futureException = new BoxIOException("download failure",
e.getCause());
} catch (CancellationException e) {
futureException = new BoxIOException("download cancelled", e);
} catch (TimeoutException e) {
futureException = new BoxIOException("download timeout", e);
}
if (futureException != null) {
if (streamException != null)
futureException.addSuppressed(streamException);
throw futureException;
}
if (streamException != null)
throw streamException;
}
#Override
public synchronized void mark(final int readlimit)
{
in.mark(readlimit);
}
#Override
public synchronized void reset()
throws IOException
{
try {
in.reset();
} catch (IOException e) {
future.cancel(true);
throw new BoxIOException("download failure", e);
}
}
#Override
public boolean markSupported()
{
return in.markSupported();
}
}
The code consistenly fails with the following stack trace (that is in int read(byte[]):
Exception in thread "main" com.github.fge.filesystem.box.exceptions.BoxIOException: download failure
at com.github.fge.filesystem.box.io.BoxFileInputStream.read(BoxFileInputStream.java:81)
at java.nio.file.Files.copy(Files.java:2735)
at java.nio.file.Files.copy(Files.java:2854)
at java.nio.file.CopyMoveHelper.copyToForeignTarget(CopyMoveHelper.java:126)
at java.nio.file.Files.copy(Files.java:1230)
at Main.main(Main.java:37)
[ IDEA specific stack trace elements follow -- irrelevant]
Caused by: java.io.IOException: Pipe broken
at java.io.PipedInputStream.read(PipedInputStream.java:322)
at java.io.PipedInputStream.read(PipedInputStream.java:378)
at java.io.InputStream.read(InputStream.java:101)
at com.github.fge.filesystem.box.io.BoxFileInputStream.read(BoxFileInputStream.java:78)
... 10 more
But when it fails, the download is already complete...
OK, the thing is, I can grab the file size and hack around it but I'd prefer not to if at all possible; how can I modify this code so as to avoid EPIPE?
The SDK also provides BoxAPIRequest and BoxAPIResponse classes that let you make manual requests for advanced use-cases. These classes still automatically handle authentication, errors, back-off, etc. but give you more granular control over the request.
In your case, you could do make a download request manually by doing:
// Note: this example assumes you already have a BoxAPIConnection.
URL url = new URL("files/" + file.getID() + "/content")
BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
BoxAPIResponse response = request.send();
InputStream bodyStream = response.getBody();
// Use the stream.
response.disconnect();
Well, I found the solution, although I am not very satisfied with it...
Since I can know the file size which I try to open an inputstream on, I just pick the size and decrease it by the amount of bytes read -- unless the size reaches 0, in this case all read methods return -1.