从 JRE 1.7.0_21 升级到 1.7.0_25-b15 后,当我的应用程序从 Java WebStart 运行时,它开始在 SwingUtilities.invokeLater(...) 中抛出 NullPointerException。令人惊讶的是,当它作为独立应用程序(在 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。如果程序由 JWS 启动,看起来 JRE 会为不同的应用程序上下文创建额外的事件队列。 JWS 中有 3 个 AppContext 和 3 个 EVT,如果程序从 IDE 执行,则只有一个上下文和 EVT。
---- 更新2:
下面的 guruman 建议有一个解决方法(非常感谢)。不幸的是,必须替换从 RMI 线程对 SwingUtilities.invokeLater(..)
的所有调用,并且程序开始依赖于 Sun JRE 内部 API。
我仍在寻找不特定于 Sun JRE 的更通用的方法。我认为这是一个 JRE 错误。也许可以通过某种方式对其进行修补:AppContext 在 RMI 线程中不应为空。
----更新3:
我做了一个简单的测试用例来说明问题。它包含4个文件。要运行此测试用例,需要对目标 jar (TestCase.jar) 进行签名。首先在 launch.jnlp 中指定正确的 codebase,然后通过 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接口(interface)定义(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 错误:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8019272
其他相关错误:
最佳答案
我发现我认为可以更好地解决此错误。
我只是在调用 SwingUtilities 或任何与 Swing 相关的组件方法之前添加了以下代码。它为 RMI 线程创建一个新的 AppContext(运行下面的代码时,RMI 线程必须是当前线程)。
if(AppContext.getAppContext() == null){
SunToolkit.createNewAppContext();
}
由于我的应用程序的需要,我能够将它添加到使用 SwingUtilities 的单个方法中,但您可能需要将它添加到 RMI 可调用对象的每个方法中。
代码只需运行一次,因此请检查您的应用程序的行为。
关于java - 通过 Java Webstart 运行时 invokeLater 出现 NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17275259/