Accessing Files in a JAR

May 28, 2015

I wanted to have text files in my Java project that I could read and process in a Java program. I wanted this to work both when running code in Eclipse, and when running an executable JAR file of the same code.

I added a marker.txt file at top of my project inside the src directory. This will be included at the top level of the JAR file.

We can get hold of the location of this file using the following code:

URI lUri = DatabaseConfigurator.class.getResource("/marker.txt").toURI();

Once we have this, we can work out whether we're in a JAR file or not, and populate the lPath variable appropriately.

Path lPath;

if ("jar".equals(lUri.getScheme()))
{
  // We're running within a jar file
  if (this.fileSystem == null)
  {
    try
    {
      // Create a reference to the filesystem
      this.fileSystem = FileSystems.newFileSystem(lUri, Collections.<String, Object> emptyMap());
    }
    catch (FileSystemAlreadyExistsException lEx)
    {
      // Sometimes newFileSystem seems to raise FileSystemAlreadyExistsException - this code gets around this problem.
      this.fileSystem = FileSystems.getFileSystem(lUri);
    }
  }

  // Get hold of the path to the top level directory of the JAR file
  lPath = this.fileSystem.getPath("/");
}
else
{
  // We're running within eclipse, we'll be in the bin directory.
  lUri = ThisClass.class.getResource("/").toURI();
  // Move to ../src to move out of the bin directory
  lPath = Paths.get(lUri).getParent().resolve("src");
}

At this point we have lPath pointing at the top level src directory, or the top level directory in the JAR file. We can now move to the directory we want to look at.

In this instance pLocation is a string containing something like core/plsql

 lPath = lPath.resolve(pLocation);

We can now use FileFinder (a subclass of SimpleFileVisitor) to process all the files found within the location specified. This will work both inside Eclipse and inside the JAR file.

FileFinder lFileFinder = new FileFinder(pPassThingsHereToUseWithinTheFileFinder);
Files.walkFileTree(lPath, lFileFinder);

The subclass of SimpleFileVisitor contains an overload of visitFile which is called for each file found and should implement the required processing:

public FileVisitResult visitFile(Path pPath, BasicFileAttributes pAttributes)
{
  ...
}