2013-06-20 13 views
6

Abbiamo recentemente aggiornato a Java 7 Update 25 dall'aggiornamento 21 e ora stiamo riscontrando un'eccezione di puntatore nullo quando SwingUtilities.isEventDispatchThread() viene chiamato da un thread rmi perché AppContext.getAppContext () restituisce null.AppContext è nullo dal thread rmi con Java 7 Update 25

java.lang.NullPointerException a sun.awt.SunToolkit.getSystemEventQueueImplPP (Origine Sconosciuta) A sun.awt.SunToolkit.getSystemEventQueueImplPP (Origine Sconosciuta) A sun.awt.SunToolkit.getSystemEventQueueImpl (fonte sconosciuta) a java.awt.Toolkit.getEventQueue (Origine sconosciuta) a java.awt.EventQueue.isDispatchThread (Origine sconosciuta) a javax.swing.SwingUtilities.isEventDispatchThread (Origine sconosciuta) a ... ... a sun.reflect.NativeMethodAccessorImpl.invoke0 (Metodo nativo) a sun.reflect.NativeMethodAcce ssorImpl.invoke (sorgente sconosciuta) a sun.reflect.DelegatingMethodAccessorImpl.invoke (sorgente sconosciuta) a java.lang.reflect.Method.invoke (fonte sconosciuta) a sun.rmi.server.UnicastServerRef.dispatch (sorgente sconosciuta) a sun.rmi.transport.Transport $ 1.run (sorgente sconosciuta) a sun.rmi.transport.Transport $ 1.run (sorgente sconosciuta) a java.security.AccessController.doPrivileged (metodo nativo) a sun.rmi .transport.Transport.serviceCall (Origine sconosciuta) a sun.rmi.transport.tcp.TCPTransport.handleMessages (Origine sconosciuta) a sun.rmi.transport.tcp.TCPTransport $ ConnectionHandler.run0 (Sconosciuto source) a sole .rmi.transport.tcp.TCPTransport $ ConnectionHandler.run (Un noto Source) a java.util.concurrent.ThreadPoolExecutor.runWorker (Sconosciuto Source) a java.util.concurrent.ThreadPoolExecutor $ Worker.run (Sconosciuto Source) a java.lang.Thread.run (fonte sconosciuta)

Questo errore è presente solo dal web start, quando eseguiamo la nostra applicazione attraverso un IDE, va bene.

Qualcun altro si è imbattuto in questo? Qualche idea di cosa è stato modificato nell'ultimo aggiornamento di AppContext?

Sembra altri stanno avendo problemi in qualche modo correlati con AppContext dopo l'aggiornamento: https://forums.oracle.com/message/11077767#11077767

+0

non posso fornire una soluzione, ma abbiamo lo stesso problema con Java 7 Update 25. Abbiamo un'applicazione basata su Swing che è distribuita e avviata con Java Webstart. Funziona anche come server RMI. Quando una chiamata RMI in entrata chiama EventQueue.isDispatchThread, otteniamo la stessa eccezione: sun.awt.AppContext # getAppContext() sembra restituire null in questa situazione. È una regressione seria che rende Java 7 Update 25 inutile per la nostra applicazione. Quando proviamo ad applicare la soluzione alternativa dal Bug 4711515, otteniamo un ulteriore passo avanti. Ma poi il problema Nullpointer in JNLPClassLoader.getPermissions si verifica ... – Holger

+0

Il problema Nullpointer in JNLPClassLoader.getPermissons() è menzionato qui: http://stackoverflow.com/questions/17230773/java-7-update-25-makes-our -java-web-start-application-fail-with-no-logging C'è qualcosa di gravemente sbagliato nella versione di aggiornamento ... – Holger

+0

Abbiamo probabilmente un problema simile qui: . Nella nostra applicazione il client funge anche da server RMI. –

risposta

0

Non è una risposta definitiva, ma si tratta di una soluzione, che funziona per me.

L'applicazione necessario salvare il corrente AppContext in EVT:

AppContext evtContext; //field 

    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      evtContext = AppContext.getAppContext(); 
     } 
    }); 

Poi tutte le chiamate a SwingUtilities.invokeLater(..) da discussioni RMI deve essere sostituito con invokeLater2(Runnable rn) metodo personalizzato che utilizza sun.awt.SunToolkit.invokeLaterOnAppContext(..,..) come:

void invokeLater2(Runnable rn) { 
    if (AppContext.getAppContext() == null) { 
     logger.warning("AppContext is null, using EVT AppContext" 
      + " through SunToolKit"); 
     sun.awt.SunToolkit.invokeLaterOnAppContext(evtContext, rn); 
    } else { 
     SwingUtilities.invokeLater(rn); 
    } 
} 

Sfortunatamente tutte le chiamate a SwingUtilities.invokeLater (..) dai thread RMI devono essere sostituite e il programma ora dipende dall'API proprietaria Sun JRE interna.

Spero che Oracle fornisca presto patch per JRE 1.7.0.u25 per risolvere questo problema.

- Questa soluzione è stata apportata ai suggerimenti da guruman nei commenti here.

+0

1) Devo compilare il mio codice usando java 5 e mi dà errore quando ho provato a usare sun.awt.SunToolkit.invokeLaterOnAppContext, era qualcosa che è stato aggiunto dopo Java 5? 2) Ho provato l'approccio di creare un thread appartenente a un particolare gruppo di thread e ha funzionato, ma non posso davvero usare questo approccio dato che finirò per creare molti thread. 2.1) C'è un modo per assegnare a creare un singolo thread e assegnargli compiti 2.2) C'è un modo per assegnare compiti all'EDT senza usare invokeLater e invokeAndWait – user1216750

+0

In realtà io uso java 1.7.0 Update 21. Ho fatto non controllare il codice nelle precedenti versioni di Java. Questo è il motivo per cui personalmente non mi piace usare le classi interne di JRE. –

1

Ho avuto lo stesso problema con Java3D in esecuzione come Java Web Start. Ho trovato un'altra soluzione. Devi preparare il tuo InvokeLaterProcessor con la coda dei runnables. Essa deve estendersi Discussione e pick up runnables ed elaborarli in metodo run:

public class InvokeLaterProcessor extends Thread { 

    private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<Runnable>(1); 

    public InvokeLaterProcessor(String name) { 
    super(name); 
    } 

    public void invokeLater(Runnable runnable) { 
    try { 
     queue.put(runnable); 
    } catch (InterruptedException ex) { 
     log.warn("invokeLater interrupted"); 
    } 
    } 

    public void run() { 
    Runnable runnable=null; 
    do { 
     try { 
     runnable = queue.take(); 
     SwingUtilities.invokeLater(runnable); 
     } catch (InterruptedException ex) { 
     runnable=null; 
     } 
    } while(runnable!=null); 
    } 
} 

Che tutto quello che dovete fare è creare in statica di una classe che si crea nel thread principale:

static { 
    invokeLaterProcessor=new InvokeLaterProcessor("MyInvokeLater"); 
    invokeLaterProcessor.start(); 
} 

e il processo runnables da questo codice:

invokeLaterProcessor.invokeLater(runnable); 

non è necessario proprietaria

sun.awt.SunToolkit.invokeLaterOnAppContext(evtContext, rn) 
0

Ecco una soluzione alternativa per JDK-8019274, inclusa in una classe di utilità.

Per noi, invokeAndWait() era il grande problema. Questo esempio ha la correzione esistente per invokeLater() e una nuova correzione per invokeAndWait().

Note:

  • avrete bisogno jnlp.jar
  • chiamata init() presto nel metodo main(), prima di chiamare invokeLater()!
  • Sostituire tutte le chiamate al invokeLater() e invokeAndWait() con queste chiamate

(Disclaimer:.. Questo è il nostro prodotto dal Alcuni aspetti di questa soluzione potrebbe non essere applicabile a voi)

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