Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.out closed? Can I reopen it?

Tags:

java

io

I was helping a friend to write some Java code, who doesn't know a lot about Java. So I wrote him some helper functions to easily get things done that are a little quirky in his eyes. One of them is a function, that writes a String to an OutputStream. Have a look:

public void write(String txt, OutputStream out) {
    PrintWriter printer = new PrintWriter(out);
    printer.print(txt);
    printer.close();
}

Now, you can use that easily in different ways to write wherever you want. For example you could do that:

(new StreamHelper()).write("Hello Test", System.out);

Doing that I found out that afterwards System.out.println() doesn't write anything to the shell anymore. So I think that maybe printer.close() automatically also closed System.out and I wonder how to reactivate it that I can use it after this function is finished again.

Is my assumption correct? (How could I have found out without asking here?)

How can I continue to use System.out after a call to the write() function?

Are there better ways to write such a helper function?

like image 488
erikbstack Avatar asked Jan 20 '12 12:01

erikbstack


7 Answers

The general contract for OutputStream's close:

public void close() throws IOException Closes this output stream and releases any system resources associated with this stream. The general contract of close is that it closes the output stream. A closed stream cannot perform output operations and cannot be reopened.

PrintStream's

public void close() Close the stream. This is done by flushing the stream and then closing the underlying output stream.

The only advice I can give you is that you should not write asymmetrical code, that is, don't delegate the closing of resources your code has created to somewhere else.

Even if in your case it might seemed sensible to close the wrapper stream, the fact is that you should not because you are closing a stream opened somewhere else.

In short:

public void write(String txt, OutputStream out) {
    PrintWriter printer = new PrintWriter(out);
    printer.print(txt);
    printer.flush();
    //it is very unpolite to close someone else's streams!
    //printer.close();
}

Oh, and by the way, you may want to change the function name to print, rather than write.

like image 88
fortran Avatar answered Oct 25 '22 11:10

fortran


Like the others have said, a stream should be closed where it was opened. However, you could write a facade to protect a stream from getting closed like this:

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class UnclosableOutputStream extends FilterOutputStream {

    public UnclosableOutputStream(OutputStream out) {
        super(out);
    }

    @Override
    public void close() throws IOException {
        out.flush();
    }
}

and use it like this:

new StreamHelper().write("Hello Test", new UnclosableOutputStream(System.out));

Might come in handy for test scenarios etc.

like image 39
Tim Büthe Avatar answered Oct 25 '22 11:10

Tim Büthe


System.out is a PrintStream, therefore the code you provide above has literally no advantage over just calling System.out.print directly. The reason it is not writing anymore is that close did, in fact, close System.out.

If this is for logging, learn log4j for your friend or help him learn it. Log4j handles situations where you need to write to a file stream, standard out, etc. simultaneously quite well.

like image 35
Mike Thomsen Avatar answered Oct 25 '22 10:10

Mike Thomsen


You can check if the out input being passed is System.out and selectively decide not to close.

The flush() call is necessary. Note that I am doing == check since the references will be identical if you were to invoke this write method with a System.out argument.

public void write(String txt, OutputStream out) {
    PrintWriter printer = new PrintWriter(out);
    printer.print(txt);
    printer.flush();

    if(out != System.out) {
        printer.close();
    }
}

But honestly, I would keep a different method for closing or call this method writeAndClose in order to avoid the confusion.

If you want to maintain abstraction (as hinted by @Urs), do the below. But I don't see the point of this rather over-engineering

public void write(String txt, OutputStream out) {
    PrintWriter printer = new PrintWriter(out);
    printer.print(txt);
    printer.flush();
}

public void close(OutputStream out) {
    out.close();
}
like image 30
adarshr Avatar answered Oct 25 '22 11:10

adarshr


If you want to try the output on JVM console, you'd better to get a Console instance with System.console(). And then get the PrintWriter with console.writer() method. After using the PrintWriter, you can close it. Based on the console description of JavaDoc,

Invoking close() on the objects returned by the reader() and the writer() will not close the underlying stream of those objects.

You can do this again and again. No need to worrying about reopen the stream.

like image 45
user7755940 Avatar answered Oct 25 '22 10:10

user7755940


Do what Stas Kurilin suggests.

In general, streams should be closed by the party that opened/created them.

In your method, just flush the stream. Close it wherever it was opened when it is no longer needed.

like image 21
Urs Reupke Avatar answered Oct 25 '22 11:10

Urs Reupke


Let's look at this from the caller's point of view.

The caller has an OutputStream of some sort, and calls a method called write(). After the call completes, the caller discovers that the stream has been closed.

In my view, your write() method simply should not call printer.close(). The latter closes the stream provided by the caller and is probably not what the caller expects.

If you need to flush the stream, you could use flush().

like image 38
NPE Avatar answered Oct 25 '22 11:10

NPE