NullPointerException em invokeLater durante a execução do Java Webstart

Após o upgrade do JRE 1.7.0_21 para 1.7.0_25-b15, meu aplicativo começou a lançar o NullPointerException no SwingUtilities.invokeLater (...) quando ele é executado a partir do Java WebStart. Surpreendentemente, quando executado como um aplicativo independente (fora do JWS), ele funciona muito bem.

Aqui está o topo da pilha:

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

Para obter uma imagem completa: o método setBaseUnits (..) é chamado como um retorno de chamada do RMI pelo servidor remoto. O rastreamento completo da pilha é bastante longo.

Existe algo no modelo de segurança que mudou no RMI ou JWS que poderia quebrar as coisas? Nesse caso, esperaria alguma exceção de segurança, mas poderia ser algo que não é corretamente detectado no JRE e leva ao NPE.

Qualquer sugestão é apreciada.

----Atualização 1:

Há problemas semelhantes com a atualização do JRE 1.7.0_25, provavelmente com relação a algumas mudanças de segurança e objetos AppContext:https://forums.oracle.com/message/11080621 https://forums.oracle.com/thread/2552799 . Eu tentei corrigir sugerido:https://forums.oracle.com/message/11082162#11082162 mas sem sucesso algum.

Eu posso ver 3 threads AWT-EventQueue no meu aplicativo com números de 0 a 2. Parece que o JRE cria filas de eventos adicionais para diferentes contextos de aplicativos se o programa for iniciado pelo JWS. Existem 3 AppContext e 3 EVTs no JWS e existe apenas um contexto e EVT se o programa for executado a partir do IDE.

----Update2:

Existe uma solução alternativa sugerida porguruman abaixo (muito obrigado). Infelizmente todas as chamadas para oSwingUtilities.invokeLater(..) dos encadeamentos RMI devem ser substituídos e o programa começa a depender da API interna do Sun JRE.

Eu ainda estou procurando uma abordagem mais geral, não específica para o Sun JRE. Eu acho que é um bug do JRE. Talvez possa ser corrigido de alguma forma: AppContext não deve ser nulo no thread RMI.

---- Update3:

Eu fiz um teste simples para mostrar o problema. Consiste em 4 arquivos. Para executar este caso de teste, é necessário assinar o frasco de destino (TestCase.jar). Primeiro de tudo, especifique corretocodebase no launch.jnlp, execute o servidor pelo Java Web Start (por exemplo, usando javaws launch.jnlp). Um quadro a seguir deve aparecer na tela:

Então o cliente RMI poderia ser executado. Após a execução bem sucedida, o quadro deve consistir em:

mas se você tentar executar o servidor usando o JWS Você obterá a seguinte exceção no programa cliente (a exceção é propagada do servidor RMI para o cliente 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)

Então, aqui estão os arquivos do caso de teste:

1) Definição de arquivo 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) Definição da interface RMI (RmiService.java):

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

3) Código de serviço RMI e a classe principal de serviço:

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) código do cliente simples:

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

Outros bugs relacionados:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8019274http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8028290http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8017770http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8021370

questionAnswers(5)

yourAnswerToTheQuestion