JDK-6865375 : (cl) Request ClassLoader.getResourcePairs(String) => Enumeration<(ClassLoader,URL)>
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang:class_loading
  • Affected Version: 5.0
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: linux
  • CPU: x86
  • Submitted: 2009-07-27
  • Updated: 2012-10-29
Related Reports
Relates :  
Description
There are various situations where calling ClassLoader.getResources is unsatisfactory. The problem is that the returned "flattened" enumeration gives no hint which ancestor loader was the defining loader for a given element: unlike a Class which has a getClassLoader() method, a URL tells you nothing more than how to open it.

A particularly nasty consequence is when you try to load a class given by name in a resource. Several difficult problems can arise:

1. The initial loader might not be able to load this class even though an ancestor can, for whatever reason (e.g. cross-module API visibility rules).

2. In a multiply-parented loader, there might be several ancestors which can all load a class of the same name, and which independently mention that class in a resource. Trying to load the class through the initial loader could produce an error or an arbitrary and incorrect result, such as one ancestor being used twice while another defining ancestor is not used at all.

3. An supertype to which the instantiated class is expected to be assignable might be defined differently in some ancestor. This can usually be worked around by doing an explicit checkcast on the implementation class.


Suggest a method which would return a list of pairs of class loader and resource.

Some examples of where this would be useful:

https://sezpoz.dev.java.net/source/browse/*checkout*/sezpoz/trunk/sezpoz/src/main/java/net/java/sezpoz/Index.java?rev=85

gets a list of URLs from an unknown class loader hierarchy but has no way of knowing what loader produced a given element. Since each such resource is associated with some Java classes that need to be loaded, it is necessary to just guess that the class can be loaded via an initiating class loader.

http://hg.netbeans.org/main-silver/raw-file/f3336fe87290/openide.util/src/org/openide/util/lookup/MetaInfServicesLookup.java

has a similar problem. In fact java.util.ServiceLoader suffers from this problem, as corrected in the supplied patch.

Bug #6723276 gives a different example. See comment #1 under Suggested Fix.


http://mail.openjdk.java.net/pipermail/jigsaw-dev/2009-May/000093.html

also alludes to problems with getResource* returning URL rather than some richer type. It is not clear from that message what the suggested API would be.

Comments
WORK AROUND http://hg.netbeans.org/main-silver/rev/69e552913da8 shows how to use URLConnection.getContent as a "back channel": the defining loader can produce a URL with an explicit handler that supplies that loader when getContent(ClassLoader.class) is requested. The client (here an enhanced version of ServiceLoader) can then use the defining loader rather than the originating loader.
29-08-2011

WORK AROUND No good workaround exists. You could call getResources and try to guess from the shape of the returned URLs what the defining loader was, but this cannot work with unknown third-party loaders. Without this API, you simply have to hope that similar-looking resources are not to be found among multiple ancestor loaders, and that the initiating loader will delegate to the right ancestor when asked. You could reflectively make findResources accessible and call it as public, collecting your own list of (ClassLoader,URL) pairs, but this would only work for loaders which do not override getResources.
27-07-2009

SUGGESTED FIX Attaching sketch of a patch. A major issue is compatibility for ClassLoader subclasses existing before the new API was introduced which nontrivially override getResources, e.g. to implement multiple parents. These ought to be retrofitted to also override getResourcePairs, since the default implementation is unlikely to be correct. It is not clear if there is a better fallback implementation. Checking whether getClass() overrides getResources() could at least indicate whether the default implementation of getResourcePairs() is reliable, but there might be subclasses which override getResources() merely to add logging (say), without actually changing the super result. http://hg.netbeans.org/main-silver/raw-file/f3336fe87290/o.n.bootstrap/src/org/netbeans/ProxyClassLoader.java is an example of such a loader.
27-07-2009