The Cave

09 Feb

Bug ID 6245257: XPathFactoryFinder

Java 1.5 is broke.

I found this one the hard way. The bug report says it occurs on W2K when called from an ActiveX bean; I saw the same problem running on W2K3 when the code is called under IIS to the JVM thru JNI, more specifically

IIS 6.0 -> foo.ASP -> C++/ATL COM object -> JNI -> pEnv->Call…Method()

To recap:

  1. Compile XPathFactory.newInstance() in Java
  2. Create the JVM via JNI and call the Java code
  3. Wrap the JNI calling code in a COM object via ATL
  4. Call the COM object from a .ASP page

As per the Javadoc “… this method will never fail.”
Wrong.

java.lang.NullPointerException
at javax.xml.xpath.XPathFactoryFinder._newFactory
at javax.xml.xpath.XPathFactoryFinder.newFactory
at javax.xml.xpath.XPathFactory.newInstance
...

The problem’s simple

_newFactory(uri) does the lookup to find the factory implementation that doles out instances of the XPathFactory object, using the following hunt sequence:

  1. class = System.getProperty(uri)
  2. uri=class in (java.home)/lib/jaxp.properties
  3. iterate over META-INF/services for the uri
  4. com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl (hard-coded built-in default)

where uri=javax.xml.xpath.XPathFactory:http://java.sun.com/jaxp/xpath/dom

It’s #3 that throws a java.lang.NullPointerException. From javax.xml.XPathFactoryFinder.java line 194+

        // try META-INF/services files
        Iterator sitr = createServiceFileIterator();
        while(sitr.hasNext()) {
==>         URL resource = (URL)sitr.next();
            debugPrintln("looking into " + resource);
            try {
                //sf = loadFromProperty(uri,resource.toExternalForm(),resource.openStream());
==>             sf = loadFromProperty(uri,resource.toExternalForm(),ss.getURLInputStream(resource));
                if(sf!=null)    return sf;
            } catch(IOException e) {
                if( debug ) {
                    debugPrintln("failed to read "+resource);
                    e.printStackTrace();
                }
            }
        }

createServiceFileIterator() is, in my case, quite simple:

        if (classLoader == null) {
            return new SingleIterator() {
                protected Object value() {
                    ClassLoader classLoader = XPathFactoryFinder.class.getClassLoader();
                    return ss.getResourceAsURL(classLoader, SERVICE_ID);
                    //return (ClassLoader.getSystemResource( SERVICE_ID ));
                }
            };
        } else {
        ...

I have no ClassLoader defined, so createServiceFileIterator() returns an iterator to one thing. However, for some reason ss.getResourceAsURL() is returning null, so in _newInstance() the META-INF/services logic enters the while loop, but the resource variable is null – which is then dereferenced.

You can confirm this by adding the System property jaxp.debug=anything (check out the top of the source file, line 39 to be exact); if jaxp.debug exists, debugPrintln() writes to stdout. I always see

looking into null

from the debugPrintln() statement right before resource is dereferenced (line 198).

This is trivial to fix in the source code (if you can modify the source):

        // try META-INF/services files
        Iterator sitr = createServiceFileIterator();
        while(sitr.hasNext()) {
            URL resource = (URL)sitr.next();
+++         if (resource == null)
+++         {
+++             debugPrintln("skipping resource " + resource);
+++         }
+++         else
+++         {
                debugPrintln("looking into " + resource);
                try {
                    //sf = loadFromProperty(uri,resource.toExternalForm(),resource.openStream());
                    sf = loadFromProperty(uri,resource.toExternalForm(),ss.getURLInputStream(resource));
                    if(sf!=null)    return sf;
                } catch(IOException e) {
                    if( debug ) {
                        debugPrintln("failed to read "+resource);
                        e.printStackTrace();
                    }
                }
+++         }
        }

But seeing as how I’m not Sun, there’s a simple workaround:
Add -Djavax.xml.xpath.XPathFactory:http://java.sun.com/jaxp/xpath/dom=com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl to the command line (or otherwise define that property).

In short, feed the built-in default class name to the system as a property to force the 1st check in the hunt sequence to succeed. This way it never needs to hit option #3 in the search and thus no NullPointerException.

Why the META-INF/services thinks he has a whole-lotta-nuthin’ (a list of 1 item being null) is beyond me. I spent 2 days of my life reverse engineering and rewriting the operating system; I leave answering that question to Sun.

P.S. This was first reported against Java 1.5.0_02, but still exists in 1.5.0_11 and 1.6.0.

FacebookGoogle GmailGoogle BookmarksTechnorati FavoritesTwitterHotmailYahoo MailYahoo MessengerWordPressStumbleUponRedditEmailInstapaperGoogle+Share

Comments are closed.

© 2014 The Cave | Entries (RSS) and Comments (RSS)

GPS Reviews and news from GPS Gazettewordpress logo