Sunday, 8 May 2011

Reading Files inside a jar file correctly

Recently I've ran into some problems when trying to create a simple jar package containing multiply SQL files which in-turn would be ran against several databases. The application isn't complex, but simply a utility application which would be ran once a week/month against very large data sets in order to clean up data and fix known issues. The jar file would be invoked via a cron script and left to run over several hours on a weekend. I used the maven-assembly-plugin to build the jar file, simply specifying the main method to invoke.

First time I tried to deploy the program when it ran, all the files where trying to be loaded via a none relative path, i.e. trying to access files on the files system not inside the jar itself. After several different attempts at trying to read in files I found a simple solution using Google Guava.

Google guava has many helpful and functional utility libraries, commonly I use classes in:
  • com.google.common.io  - Utility methods and classes for working with Java I/O, for example input streams, output streams, readers, writers, and files.
  • com.google.common.collect - Generic collection interfaces and implementations, and other utilities for working with collection.
  • com.google.common.base - Basic utility libraries and interfaces. 
The solution I found simple used a new class to me found inside com.google.common.io  called Resources

/**
 * @author James Edward Morgan
 */
public class ResourceFileLoader {

 /**
  * @param location
  * @return a {@link String} {@link List} of all lines in the file
  * @throws IOException
  * @throws URISyntaxException
  */
 public static List readLines(final String location) throws IOException, URISyntaxException {
  return Files.readLines(new File(Resources.getResource(location)
   .toURI()), Charsets.UTF_8);
 }

/**
  * @param location
  * @return The {@link File} Resource
  * @throws URISyntaxException
  */
 public static File readFile(final String location) throws URISyntaxException {
  return new File(Resources.getResource(location)
   .toURI());
 }

 /**
  * @param location
  * @return String the complete loaded file
  * @throws IOException
  * @throws URISyntaxException
  */
 public static String readAll(final String location) throws IOException, URISyntaxException {
  final StringBuilder builder = new StringBuilder();
  for (final String line : Files.readLines(new File(Resources.getResource(location)
   .toURI()), Charsets.UTF_8)) {
   builder.append(line);
  }
  return builder.toString();
 }
}


This utility class simply wraps the use of the Files and Resources class in side google.io to expose a few cleaner utility methods which will in turn be used to load any text files needed. The solution is neat and simple, and since the project already has a dependency on the Google guava the over head is minimal.

All in all, Google Guava contains a very useful set of utility classes which in my opinion should almost be as standard as using the Apache Commons libraries.

All source code, tests and the original class I created with many good/bad and ugly attempts as over coming this problem can be found on my Git Hub account @ https://github.com/jamesemorgan/SampleJarReading