2

In order to list file contents of a specific directory on classpath I'm using the new FileSystem and Path features of Java 7. In one deployment the directory is stored on file system, directly. In another deployment it is stored into a JAR file.

My approach works fine with JAR files: I create a FileSystem object which refers to the JAR file and access the content via Path object.

        ...
        URI dir = ...
        String[] array = dir.toString().split("!");

        try (final FileSystem fs = FileSystems.newFileSystem(URI.create(array[0]), new HashMap<String, Object>()))
        {
            final Path directory = fs.getPath(array[1]);
            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory))
            {
        ...

Due to the dir object has following value, it works:

jar:file:/C:/Users/pax/.../Detector-1.0.jar!/org/.../destinationdir

But in the other environment the destination directory is stored on file system, directly. dir object contains the value:

file:/C:/Users/pax/.../destinationdir

FileSystems.newFileSystem(...) always throws following exception for / and file:/C:/Users/pax/.../destinationdir as URI:

java.lang.IllegalArgumentException: Path component should be '/'
at sun.nio.fs.WindowsFileSystemProvider.checkUri(WindowsFileSystemProvider.java:68)

How do you use FileSystem.newFileSystem for destinations on file system?

Is there a better approach in order to list the directories content independently from its specific kind of storage (file system or JAR file)?

PAX
  • 1,056
  • 15
  • 33

1 Answers1

5

Following question's resolution tackles the issue ("destination on file system" versus "destination in JAR file") by try-catch approach: NIO2: how to generically map a URI to a Path?

This utility method tries to obtain a correct Path instance. But there may occur a further problem: If this destination resource is contained by a JAR file (instead of file system) then you can only access the resource via its associated FileSystem instance which must not be closed! So, your helper method needs to return the Path object as well as the FileSystem instance (only required if it's not on file system directly). The invoker has to close the FileSystem object, manually:

public static PathReference getPath(final URI resPath) throws IOException
{
    try
    {
        // first try getting a path via existing file systems
        return new PathReference(Paths.get(resPath), null);
    }
    catch (final FileSystemNotFoundException e)
    {
        /*
         * not directly on file system, so then it's somewhere else (e.g.:
         * JAR)
         */
        final Map<String, ?> env = Collections.emptyMap();
        final FileSystem fs = FileSystems.newFileSystem(resPath, env);
        return new PathReference(fs.provider().getPath(resPath), fs);
    }
}

The wrapper class PathReference should implement AutoClosable so that it can be used in try block:

public class PathReference implements AutoCloseable
{
   ...
    @Override
   public void close() throws Exception
    {
        if (this.fileSystem != null)
            this.fileSystem.close();
    }

    public Path getPath()
    {
        return this.path;
    }

    public FileSystem getFileSystem()
    {
        return this.fileSystem;
    }
}

This makes the release of the FileSystem instance a bit more transparent:

...
try (final PathReference fileObj = SignatureUtils.getPath(file))
{
    ...
    try (InputStream fileStream = Files.newInputStream(fileObj.getPath()))
    {
    ...
Community
  • 1
  • 1
PAX
  • 1,056
  • 15
  • 33