JDK-6723276 : Confusion between bootstrap and system class loaders in FactoryFinder.findJarServiceProvider
  • Type: Bug
  • Component: xml
  • Sub-Component: org.xml.sax
  • Affected Version: 5.0
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2008-07-07
  • Updated: 2012-04-25
  • Resolved: 2011-07-18
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 JDK 7
1.4.0Fixed 6-poolResolved 7 b142Fixed
Related Reports
Relates :  
Description
For background, see:

http://www.netbeans.org/nonav/issues/show_bug.cgi?id=139048

To reproduce, run the following with Apache Xerces in the classpath:

---%<---
import java.net.URL;
import java.net.URLClassLoader;
import javax.xml.parsers.SAXParserFactory;
public class TestJAXP {
    public static void main(String[] args) throws Exception {
        Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[0], ClassLoader.getSystemClassLoader().getParent()));
        SAXParserFactory.newInstance();
    }
}
---%<---

Under JDK 6 I get:

Exception in thread "main" javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.SAXParserFactoryImpl not found
        at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:134)
        at TestJAXP.main(TestJAXP.java:7)

The reason is that FactoryFinder.findJarServiceProvider behaves as follows:

1. Check the thread's CCL for META-INF/services/javax.xml.parsers.SAXParserFactory. This is not found, since the CCL does not see the app class loader.

2. is == null, so set cl = null (bootstrap loader), and call SecuritySupport.getResourceAsStream.

3. ClassLoader.getSystemResourceAsStream(...) returns the resources from xerces.jar, because it checks the app class loader, not the bootstrap loader (which has no such resource).

4. newInstance(null, "org.apache.xerces....", false) is called, which asks for org.apache.xerces... from the CCL, which cannot see it.

Comments
EVALUATION jaxp 1.4.5 will be integrated in b142 (7040147).
30-04-2011

EVALUATION Fixed in jaxp 1.4. Will integrate into jdk7 in next integration.
16-03-2011

EVALUATION Will fix in jaxp 1.4 first.
08-03-2011

EVALUATION will be considered along with 6941169.
19-11-2010

SUGGESTED FIX Seems that other packages in JAXP suffer from the same problem, e.g. javax.xml.datatype. (Why the duplication of FactoryFinder etc.? Surely you can just put such classes in sun.** packages to hide them.)
08-07-2008

WORK AROUND Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[0], ClassLoader.getSystemClassLoader().getParent()) { protected @Override Class<?> findClass(String name) throws ClassNotFoundException { if (name.equals("org.apache.xerces.jaxp.SAXParserFactoryImpl")) { return ClassLoader.getSystemClassLoader().loadClass(name); } else { return super.findClass(name); } } }); works on JDK 6 (which falls back to CCL for cl == null in newInstance), but not JDK 5 (which uses Class.forName and thus the bootstrap loader). What does seem to work in both: Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[0], ClassLoader.getSystemClassLoader().getParent()) { @Override public InputStream getResourceAsStream(String name) { if (name.startsWith("META-INF/services/javax.xml.parsers.")) { return new ByteArrayInputStream(new byte[0]); } else { return super.getResourceAsStream(name); } } });
07-07-2008

SUGGESTED FIX Fix the inconsistency with the use of null as a ClassLoader, which should always mean the bootstrap class loader. SecuritySupport.getResourceAsStream should only check the bootstrap loader for resources. There is no apparent call in ClassLoader to check for resources in the bootstrap loader (i.e. equivalent to Class.forName(..., null) for resources), or to verify whether a returned resource in fact came from the "system" (app) class loader; so it may be best to just return null in case cl == null. (Since the JAXP-containing JRE in fact has no META-INF/services entries for JAXP factories anyway, this should not harm anything.) Failing that, make SS.gRAS treat null as the CCL just like FactoryFinder.getProviderClass does. Ideally, a new variant of ClassLoader.getResource would return a pair of URL and defining ClassLoader, so that you would only attempt to load the provider class from the actual class loader which claimed it existed. (I have mentioned this deficit in the API in an unrelated bug involving class loading.)
07-07-2008