Accessing properties file in a JSF application programmatically

The Class#getResourceAsStream() can take a path which is relative to the location of the Class which you’re using there as starting point. So, for example, if the class is located in the com.example package and you request the path foo/filename.properties, then it will actually load the com/example/foo/filename.properties file. But if you use /foo/filename.properties, then it will actually load foo/filename.properties from the classpath root.

So, your code

java.util.Properties prop = new java.util.Properties();
String path = "localization/stat_codes.properties";
InputStream foo = prop.getClass().getResourceAsStream(path);

will actually look for java/util/localization/stat_codes.properties file.

But in applications with a complex multiple classloader hierarchy, the one classloader isn’t the other. The classloader which loaded the core Java classes does not necessarily have knowledge about files which are in the webapp’s /WEB-INF/classes. So prefixing the path with / will not necessarily be the solution, it would still return null.

If you can guarantee that the current class is visible by the same classloader as the properties files (because they’re in the same sub-root of the classpath, e.g. /WEB-INF/classes, then you should indeed use

String path = "/localization/stat_codes.properties";
InputStream foo = this.getClass().getResourceAsStream(path);

But if at some point, the properties files will be externalized because of more easy maintenance/editing during runtime so that you don’t need to rebuild/redeploy/restart the webapp whenever you want to edit the files, then the above line of code will likely fail as well. The externalized location would be only accessible by a different classloader. The canonical solution is to use the thread’s context classloader as starting point instead, it has access to all resources in the classpath.

String path = "localization/stat_codes.properties";
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream foo = loader.getResourceAsStream(path);

(note that this one cannot take a path starting with /, it’s always relative to the common root)

See also:

Leave a Comment