以下是堆栈的顶部:
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。看起来,如果程序是由JWS启动的,JRE会为不同的应用程序上下文创建额外的事件队列。在JWS中有3个AppContext和3个EVT,而如果程序是从IDE执行的,则只有一个上下文和EVT。
---- Update2:
以下是guruman在下面提出的一个解决方法(非常感谢)。不幸的是,所有对
SwingUtilities.invokeLater(..)
的调用都必须被替换,并且程序开始依赖于Sun JRE的内部API。我仍然在寻找更通用的方法,而不是特定于Sun JRE。我认为这是一个JRE的bug。也许可以通过某种方式进行修补:RMI线程中的AppContext不应该为空。
---- 更新3:
我已经制作了一个简单的测试用例来展示这个问题。它包含4个文件。要运行这个测试用例,需要对目标jar文件(TestCase.jar)进行签名。首先,在launch.jnlp中指定正确的codebase,然后通过Java Web Start运行服务器(例如,使用javaws launch.jnlp)。屏幕上应该出现以下窗口:
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)
这里是测试用例文件。
JNLP文件定义launch.jnlp:
TestCase digital_infinity
TestCase TestCase
RMI接口定义(RmiService.java): package testcase; public interface RmiService extends java.rmi.Remote { void callBack() throws java.rmi.RemoteException; }
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); } 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); } }); } public void callBack() { Runnable rn = new Runnable() { public void run() { textField.setText("CallBack successfully called."); frame.pack(); } }; SwingUtilities.invokeLater(rn); } }
简单客户端代码: package testcase;
import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RmiClient { public static void main(String[] args) throws Exception { Registry reg = LocateRegistry.getRegistry(RmiServiceImpl.PORT); RmiService serv = (RmiService) reg.lookup("test"); serv.callBack(); } }
---- 更新4:
我提交的JRE Bug:https://bugs.java.com/bugdatabase/view_bug?bug_id=8019272
其他相关的Bug:
NullPointerException
漏洞,但在以前的JVM版本中从未发生过这种情况,这是否可能?也许这些线程之间存在竞争条件。这样的问题可能不会在运行时始终引发异常,但随着外部环境的变化,这些问题可能开始出现,即使抛出异常的代码已经很长时间没有更改了。 - Byron Hawkins