I am trying to access the i18n properties file I’m using in my JSF application in code. (The idea is to have a page that displays its keys and values as a table actually.)
The project is a maven project, and in the src/resources/localization folder, and deployed in the war file in WEB-INF\classes\localization\
java.util.Properties prop = new java.util.Properties();
String path = "localization/stat_codes.properties";
InputStream foo = prop.getClass().getResourceAsStream(path);
But the variable foo turns out to be null whatever I set the path variable to, /WEB-INF/classes/localization/stat_codes.properties, “localization.stat_codes.properties” etc. A similar question is here, but there is no helpful answer there as well.
The
Class#getResourceAsStream()can take a path which is relative to the location of theClasswhich you’re using there as starting point. So, for example, if the class is located in thecom.examplepackage and you request the pathfoo/filename.properties, then it will actually load thecom/example/foo/filename.propertiesfile. But if you use/foo/filename.properties, then it will actually loadfoo/filename.propertiesfrom the classpath root.So, your code
will actually look for
java/util/localization/stat_codes.propertiesfile.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 returnnull.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 useBut 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.
(note that this one cannot take a path starting with
/, it’s always relative to the common root)See also: