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 クライアントを実行できます。実行が成功すると、フレームは次のようになります。
ただし、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
その他の関連するバグ: