In my application, several libraries are assembled.
Whenever those libraries comes with resources having a same name, getResourceAsStream("myfile")
seems to return a stream to whatever file come first.
This is true for any file, including Resource bundles.
In the following example, a library with a MyMibrary class has a file called "config.properties". And does
InputStream is = MyLibrary.class.getResourceAsStream("/config.properties")
When called in a standalone context, the right config file is used.
But when loaded as a dependency from an application having its own "config.properties", the same statement returns the config file of the application instead of the one of the library.
The same applies also for ResourceBundles (which is using getResourceAsStream
too) :
If both the library and the application are using Resource bundles named the same way, when called from the application, the library will be using the application resource bundle instead of its own.
My question is how to instruct getResourceAsStream
(especially in the context of Resource bundle loading) to use the right resource ?
EDIT: this question differs partially from How to read several resource files with the same name from different in the way the latter question deals only with resources while my question deals also with ResourceBundles.
you need getResources
. Note the plural. Unfortunately, whilst MyLibrary.class.getResource
is right, class does not have the getResources
method copied over from ClassLoader
. So, you do have to go via classloader. This is annoying, because in admittedly exotic (Agents, bootstrap classes) situations, MyClass.class.getClassLoader()
will be null
, so then you need to write code to defer to the system loader instead. For bonus annoyance, the method predates iterators, so it returns an Enumeration
instead.
Thus, the full treatment:
ClassLoader cl = MyClass.class.getClassLoader();
if (cl == null) cl = ClassLoader.getSystemClassLoader();
var resources = cl.getResources("config.properties"); // No slash! See footnote 1
while (resources.hasNextElement()) {
URL resource = resources.nextElement();
try (var in = resource.openStream()) {
// yay we can read it now!
}
}
In admittedly really exotic context
[1] Loading via the classloader already starts at the classloader's root, so the leading slash is not required. In fact, if you use it, it'll fail. So, MyClass.class.getLoader().getResource("foo")
is the same as MyClass.class.getResource("/foo")
, except that the first snippet fails in agent/bootstrap situations.