После обновления с JRE 1.7.0_21 до 1.7.0_25-b15 мое приложение начало бросать NullPointerException в SwingUtilities.invokeLater(...), когда оно запускается из Java WebStart. Удивительно, когда он выполняется как отдельное приложение (вне JWS), он отлично работает.
Вот вершина стека:
Exception in thread "AWT-EventQueue-2" java.lang.NullPointerException
at sun.awt.SunToolkit.getSystemEventQueueImplPP(SunToolkit.java:1011)
at sun.awt.SunToolkit.getSystemEventQueueImplPP(SunToolkit.java:1007)
at sun.awt.SunToolkit.getSystemEventQueueImpl(SunToolkit.java:1002)
at java.awt.Toolkit.getEventQueue(Toolkit.java:1730)
at java.awt.EventQueue.invokeLater(EventQueue.java:1217)
at javax.swing.SwingUtilities.invokeLater(SwingUtilities.java:1290)
at AppletView$8.setBaseUnits(AppletView.java:536)
(...)
Чтобы получить полную картину: метод setBaseUnits (..) вызывается как обратный вызов RMI удаленным сервером. Полная трассировка стека довольно длинная.
Есть ли что-то в модели безопасности, которая изменилась в RMI или JWS, что может сломать вещи? Если это так, я бы ожидал некоторого исключения безопасности, но это могло быть что-то, что не было правильно обнаружено в JRE и привело к NPE.
Любые предложения приветствуются.
---- Обновление1:
Есть аналогичные проблемы с обновлением JRE 1.7.0_25, вероятно, в отношении некоторых изменений безопасности и объектов AppContext: https://forums.oracle.com/message/11080621 https://forums.oracle.com/thread/2552799. Я попробовал исправить ошибку: https://forums.oracle.com/message/11082162#11082162, но без каких-либо успехов.
Я вижу 3 приложения AWT-EventQueue в приложении с номерами от 0 до 2. Похоже, JRE создает дополнительные очереди событий для разных контекстов приложения, если программа запускается JWS. В JWS есть 3 AppContext и 3 EVT, и есть только один контекст и EVT, если программа выполнена из IDE.
---- Обновление2:
Существует обходное решение, предложенное гуруманом ниже (спасибо большое). К сожалению, все вызовы SwingUtilities.invokeLater(..)
из потоков RMI должны быть заменены, и программа начинает зависеть от внутреннего API Sun JRE.
Я все еще ищу более общий подход, не относящийся к Sun JRE. Я думаю, что это ошибка JRE. Возможно, это может быть исправлено каким-то образом: AppContext не должен быть пустым в потоке RMI.
---- Обновление3:
Я сделал простой тестовый пример, чтобы показать проблему. Он состоит из 4 файлов. Для запуска этого тестового примера необходимо подписать целевую банку (TestCase.jar). Прежде всего укажите правильную базу кода в launch.jnlp, затем запустите сервер с помощью Java Web Start (например, с помощью javaws launch.jnlp). На экране должен появиться следующий кадр:
Затем клиент RMI может быть выполнен. После успешного выполнения кадр должен состоять из:
но если вы попытаетесь выполнить сервер с помощью JWS, вы получите следующее исключение в клиентской программе (исключение распространяется с сервера RMI на клиент RMI):
Exception in thread "main" java.lang.NullPointerException
at sun.awt.SunToolkit.getSystemEventQueueImplPP(SunToolkit.java:1011)
at sun.awt.SunToolkit.getSystemEventQueueImplPP(SunToolkit.java:1007)
at sun.awt.SunToolkit.getSystemEventQueueImpl(SunToolkit.java:1002)
at java.awt.Toolkit.getEventQueue(Toolkit.java:1730)
at java.awt.EventQueue.invokeLater(EventQueue.java:1217)
at javax.swing.SwingUtilities.invokeLater(SwingUtilities.java:1290)
at testcase.RmiServiceImpl.callBack(RmiServiceImpl.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:553)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:808)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:667)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:273)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:251)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:160)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:194)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:148)
at com.sun.proxy.$Proxy0.callBack(Unknown Source)
at testcase.RmiClient.main(RmiClient.java:22)
Итак, вот файлы тестовых файлов:
1) Определение файла JNLP launch.jnlp:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<jnlp codebase="file:/home/user/NetBeansProjects/TestCase/dist/" href="launch.jnlp" spec="1.0+">
<information>
<title>TestCase</title>
<vendor>digital_infinity</vendor>
<homepage href=""/>
<description>TestCase</description>
<description kind="short">TestCase</description>
</information>
<security>
<all-permissions/>
</security>
<update check="always"/>
<resources>
<j2se version="1.7+"/>
<jar href="TestCase.jar" main="true"/>
</resources>
<application-desc main-class="testcase.RmiServiceImpl">
</application-desc>
</jnlp>
2) Определение интерфейса RMI (RmiService.java):
package testcase;
public interface RmiService extends java.rmi.Remote {
void callBack() throws java.rmi.RemoteException;
}
3) Код службы RMI и основной класс службы:
package testcase;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
*/
public class RmiServiceImpl extends java.rmi.server.UnicastRemoteObject
implements RmiService {
final static int PORT = 1099;
static JFrame frame;
static JTextField textField;
public RmiServiceImpl() throws RemoteException {
super(PORT);
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception {
Registry reg;
RmiServiceImpl service = new RmiServiceImpl();
try {
reg = LocateRegistry.getRegistry(PORT);
reg.rebind("test", service);
} catch (RemoteException ex) {
reg = LocateRegistry.createRegistry(PORT);
reg.rebind("test", service);
}
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
frame = new JFrame("Test App");
textField = new JTextField("Before call to callBack");
frame.getContentPane().add(textField);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
/** RMI callback */
public void callBack() {
Runnable rn = new Runnable() {
public void run() {
textField.setText("CallBack succesfully called.");
frame.pack();
}
};
SwingUtilities.invokeLater(rn);
}
}
4) Простой код клиента:
package testcase;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RmiClient {
public static void main(String[] args) throws Exception {
//now we trying to communicate with object through RMI
Registry reg = LocateRegistry.getRegistry(RmiServiceImpl.PORT);
//after got the registry, lookup the object and finally do call
RmiService serv = (RmiService) reg.lookup("test");
serv.callBack();
}
}
---- Обновление4:
JRE Bug Я отправил: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8019272
Другие связанные ошибки: