Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6552811

PrintStream class implementation is inconsistent with its specification

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 1.4.2
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:
    • Subcomponent:
    • CPU:
      x86
    • OS:
      linux

      Description

      Specification of 2 PrintStream's methods looks as follows:

      -------------------------------------
      public void close()

          Closes the stream. This is done by flushing the stream and then closing the underlying output stream.

      --------------------------------------------------------

      public boolean checkError()

          Flushes the stream and checks its error state. The internal error state is set to true when the underlying output stream throws an IOException other than InterruptedIOException, and when the setError method is invoked. If an operation on the underlying output stream throws an InterruptedIOException, then the PrintStream converts the exception back into an interrupt by doing:

           Thread.currentThread().interrupt();
       

          or the equivalent.

          Returns:
              true if and only if this stream has encountered an IOException other than InterruptedIOException, or the setError method has been invoked
      ---------------------------------------------

      Let's consider situation when underlying stream is one that doesn't care about IOE throwing in case of IO related invocations made after 'close' method invocation. It may be ByteArrayOutputStream (specification of it's 'close' method contains following phrase: "The methods in this class can be called after the stream has been closed without generating an IOException.") or some class defined by application:

      public class PS extends PrintStream {
          PS(OutputStream os) {
              super(os);
          }

          protected OutputStream getOut() {
              return out;
          }

          public static void main(String[] args) {
              PS ps = new PS(new ByteArrayOutputStream());
              ps.print("Hello");
              System.out.println("out: " + ps.getOut());
              ps.close();
              System.out.println("out: " + ps.getOut());
              System.out.println("checkError: " + ps.checkError());
              ps.flush();
              System.out.println("checkError: " + ps.checkError());
          }
      }
      Accordingly with specification:
      1. First invocation of 'getOut' should return "Hello"
      2. 'ps.close()' means flushing of underlying ByteArrayOutputStream instance followed by it's closing (and nothing but this).
      3. So second invocation of 'getOut' should return "Hello" again.
      4. first invocation of 'ps.checkError' means another flushing of underlying ByteArrayOutputStream instance followed by checking if IOException was thrown by underlying stream or not. Accordingly with ByteArrayOutputStream specification IOException should not be thrown so false should be returned here.
      5. 'ps.flush()' means writing buffered output bytes to underlyin stream (there are no ones here) followed by flusing underlying stream (which should not cause IOException here)
      6. second invocation of 'ps.checkError' means yet another flushing of underlying ByteArrayOutputStream instance followed by checking if IOException was thrown by underlying
      stream or not. So false should be returned here too.

      All above means that following output should be produced:

      out: Hello
      out: Hello
      checkError: false
      checkError: false

      However JDK output is following:

      out: Hello
      out: null
      checkError: false
      checkError: true

      This is caused by following spec violations:

      1. PrintStream.close method set's protected varaible out to null which is not permitted by spec
          public void close() {
              synchronized (this) {
                  if (! closing) {
                      closing = true;
                      try {
                          textOut.close();
                          out.close();
                      }
                      catch (IOException x) {
                          trouble = true;
                      }
                      textOut = null;
                      charOut = null;
                      out = null;
                  }
              }
          }

      2. Accordingly with spec PrintStream instance track just IOExceptions thrown by underlying stream not by PrintStream itself. However it throws IOE by private ensureOpen() method invoked as part of IO-related method implementaion:

          private void ensureOpen() throws IOException {
              if (out == null)
                  throw new IOException("Stream closed");
          }

          public void write(int b) {
              try {
                  synchronized (this) {
                      ensureOpen();
                      out.write(b);
                      if ((b == '\n') && autoFlush)
                          out.flush();
                  }
              }
              catch (InterruptedIOException x) {
                  Thread.currentThread().interrupt();
              }
              catch (IOException x) {
                  trouble = true;
              }
          }

      3. Accordingly with spec:

      ----------------------------
      public boolean checkError()

          Flushes the stream and checks its error state.
      ----------------------------

      Instead it is implemented as follows:

          public boolean checkError() {
              if (out != null)
                  flush();
              if (out instanceof java.io.PrintStream) {
                  PrintStream ps = (PrintStream) out;
                  return ps.checkError();
              }
              return trouble;
          }

      So 'flush' is not invoked by 'checkError' if 'close' swas invoked earlier which violates specification and causes stgrange result when result of 'checkError' before 'flush' invocation differs from one after that while accordingly with spec 'checkError' invocation should mean 'flush' invocation.

      (Without third issue output would be
      out: Hello
      out: null
      checkError: true
      checkError: true)


      So something should be fixed - either specification or implementation.

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            sreznick Sergey Reznick (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Dates

              Created:
              Updated:
              Imported:
              Indexed: