17

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:

おそらくいくつかのセキュリティ変更と AppContext オブジェクトに関して、JRE 1.7.0_25 更新に同様の問題があります: https://forums.oracle.com/message/11080621 https://forums.oracle.com/thread/2552799。提案された修正を試みました: https://forums.oracle.com/message/11082162#11082162しかし、成功しませんでした。

アプリケーションで 0 から 2 までの番号を持つ 3 つの AWT-EventQueue スレッドを確認できます。プログラムが JWS によって開始された場合、JRE はさまざまなアプリケーション コンテキストに対して追加のイベント キューを作成するようです。JWS には 3 つの AppContext と 3 つの EVT があり、プログラムが IDE から実行される場合、コンテキストと EVT は 1 つだけです。


----アップデート 2:

以下のグルマンが提案する回避策があります(どうもありがとう)。残念ながら、SwingUtilities.invokeLater(..)RMI スレッドからのすべての呼び出しを置き換える必要があり、プログラムは Sun JRE 内部 API に依存し始めます。

Sun JRE に固有ではない、より一般的なアプローチをまだ探しています。JREのバグだと思います。何らかの方法でパッチを当てることができるかもしれません: AppContext は RMI スレッドで null であってはなりません。


---- Update3:

問題を示す簡単なテスト ケースを作成しました。4つのファイルで構成されています。このテスト ケースを実行するには、宛先 jar (TestCase.jar) に署名する必要があります。最初に、launch.jnlp で正しいコードベースを指定してから、Java Web Start でサーバーを実行します (例: javaws launch.jnlp を使用)。次のフレームが画面に表示されます。

起動後のサーバーアプリフレーム

その後、RMI クライアントを実行できます。実行が成功すると、フレームは次のようになります。

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 バグ: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8019272

その他の関連するバグ:

4

5 に答える 5