Issue Details (XML | Word | Printable)

Key: JDO-178
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Minor Minor
Assignee: Craig Russell
Reporter: Marcin Owsiany
Votes: 0
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
JDO

javax.jdo.spi.I18NHelper causes NullPointerException

Created: 07/Oct/05 05:06 AM   Updated: 07/Dec/05 09:47 AM
Return to search
Component/s: api11, api2
Affects Version/s: None
Fix Version/s: JDO 2 beta

Time Tracking:
Not Specified

File Attachments:
  Size
Text File Licensed for inclusion in ASF works i18nhelper.patch 2005-10-07 10:57 PM Craig Russell 0.7 kB
Text File Licensed for inclusion in ASF works i18nhelperpatch.txt 2005-10-18 08:59 AM Craig Russell 3 kB
Environment:
Debian GNU/Linux sid
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_02-b09)
Jakarta-Tomcat 5.5.9
KODO JDO 3.3.4

Resolution Date: 19/Oct/05 02:48 AM


 Description  « Hide
In the above environment, I encountered exception being thrown on JDOHelper.getPersistenceManagerFactory(p) invocation. The exception was a tricky one, because .printStackTrace() called on it caused another exception to be thrown, forcing me to manually inspect it by iterating on result of .getStackTrace().

Anyway, here is what I found after a loooong battle:

On JDOHelper class init, an I18NHelper instance is instantiated. On that instantiation, an attempt is made to load a resource bundle, using ResourceBundle.getBundle(s, locale, classloader). The classloader is obtained by calling (javax.jdo.spi.I18NHelper.class).getClassLoader(). However in my case, this getClassLoader() call returned null, which, to my surprise, is fine according to J2SE API.
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getClassLoader() says that:

    Some implementations may use null to represent the bootstrap class loader. This
    method will return null in such implementations if this class was loaded by the
    bootstrap class loader.

Anyway, when that null is passed to ResourceBundle.getBundle, it throws a NullPointerException.

Furthermore, that exception is wrapped in another exception, JDOFatalInternalException if I recall correctly, whose superclass' toString() uses (surprise, surprise) an I18NHelper instance to get the exception description. This makes it impossible to investigate such exception using toString() (for example via log4j).

OK, in truth, the above is a simplified version :-) In fact the exception is saved for later use by I18NHelper, and makes it to the JDO user only when the JDO implementation being used throws another, possibly unrelated, exception during its PersistenceManagerFactory instantiation (in my case it was a trial kodo version throwing a LicenseException). But the exception thrown by the JDO implementation gets lost, and the user only gets a quite unexpected JDO exception, which is hard to debug because of the aforementioned attachment of the exception to I18NHelper.

Fortunately, the fix is quite easy:

--- javax/jdo/spi/I18NHelper.java 2005-10-06 21:26:17.000000000 +0200
+++ javax/jdo/spi/I18NHelper.java 2005-10-06 21:26:11.000000000 +0200
@@ -114,7 +114,10 @@
         ResourceBundle resourcebundle = (ResourceBundle)bundles.get(s);
         if(resourcebundle == null)
         {
+ if(classloader != null)
             resourcebundle = ResourceBundle.getBundle(s, locale, classloader);
+ else
+ resourcebundle = ResourceBundle.getBundle(s, locale);
             bundles.put(s, resourcebundle);
         }
         return resourcebundle;

This uses another ResourceBundle method if bootstrap classloader is to be used. Another way could possibly be to use a thread context classloader before trying the system classloader, but that's just my guess (I didn't try if the other classloader turns out non-null).

I hope that my convoluted description can be understood. The above patch fixes the problem for me.

regards,

Marcin

 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Marcin Owsiany added a comment - 07/Oct/05 05:09 AM
Oops, forgot to note that this was with jdo-1.0.2.jar

Craig Russell added a comment - 07/Oct/05 07:43 AM
I am surprised that the I18NHelper would ever get the bootstrap class loader passed as an argument. I thought that the bootstrap class loader was used only for java.lang classes and system extensions which (I thought) should never be the class loader passed to the I18NHelper.

Aha! Did you put the JDO implementation jar files into a system extension?

Marcin Owsiany added a comment - 07/Oct/05 05:58 PM
I put the kodo jars and the jdo-1.0.2.jar in $CATALINA_HOME/common/endorsed/

Craig Russell added a comment - 07/Oct/05 10:57 PM
I adapted the patch to the apache versions of the code line. This patch applies to both the api11 and the api20 projects.

Please review.

Thanks,

Craig

Michael Bouschen added a comment - 18/Oct/05 06:07 AM
The patch calls ResourceBundle.getBundle(bundleName, locale) if the specified class loader is null. This calls getBundle with the caller's class loader, in this case the class loader that loaded I18NHelper.

I'm not sure whether this is the right class loader. Instead we could use the system class loader, if the specified class loader is null:
    if (loader == null) {
         loader = ClassLoader.getSysytemClassLoader();
    }
    messages = ResourceBundle.getBundle(bundleName, locale, loader);

Craig Russell added a comment - 18/Oct/05 06:52 AM
I agree with Michael that the appropriate class loader is the system class loader (since clearly the bootstrap class loader is not accessible as an instance). The only catch is that the getSystemClassLoader is protected by security and needs to be called in a doPrivileged block.

I'll see how to incorporate this information into the patch.

Craig Russell added a comment - 18/Oct/05 08:59 AM
This patch applies at the "trunk" level. It includes a new method getSystemClassLoaderPrivileged that is used to load the bundle in case the class loader passed to loadBundle is null.

Craig Russell added a comment - 19/Oct/05 02:48 AM
Patch 2 was committed to both api11 and api20 repositories.

Committed version 326164.