One and only one of my hundreds of users has trouble starting my Java desktop app. It only starts for him about one-third of the time. The other two-thirds of the time a NullPointerException is thrown at startup:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at java.util.Hashtable.put(Hashtable.java:394)
at javax.swing.JEditorPane.registerEditorKitForContentType(JEditorPane.java:1327)
at javax.swing.JEditorPane.registerEditorKitForContentType(JEditorPane.java:1309)
at javax.swing.JEditorPane.loadDefaultKitsIfNecessary(JEditorPane.java:1387)
at javax.swing.JEditorPane.getKitTypeRegistry(JEditorPane.java:1344)
at javax.swing.JEditorPane.getEditorKitClassNameForContentType(JEditorPane.java:1340)
at javax.swing.JTextPane.<init>(JTextPane.java:76)
at myapp.Launcher$1.run(Launcher.java:13)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:633)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
I've followed the stack trace to find that the cause is that
Thread.currentThread().getContextClassLoader()
in JEditorPane is returning null.
Googling reveals that this is a sporadic, very infrequent, and mysterious problem that affects a few people.
My question is, what can I do as a work-around? This might work, if I call it before creating an EditorPane:
Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());
But I don't really understand classloaders as well as I would like (and I've tried to understand them better). I feel that changing the contextClassLoader in the EDT could have bad ramifications.
Any ideas what I can do?
EDIT: I had some correspondence with someone who understands Java ClassLoaders well. It seems this is an obscure ClassLoader race condition. That is, a bug in Java.
Thread.currentThread().getContextClassLoader()
If the code in JEditorPane.registerEditorKitForContentType
does not check for a null return value in the above code, this is a bug in JEditorPane
. Note that MyClass.class.getClassLoader()
may also return null. The only one you can rely on is the system ClassLoader.
The pattern for setting the context ClassLoader
for an invocation usually looks something like this:
Thread thread = Thread.currentThread();
ClassLoader old = thread.getContextClassLoader();
thread.setContextClassLoader(fooClassLoader);
try {
// do call that depends on context ClassLoader
} finally {
thread.setContextClassLoader(old);
}
The value that should be set via setContextClassLoader
will depend on the intent of the code that is consuming it and the design of the ClassLoader
framework you are running in.
In a stand-alone application, you can probably get away with just using this ClassLoader
(passing in a ref to the current class):
private ClassLoader findClassLoaderForContext(Class<?> c) {
ClassLoader context = Thread.currentThread().getContextClassLoader();
ClassLoader me = c.getClassLoader();
ClassLoader system = ClassLoader.getSystemClassLoader();
return (context == null) ? (me == null) ? system : me : context;
}
In a ClassLoader-sensitive plug-in framework (a Java EE server would be a prime example), it would pay to understand the nature and usage of the loading scheme.