ClassLoader.getResources(String)

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

ClassLoader.getResources(String)

Stephen Colebourne
I've been trying to use ClassLoader.getResources(String). The entire
application is in one named module, this includes the code that
invokes the ClassLoader method and the resource that it is trying to
find. The Javadoc says:

"Resources in named modules are subject to the encapsulation rules
specified by Module.getResourceAsStream. Additionally, and except for
the special case where the resource has a name ending with ".class",
this method will only find resources in packages of named modules when
the package is opened unconditionally (even if the caller of this
method is in the same module as the resource)."
https://docs.oracle.com/javase/9/docs/api/java/lang/ClassLoader.html#getResources-java.lang.String-

The call to ClassLoader.getResources(String) does not find the
resource. I assume that this is because of the last clause in the spec
"even if the caller of this method is in the same module as the
resource".

But I can't for the life of me think why such a difficult to meet
restriction has been added. The only way around it is to make the
package open, which is far from ideal. If its all within one module,
applying an access restriction like this is just unhelpful.

Stephen
Reply | Threaded
Open this post in threaded view
|

Re: ClassLoader.getResources(String)

Alan Bateman
On 07/02/2018 14:23, Stephen Colebourne wrote:
> I've been trying to use ClassLoader.getResources(String). The entire
> application is in one named module, this includes the code that
> invokes the ClassLoader method and the resource that it is trying to
> find.
Can you summarize what you are trying to do? If this is code in a module
trying to locate one of its own resources then Class.getResourceXXX or
Module.getResourcAsStream are the candidate APIs to use (not
ClassLoader.getResourceXXX as that can never locate resources that are
encapsulated).

-Alan
Reply | Threaded
Open this post in threaded view
|

Re: ClassLoader.getResources(String)

Stephen Colebourne
On 7 February 2018 at 16:35, Alan Bateman <[hidden email]> wrote:

> On 07/02/2018 14:23, Stephen Colebourne wrote:
>>
>> I've been trying to use ClassLoader.getResources(String). The entire
>> application is in one named module, this includes the code that
>> invokes the ClassLoader method and the resource that it is trying to
>> find.
>
> Can you summarize what you are trying to do? If this is code in a module
> trying to locate one of its own resources then Class.getResourceXXX or
> Module.getResourcAsStream are the candidate APIs to use (not
> ClassLoader.getResourceXXX as that can never locate resources that are
> encapsulated).

I was using maven to create a jar-with-dependencies file, so I could
use jlink. With all the code in one jar file, there shouldn't be any
access barriers to worry about.

ClassLoader.getResources(String) worked just fine until Java 9. The
two APIs are not comparable - the ClassLoader one returns all URLs
found, whereas the Class one returns just one URL. Switching API would
change behaviour.

The code can be seen here:
https://github.com/OpenGamma/Strata/blob/master/modules/collect/src/main/java/com/opengamma/strata/collect/io/ResourceConfig.java#L242
It is a core part of the system that loads configuration at startup.

thanks
Stephen
Reply | Threaded
Open this post in threaded view
|

Re: ClassLoader.getResources(String)

Alan Bateman
On 07/02/2018 16:56, Stephen Colebourne wrote:
> :
> I was using maven to create a jar-with-dependencies file, so I could
> use jlink. With all the code in one jar file, there shouldn't be any
> access barriers to worry about.
>
> ClassLoader.getResources(String) worked just fine until Java 9. The
> two APIs are not comparable - the ClassLoader one returns all URLs
> found, whereas the Class one returns just one URL. Switching API would
> change behaviour.
ClassLoader.getResources searches the class path as it did in JDK 9 and
older, it it just can't locate non-".class" resources in modules when
they are encapsulated. Class loaders are oblivious as to who is
ultimately attempting to load a class or locate a resource (the
initiating and defining loader can be different, they can many class
loaders in the delegation chain).

With the uber modular JAR scenario then all classes for several
libraries are in the same module. This means that the names of resources
in that module are unique. If several libraries have the same resource
then I assume you drop all but one when you create this uber JAR (or
maybe you are merging some of the configuration files, I can't tell). So
I assume you could change this code to use Class.getResource and it will
locate at-most-one resource with a specific name.

To do a proper migration means re-examining ResourceConfig of course.
Using services is likely to be a lot cleaner and more robust than
scanning for configuration files.

-Alan