Friday, 27 July 2012

How to Access Files/Data in the Resource Directory at Runtime?

A common concern of developers using Maven for the first time is how to access a file (binary or text) declared as a resource in Maven. Contrary to typical NetBeans projects (for example), maven insists on using a standard directory structure (more on Maven concepts here):

My Project
  |-src
    |-main
      |-java
      | |-MyPackage
      |   |-MyClass.java
      |-resources
        |-MyPackage
          |-MyData.txt

In a standard NetBeans project, since MyClass.java uses MyData.txt, both are (often) located in the same directory corresponding to the MyPackage Java package. However, in a Maven project, code is separated from data in different directories.

Some developers believe that the Maven directory structure will be replicated in the .jar after compilation. Therefore, they are tempted to try to open the data resource with a strategy similar to this one:
String loc = new File(".").getAbsolutePath()
  + "\src\main\resources\MyPackage\MyData.txt";
FileInputStream FIS = new FileInputStream(new File(loc));
...
In other words, they believe they have to handle the Maven directory structure themselves, including the absolute path to the current .jar location. This does not work.

First, one must keep in mind that Maven does not "push" its directory structure in .jars. Both MyClass.java and MyData.txt will be located in the same \MyPackage directory within the .jar. You can check this by opening the .jar with winzip for example.

The solution is the following (resource location is relative to Myclass location or package (*)):
InputStream IS = MyClass.class
    .getResourceAsStream("MyData.txt");

Caveat - Encoding Issue

If your data is not binary, you need to pay attention to the encoding format in the file itself (for example, set it to UTF-8) and make sure it is the same encoding as in your Maven project. You should have the following setting in your pom.xml:
<properties>
  <project.build.sourceEncoding>
    UTF-8
  </project.build.sourceEncoding>
  ...
<properties>
This is an issue that took me hours to solve, because encoding/display errors would only happen at runtime.

(*) More on absolute and relative path here.

REM: This post is a follow-up to an answer I provided on Stackoverflow.com.