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

(zipfs) Files.newOutputStream on a ZipFileSystem path leads to java.nio.file.FileAlreadyExistsException

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P4
    • Resolution: Duplicate
    • Affects Version/s: 8
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:
      None

      Description

      (Reported as a issue in Quarkus project https://github.com/quarkusio/quarkus/issues/4858 )

      The Files.newOutputStream javadoc states:

           * <p> This method opens or creates a file in exactly the manner specified
           * by the {@link #newByteChannel(Path,Set,FileAttribute[]) newByteChannel}
           * method with the exception that the {@link StandardOpenOption#READ READ}
           * option may not be present in the array of options. If no options are
           * present then this method works as if the {@link StandardOpenOption#CREATE
           * CREATE}, {@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING},
           * and {@link StandardOpenOption#WRITE WRITE} options are present. In other
           * words, it opens the file for writing, creating the file if it doesn't
           * exist, or initially truncating an existing {@link #isRegularFile
           * regular-file} to a size of {@code 0} if it exists.

      Now consider this code (which has also been attached as a file to this issue):


      import java.io.OutputStream;
      import java.nio.file.FileSystem;
      import java.nio.file.FileSystems;
      import java.nio.file.Files;
      import java.nio.file.Path;
      import java.nio.file.Paths;
      import java.util.Collections;
      import java.util.HashSet;
      import java.util.Set;
      import java.util.zip.ZipEntry;
      import java.util.zip.ZipInputStream;
      import java.net.*;

      public class ZipFSNewOutputStream {

      public static void main(final String[] args) throws Exception {
      System.out.println("Running tests using " + System.getProperty("java.version"));
      final Path jarPath = Paths.get("zipfs-outstream-test.jar");
              // cleanup existing jar
              if (Files.exists(jarPath)) {
                  Files.delete(jarPath);
                  System.out.println("Deleted " + jarPath + " in preparation for tests");
              }
              final String pathWithinJar = "foo.txt";
              final URI zipFsUri = new URI("jar:" + jarPath.toUri().getScheme(), jarPath.toUri().getPath(), null);
              try (final FileSystem zipFs = FileSystems.newFileSystem(zipFsUri, Collections.singletonMap("create", "true"))) {
                  // create some file within the jar
                  addContent(zipFs, pathWithinJar);
                  System.out.println("Added " + pathWithinJar + " into " + jarPath);
              }
              System.out.println("Created file using zipfs at " + jarPath);
              // now open it again and then try to write content within it, at the same path again
              try (final FileSystem zipFs = FileSystems.newFileSystem(zipFsUri, Collections.singletonMap("create", "true"))) {
                  System.out.println("Re-opening and attempting to add " + pathWithinJar + " into " + jarPath);
                  // create some file within the jar
                  addContent(zipFs, pathWithinJar);
              }
      }

      private static void addContent(final FileSystem zipFs, final String path) throws Exception {
      final Path filePath = zipFs.getPath(path);
              try (final OutputStream os = Files.newOutputStream(filePath)) {
                  final byte[] someData = new byte[]{'b', 'c', 'd'};
                  os.write(someData);
              }
      }
      }

      What this does is create a jar file using ZipFS, adds a file to it at a specific path in the jar and closes it. This goes fine. It then opens the same jar file using ZipFS (this again goes fine) and then tries to add content to the same path, as previous, within the jar. To do that it uses:

      final Path filePath = zipFs.getPath(path);
              try (final OutputStream os = Files.newOutputStream(filePath)) {

      The call to Files.newOutStream with a path pointing to the existing path within the jar, however, leads to this exception:

      Exception in thread "main" java.nio.file.FileAlreadyExistsException: foo.txt
      at com.sun.nio.zipfs.ZipFileSystem.newOutputStream(ZipFileSystem.java:516)
      at com.sun.nio.zipfs.ZipPath.newOutputStream(ZipPath.java:790)
      at com.sun.nio.zipfs.ZipFileSystemProvider.newOutputStream(ZipFileSystemProvider.java:285)
      at java.nio.file.Files.newOutputStream(Files.java:216)
      at ZipFSNewOutputStream.addContent(ZipFSNewOutputStream.java:43)
      at ZipFSNewOutputStream.main(ZipFSNewOutputStream.java:37)

      This is contrary to what the javadoc of Files.newOutputStream states.

      This issue is reproducible only with Java 8. I've used the latest Java 8 from adoptopenjdk:

      java -version
      openjdk version "1.8.0_222"
      OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_222-b10)
      OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.222-b10, mixed mode)

      This works fine with Java 9 and above and it looks like this commit (related to a different issue) http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/6f00367ab8e1#l2.7 seems to have fixed it for those versions.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                Unassigned
                Reporter:
                jpai Jaikiran Pai
              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: