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

Two calls of close method on an output stream cause errors with some VFS (JimFS)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Duplicate
    • P4
    • tbd
    • 8u20
    • core-libs
    • x86
    • windows_7

    Description

      FULL PRODUCT VERSION :
      java version "1.8.0_20-ea"
      Java(TM) SE Runtime Environment (build 1.8.0_20-ea-b17)
      Java HotSpot(TM) Client VM (build 25.20-b17, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      When you call javadoc with a com.sun.tools.javac.nio.JavacPathFileManager
      for sending generating documentation to a Path and not a File (JimFS is a virtual filesystem in memory implementing NIO.2 provider API), you have a bug because JimFS is stricter than java.io and some copied files (text files) can be closed twice.

      After the first close, the second close begin by calling flush on the OutputStream, but the OutputStream is already closed by first close then you have an exception on the flush call with JimFS.

      Javadoc of java.io.OutputStream is not really clear on throwing or not an IOException when calling flush on an already closed stream, but it is not even clear for write: javadoc of some write methods throw clearly in this case, but not all e.g. void write(byte[] b).
       
      In this case, source of bug is then not really clear (JimFS excessively strict or javadoc calling close twice on same OutputStream).

      But only one of the two methods (binary or text) for copying is really used, then only one close would be logic (in my opinion), then I send a bug report.


      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      at com.google.common.jimfs.JimfsOutputStream.checkNotClosed(JimfsOutputStream.java:100)
      at com.google.common.jimfs.JimfsOutputStream.flush(JimfsOutputStream.java:94)
      at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:141)
      at java.io.FilterOutputStream.close(FilterOutputStream.java:158)
      at com.sun.tools.doclets.internal.toolkit.util.DocFile.copyResource(DocFile.java:196)
      at com.sun.tools.doclets.formats.html.HtmlDoclet.generateOtherFiles(HtmlDoclet.java:156)
      at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.startGeneration(AbstractDoclet.java:144)
      at com.sun.tools.doclets.internal.toolkit.AbstractDoclet.start(AbstractDoclet.java:82)
      at com.sun.tools.doclets.formats.html.HtmlDoclet.start(HtmlDoclet.java:80)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:483)
      at com.sun.tools.javadoc.DocletInvoker.invoke(DocletInvoker.java:310)
      at com.sun.tools.javadoc.DocletInvoker.start(DocletInvoker.java:189)
      at com.sun.tools.javadoc.Start.parseAndExecute(Start.java:366)
      at com.sun.tools.javadoc.Start.begin(Start.java:219)
      at com.sun.tools.javadoc.Start.begin(Start.java:212)
      at com.sun.tools.javadoc.api.JavadocTaskImpl.call(JavadocTaskImpl.java:79)
      [...]

      REPRODUCIBILITY :
      This bug can be reproduced always.

      CUSTOMER SUBMITTED WORKAROUND :
      There is two workarounds possible for solving the problem: remove the throwing of IOException in JimFS from flush method when the stream is already closed or invoke close method only one time in javadoc (detailed under):

      Change in code of com.sun.tools.doclets.internal.toolkit.util.DocFile.copyResource method to have try {} finally{} around each copy method (bytes or String) and not nested try{} finally{}.

      // From http://hg.openjdk.java.net/jdk8u/jdk8u/langtools/file/20bab46f4db6/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/DocFile.java

          public void copyResource(DocPath resource, boolean overwrite, boolean replaceNewLine) {
              if (exists() && !overwrite)
                  return;

              try {
                  InputStream in = Configuration.class.getResourceAsStream(resource.getPath());
                  if (in == null)
                      return;

                  OutputStream out = openOutputStream();
                  try {
                      if (!replaceNewLine) {
                          byte[] buf = new byte[2048];
                          int n;
                          while((n = in.read(buf))>0) out.write(buf,0,n);
                      } else {
                          BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                          BufferedWriter writer;
                          if (configuration.docencoding == null) {
                              writer = new BufferedWriter(new OutputStreamWriter(out));
                          } else {
                              writer = new BufferedWriter(new OutputStreamWriter(out,
                                      configuration.docencoding));
                          }
                          try {
                              String line;
                              while ((line = reader.readLine()) != null) {
                                  writer.write(line);
                                  writer.write(DocletConstants.NL);
                              }
                          } finally {
                              reader.close();
                              writer.close();
                          }
                      }
                  } finally {
                      in.close();
                      out.close();
                  }
              } catch (IOException e) {
                  e.printStackTrace(System.err);
                  throw new DocletAbortException(e);
              }
          }

      // This method need to be replaced by following code
      public void copyResource(DocPath resource, boolean overwrite,
      boolean replaceNewLine) {
      if (exists() && !overwrite) {
      return;
      }

      try {
      InputStream in = Configuration.class.getResourceAsStream(resource
      .getPath());
      if (in == null) {
      return;
      }

      OutputStream out = openOutputStream();
      if (!replaceNewLine) {
      try {
      byte[] buf = new byte[2048];
      int n;
      while ((n = in.read(buf)) > 0) {
      out.write(buf, 0, n);
      }
      } finally {
      in.close();
      out.close();
      }
      } else {
      BufferedReader reader = new BufferedReader(new InputStreamReader(in));
      BufferedWriter writer;
      if (this.configuration.docencoding == null) {
      writer = new BufferedWriter(new OutputStreamWriter(out));
      } else {
      writer = new BufferedWriter(new OutputStreamWriter(out,
      this.configuration.docencoding));
      }
      try {
      String line;
      while ((line = reader.readLine()) != null) {
      writer.write(line);
      writer.write(DocletConstants.NL);
      }
      } finally {
      reader.close();
      writer.close();
      }
      }
      } catch (IOException e) {
      e.printStackTrace(System.err);
      throw new DocletAbortException(e);
      }
      }

      // And it works.

      Attachments

        Issue Links

          Activity

            People

              bpb Brian Burkhalter
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: