We recently updated to Java 7 Update 25 from Update 21 and are now experiencing a null pointer exception when SwingUtilities.isEventDispatchThread() is called from an rmi thread becuase AppContext.getAppContext() is returning null.
java.lang.NullPointerException at sun.awt.SunToolkit.getSystemEventQueueImplPP(Unknown Source) at sun.awt.SunToolkit.getSystemEventQueueImplPP(Unknown Source) at sun.awt.SunToolkit.getSystemEventQueueImpl(Unknown Source) at java.awt.Toolkit.getEventQueue(Unknown Source) at java.awt.EventQueue.isDispatchThread(Unknown Source) at javax.swing.SwingUtilities.isEventDispatchThread(Unknown Source) at ... ... at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) at sun.rmi.transport.Transport$1.run(Unknown Source) at sun.rmi.transport.Transport$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.Transport.serviceCall(Unknown Source) at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source)
This error is only present from web start, when we run our application through an IDE, it is fine.
Has anyone else ran into this? Any idea of what was changed in the latest update regarding AppContext?
It seems others are having somewhat related issues with AppContext after the update: https://forums.oracle.com/message/11077767#11077767
I had the same problem with Java3D running as Java Web Start. I have found another solution. You have to prepare you own InvokeLaterProcessor with queue of runnables. It must extend Thread and pick ups runnables and process them in run method:
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);
}
}
Than all you have to do is create it in static of some class which is created in main thread:
static {
invokeLaterProcessor=new InvokeLaterProcessor("MyInvokeLater");
invokeLaterProcessor.start();
}
and process runnables by this code:
invokeLaterProcessor.invokeLater(runnable);
You don't need proprietary
sun.awt.SunToolkit.invokeLaterOnAppContext(evtContext, rn)
It is not a final answer but it is a workaround, that works for me.
The application need to save the current AppContext in EVT:
AppContext evtContext; //field
SwingUtilities.invokeLater(new Runnable() {
public void run() {
evtContext = AppContext.getAppContext();
}
});
Then all the calls to SwingUtilities.invokeLater(..)
from RMI threads must be replaced with custom invokeLater2(Runnable rn)
method that uses sun.awt.SunToolkit.invokeLaterOnAppContext(..,..)
like:
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);
}
}
Unfortunately all the calls to SwingUtilities.invokeLater(..) from RMI threads must be replaced and the program now depends on internal Sun JRE proprietary API.
I hope Oracle will provide patches for the JRE 1.7.0.u25 soon to address this problem.
--
This workaround was made under suggestions from guruman
in the comments here.
Here is a workaround for JDK-8019274, packaged in a utility class.
For us, invokeAndWait()
was the big issue. This example has the
existing fix for invokeLater()
and a new fix for invokeAndWait()
.
Notes:
- You'll need jnlp.jar
- Call
init()
early in yourmain()
method, before callinginvokeLater()
! - Replace all calls to
invokeLater()
andinvokeAndWait()
with these calls
(Disclaimer: This is from our product. Some aspects of this solution may not apply to you.)
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;
}
}
来源:https://stackoverflow.com/questions/17223304/appcontext-is-null-from-rmi-thread-with-java-7-update-25