在通过Java Webstart运行时,invokeLater中出现NullPointerException [英] NullPointerException in invokeLater while running through Java Webstart

查看:103
本文介绍了在通过Java Webstart运行时,invokeLater中出现NullPointerException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从JRE 1.7.0_21升级到1.7.0_25-b15后,当从Java WebStart运行时,我的应用程序开始在SwingUtilities.invokeLater(...)中抛出NullPointerException。令人惊讶的是,它作为一个独立的应用程序(在JWS之外)执行时,效果很好。



这是堆栈的顶部:

 线程中的异常AWT-EventQueue-2java.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。



任何建议都表示赞赏。






---- Update1:​​



JRE 1.7.0_25更新可能存在类似的问题,可能涉及一些安全更改和AppContext对象:



其他相关错误:




解决方案

Webstart环境中出现此问题。在Webstart版本的Java 7u25之前,在系统线程组上设置了AppContext。然而它是在主线程组上设置的。



如果您有一个基于线程组的线程,其父或祖父不是主线程组,则它没有sun.awt.AppContext。



您应该根据安全管理器的线程组创建线程(如果存在)。

  Runnable task = .... 
ThreadGroup threadGroup = System.getSecurityManager()!= null
? System.getSecurityManager()。getThreadGroup()
:Thread.currentThread()。getThreadGroup();
线程t =新线程(threadGroup,task,my thread,0);


After upgraded from JRE 1.7.0_21 to 1.7.0_25-b15 my application started to throw NullPointerException in SwingUtilities.invokeLater(...) when it is run from Java WebStart. Surprisingly when it is executed as a standalone application (outside JWS), it works great.

Here is the top of the stack:

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)
    (...)

To get you full picture: the method setBaseUnits(..) is called as a callback from RMI by remote server. The full stack trace is quite long.

Is there something in security model that changed in RMI or JWS that could break things ? If so I would expect some security exception, but it could be something that is not correctly detected in JRE and leads to NPE.

Any suggestions are appreciated.


---- Update1:

There are similar issues with JRE 1.7.0_25 update probably regarding some security changes and AppContext objects: https://forums.oracle.com/message/11080621 https://forums.oracle.com/thread/2552799 . I tried suggested fix: https://forums.oracle.com/message/11082162#11082162 but without any success.

I can see 3 AWT-EventQueue threads in my application with numbers from 0 to 2. It looks like JRE creates additional event queues for different application contexts if program is started by JWS. There are 3 AppContext and 3 EVTs in JWS and there is only one context and EVT if program is executed from IDE.


---- Update2:

There is a workaround as suggested by guruman below (thanks a lot). Unfortunately all the calls to the SwingUtilities.invokeLater(..) from RMI threads must be replaced, and the program starts to depend on Sun JRE internal API.

I am still looking for more general approach not specific to Sun JRE. I think it is a JRE bug. Maybe it could be patched somehow: AppContext should not be null in RMI thread.


---- Update3:

I've made a simple test case to show the problem. It consists 4 files. To run this test case one need to sign the destination jar (TestCase.jar). First of all specify correct codebase in launch.jnlp, then run the server by Java Web Start (eg. using javaws launch.jnlp). A following frame should appear on the screen:

Then the RMI client could be executed. After successful execution the frame should consist:

but if You try to execute the server using JWS You will get the following exception in the client program (the exception is propagated from RMI server to RMI client):

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)

So here they are the test case files:

1) JNLP file definition 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 definition (RmiService.java):

package testcase;    
public interface RmiService extends java.rmi.Remote  {
    void callBack() throws java.rmi.RemoteException;
}

3) RMI service code and the service main class:

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) Simple client code:

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


---- Update4:

JRE Bug I submitted: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8019272

Other related bugs:

解决方案

The problem occurs in the Webstart environment. Before Webstart version of Java 7u25 the AppContext was set on the system thread group. Yet it is set on the main thread group.

If you have a thread based on a thread group where its parent or grandparent is not the main thread group it has no sun.awt.AppContext.

You should create your thread based on the thread group of the security manager if one exists.

Runnable task = ....
ThreadGroup threadGroup = System.getSecurityManager() != null
                                    ? System.getSecurityManager().getThreadGroup()
                                    : Thread.currentThread().getThreadGroup();
Thread t = new Thread(threadGroup, task, "my thread", 0);

这篇关于在通过Java Webstart运行时,invokeLater中出现NullPointerException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆