JDK-6419145 : JAXP fails to fall back properly when a non-standard class loader delegation is used
  • Type: Bug
  • Component: xml
  • Sub-Component: javax.xml.transform
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2006-04-27
  • Updated: 2012-04-25
  • Resolved: 2006-07-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
5.0u10Fixed 6 b92Fixed
Related Reports
Relates :  
javax/xml/transform/FactoryFinder.java has the following part in the newInstance method:

  // Fall back to current classloader
  cl = FactoryFinder.class.getClassLoader();
  providerClass = cl.loadClass(className);

This code is used when the context class loader of the current thread fails to load the class.
When a class loader like the one in Tomcat is used (where they don't always delegate to the parent class loader),
this code is executed in the hope that this classloader can find the class.

Alas, when JAXP is in Tiger's rt.jar, FactoryFinder.class.getClassLoaer() returns null, so we'll get NPE.
The above code needs to be changed to:

  // Fall back to current classloader
  cl = FactoryFinder.class.getClassLoader();
  providerClass = Class.forName(className,true,cl);

See Class.forName javadoc for why this works correctly when cl==null and cl!=null.

I haven't checked, but I suspect this problem to apply to other FactoryFinders.

EVALUATION I applied the patch suggested by Kohsuke to avoid a NPE. However, I believe there are a number of issues unresolved in relation to the FactoryFinder class. We may need to open another CR to address some inconsistencies in how the different factories in JAXP load classes and resources.

EVALUATION A related issue is the confusion around where the delegation is supposed to happen. For example, code like this (taken from FactoryFinder line 81: if (cl == null) { // If classloader is null Use the bootstrap ClassLoader. // Thus Class.forName(String) will use the current // ClassLoader which will be the bootstrap ClassLoader. providerClass = Class.forName(className); } else { try { providerClass = cl.loadClass(className); } catch (ClassNotFoundException x) { if (doFallback) { // Fall back to current classloader cl = FactoryFinder.class.getClassLoader(); providerClass = cl.loadClass(className); } else { throw x; } } } assumes that when the context classloader is null, it uses the bootstrap classloader (since Class.forName uses the caller's classloader, which is bootstrap loader.) Yet in some other places, such as in SecuritySupport (line 77) InputStream getResourceAsStream(final ClassLoader cl, final String name) { return (InputStream) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { InputStream ris; if (cl == null) { ris = ClassLoader.getSystemResourceAsStream(name); } else { ris = cl.getResourceAsStream(name); } return ris; } }); } ... it delegates to the system classloader. So if my context classloader is null, JAXP may find META-INF/services from my system classloader, yet it will fail to load it from the bootstrap classloader (as the implementation was in my system classpath, not bootstrap.)

EVALUATION this CR is similar to 6350682: REGRESSION: SAXParserFactory throws FactoryConfigurationError if contextClassLoader=null note that classloading has security implicatitons. the JAXP team has a query in with the security team as to a proper fix.