Как я могу безопасно решить эту проблему загрузчика классов контекста Java? - PullRequest
7 голосов
/ 06 декабря 2009

Один и только один из моих сотен пользователей испытывает проблемы с запуском моего настольного Java-приложения. Это начинается только для него примерно в одной трети времени. Другие две трети времени, когда NullPointerException генерируется при запуске:

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)

Я проследил трассировку стека и обнаружил, что причина в том, что

Thread.currentThread().getContextClassLoader()

в JEditorPane возвращает ноль.

Поиск в Google показывает, что это спорадическая, очень редкая и загадочная проблема, которая затрагивает несколько человек.

Мой вопрос: что я могу сделать в качестве обходного пути? Это может сработать, если я позвоню до создания EditorPane:

Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader());

Но я не совсем понимаю загрузчиков классов так, как хотел бы (и я пытался понять их лучше). Я чувствую, что изменение contextClassLoader в EDT может иметь плохие последствия.

Есть идеи, что я могу сделать?

РЕДАКТИРОВАТЬ: У меня была переписка с кем-то, кто хорошо понимает Java ClassLoaders. Кажется, это неясное условие гонки ClassLoader. Это ошибка в Java.

1 Ответ

4 голосов
/ 06 декабря 2009
Thread.currentThread().getContextClassLoader()

Если код в JEditorPane.registerEditorKitForContentType не проверяет нулевое возвращаемое значение в приведенном выше коде, это ошибка в JEditorPane. Обратите внимание, что MyClass.class.getClassLoader() также может возвращать ноль . Единственный, на кого вы можете положиться - это система ClassLoader .

Шаблон для установки контекста ClassLoader для вызова обычно выглядит примерно так:

Thread thread = Thread.currentThread();
ClassLoader old = thread.getContextClassLoader();
thread.setContextClassLoader(fooClassLoader);
try {
  // do call that depends on context ClassLoader
} finally {
  thread.setContextClassLoader(old);
}

Значение, которое должно быть установлено с помощью setContextClassLoader, будет зависеть от цели кода, который его потребляет, и от структуры ClassLoader платформы, в которой вы работаете.

В автономном приложении вы, вероятно, можете обойтись, просто используя ClassLoader (передавая ссылку в текущий класс):

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;
}

В платформе подключаемого модуля, чувствительного к ClassLoader (основным примером является сервер Java EE), было бы полезно понять природу и использование схемы загрузки.

...