visor start software ejecutar documentos chrome archivos aplicaciones java swing rmi java-web-start

software - NullPointerException en invokeLater mientras se ejecuta a través de Java Webstart



software de java web start (5)

Aquí hay una solución para JDK-8019274, empaquetado en una clase de utilidad. Para nosotros, invokeAndWait () seguía siendo un problema. Este ejemplo tiene la corrección existente para invokeLater () y una nueva corrección para invokeAndWait ().

Notas:

  • Tendrá que incluir el jnlp.jar en su proyecto.
  • Llame a init () al principio de su método main (), antes de llamar a invokeLater ()
  • Reemplace todas sus llamadas a SwingUtilities invokeLater () e invokeAndWait () con estas llamadas

(Descargo de responsabilidad: Esto es de nuestro producto. Es posible que algunos aspectos de esta solución no se apliquen a usted).

public class JreFix { private static String badVersionInfo = null; private static AppContext awtEventDispatchContext = null; private static AppContext mainThreadContext = null; private static Boolean isWebStart = null; private static BasicService basicService = null; private static IntegrationService integrationService = null; /** * Call this early in main(). */ public static void init() { if (isWebstart() && isApplicableJvmType()) { String javaVersion = System.getProperty("java.version"); if ("1.7.0_25".equals(javaVersion)) { badVersionInfo = "7u25"; } else if ("1.7.0_40".equals(javaVersion)) { badVersionInfo = "7u40"; } else if (javaVersion != null && "1.6.0_51".equals(javaVersion.substring(0,8))) { badVersionInfo = "6u51"; } else if ("javaws-10.25.2.16".equals(System.getProperty("javawebstart.version"))) { badVersionInfo = "Web Start 10.25.2.16"; } } if (badVersionInfo != null) { mainThreadContext = AppContext.getAppContext(); try { SwingUtilities.invokeAndWait(new Runnable() { public void run() { awtEventDispatchContext = AppContext.getAppContext(); } }); } catch (Exception e) { displayErrorAndExit(null); } if (mainThreadContext == null || awtEventDispatchContext == null) { displayErrorAndExit(null); } } } public static void invokeNowOrLater(Runnable runnable) { if (hasAppContextBug()) { invokeLaterOnAwtEventDispatchThreadContext(runnable); } else { SwingUtilities.invokeLater(runnable); } } public static void invokeNowOrWait(Runnable runnable) { if (hasAppContextBug()) { fixThreadAppContext(null); } try { SwingUtilities.invokeAndWait(runnable); } catch (Exception e) { // handle it } } public static boolean hasAppContextBug() { return isJreWithAppContextBug() && AppContext.getAppContext() == null; } public static void invokeLaterOnAwtEventDispatchThreadContext(Runnable runnable) { sun.awt.SunToolkit.invokeLaterOnAppContext(awtEventDispatchContext, runnable); } public static void fixThreadAppContext(Component parent) { try { final Field field = AppContext.class.getDeclaredField("threadGroup2appContext"); field.setAccessible(true); Map<ThreadGroup, AppContext> threadGroup2appContext = (Map<ThreadGroup, AppContext>)field.get(null); final ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup(); threadGroup2appContext.put(currentThreadGroup, mainThreadContext); } catch (Exception e) { displayErrorAndExit(parent); } if (AppContext.getAppContext() == null) { displayErrorAndExit(parent); } } private static boolean isJreWithAppContextBug() { return badVersionInfo != null; } private static void displayErrorAndExit(Component parent) { JLabel msgLabel = new JLabel("<html>" + "Our application cannot run using <b>Web Start</b> with this version of Java.<p><p>" + "Java " + badVersionInfo + " contains a bug acknowledged by Oracle (JDK-8019274)."); JOptionPane.showMessageDialog(parent, msgLabel, "Java Version Error", JOptionPane.ERROR_MESSAGE); System.exit(1); } private static boolean isApplicableJvmType() { String vendor = System.getProperty("java.vendor"); String vmName = System.getProperty("java.vm.name"); if (vendor != null && vmName != null) { return vmName.contains("Java HotSpot") && (vendor.equals("Oracle Corporation") || vendor.equals("Sun Microsystems Inc.")); } return false; } private static boolean isWebstart() { if (isWebStart == null) { try { basicService = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService"); isWebStart = true; } catch (UnavailableServiceException e) { isWebStart = false; } try { integrationService = (IntegrationService) ServiceManager.lookup("javax.jnlp.IntegrationService"); } catch (UnavailableServiceException e) { } } return isWebStart; } }

Después de actualizar de JRE 1.7.0_21 a 1.7.0_25-b15 mi aplicación comenzó a lanzar NullPointerException en SwingUtilities.invokeLater (...) cuando se ejecuta desde Java WebStart. Sorprendentemente, cuando se ejecuta como una aplicación independiente (fuera de JWS), funciona muy bien.

Aquí está la parte superior de la pila:

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 obtener una imagen completa: el método setBaseUnits (..) se llama como una devolución de llamada desde RMI por el servidor remoto. La traza de pila completa es bastante larga.

¿Hay algo en el modelo de seguridad que haya cambiado en RMI o JWS que pueda romper las cosas? Si es así, esperaría alguna excepción de seguridad, pero podría ser algo que no se detecta correctamente en JRE y lleva a NPE.

Cualquier sugerencia es apreciada.

---- Update1:

Hay problemas similares con JRE 1.7.0_25 actualización probablemente con respecto a algunos cambios de seguridad y objetos AppContext: https://forums.oracle.com/message/11080621 https://forums.oracle.com/thread/2552799 . Intenté la solución sugerida: https://forums.oracle.com/message/11082162#11082162 pero sin ningún éxito.

Puedo ver 3 subprocesos AWT-EventQueue en mi aplicación con números del 0 al 2. Parece que JRE crea colas de eventos adicionales para diferentes contextos de aplicaciones si JWS inicia el programa. Hay 3 AppContext y 3 EVT en JWS y solo hay un contexto y EVT si el programa se ejecuta desde IDE.

---- Update2:

Hay una solución alternativa sugerida por guruman a continuación (muchas gracias). Desafortunadamente, todas las llamadas a los SwingUtilities.invokeLater(..) de RMI deben reemplazarse en SwingUtilities.invokeLater(..) , y el programa comienza a depender de la API interna de Sun JRE.

Todavía estoy buscando un enfoque más general no específico para Sun JRE. Creo que es un error de JRE. Tal vez podría parchearse de alguna manera: AppContext no debería ser nulo en el hilo RMI.

---- Update3:

He hecho un caso de prueba simple para mostrar el problema. Consta de 4 archivos. Para ejecutar este caso de prueba, uno necesita firmar el jar de destino (TestCase.jar). En primer lugar, especifique el código base correcto en launch.jnlp, luego ejecute el servidor con Java Web Start (por ejemplo, utilizando javaws launch.jnlp). Debería aparecer un cuadro siguiente en la pantalla:

Entonces el cliente RMI podría ser ejecutado. Después de la ejecución exitosa, el marco debe consistir en:

pero si intenta ejecutar el servidor utilizando JWS obtendrá la siguiente excepción en el programa cliente (la excepción se propaga desde el servidor RMI al 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)

Así que aquí están los archivos de casos de prueba:

1) Definición de archivo 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) Definición de la interfaz RMI (RmiService.java):

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

3) Código de servicio RMI y la clase principal de servicio:

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 de cliente simple:

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:

Error de JRE que envié: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8019272

Otros errores relacionados:


El problema se produce en el entorno Webstart. Antes de la versión Webstart de Java 7u25, AppContext se configuraba en el grupo de subprocesos del sistema. Sin embargo, se establece en el grupo de hilo principal.

Si tiene un subproceso basado en un grupo de subprocesos donde su padre o abuelo no es el grupo de subprocesos principal, no tiene sun.awt.AppContext.

Debe crear su hilo basado en el grupo de hilos del administrador de seguridad si existe uno.

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


Encontré lo que creo que es una mejor solución a este error.

Acabo de agregar el siguiente código antes de llamar a SwingUtilities o cualquier método de componente relacionado con Swing. Crea un nuevo AppContext para el subproceso RMI (el subproceso RMI debe ser el subproceso actual cuando se ejecuta el código a continuación).

if(AppContext.getAppContext() == null){ SunToolkit.createNewAppContext(); }

Debido a las necesidades de mi aplicación, pude agregarla en un solo método que usaba SwingUtilities, pero es posible que deba agregarla a todos los métodos de su objeto que se puede llamar de RMI.

El código debe ejecutarse solo una vez, así que verifique el comportamiento de su aplicación.


Esto sigue sucediendo en Mac Sierra. Pero puedo solucionarlo llamando a:

if (sun.awt.AppContext.getAppContext() == null) { sun.awt.SunToolkit.createNewAppContext(); }

justo antes de mi primera llamada SwingUtilities.invokeLater (...).


Java 7u65 que salió ayer (2014-07-15) afirma haber solucionado este o un problema muy similar, en JDK-8019724. Estoy probando ahora mismo para averiguarlo: un controlador de uno de nuestros proveedores no funciona bajo Java Web Start, y nos ha mantenido en Java 6.

ETA: Sí, parece que esto resuelve nuestros problemas!