Bug 983

Summary: Java3D webstart crashes when used with Jogl 2.1.4
Product: Java3D Reporter: president
Component: coreAssignee: Harvey Harrison <harvey.harrison>
Status: RESOLVED FIXED    
Severity: major CC: gouessej, harvey.harrison, sgothel
Priority: ---    
Version: unspecified   
Hardware: pc_all   
OS: all   
Type: DEFECT SCM Refs:
Workaround: TRUE
Bug Depends on:    
Bug Blocks: 1004    

Description president 2014-02-23 16:38:00 CET
The changes made in Jogl 2.1.4 make it impossible to run a Java3D application without crashing in webstart. It works fine in stand-alone applications but crashes when ran in webstart. Before crashing, the following exceptions are thrown:

java.lang.IllegalArgumentException: java.lang.reflect.InvocationTargetException 
        at jogamp.nativewindow.NativeWindowFactoryImpl.getAWTNativeWindow(NativeWindowFactoryImpl.java:107) 
        at jogamp.nativewindow.NativeWindowFactoryImpl.getNativeWindowImpl(NativeWindowFactoryImpl.java:66) 
        at javax.media.nativewindow.NativeWindowFactory.getNativeWindow(NativeWindowFactory.java:583) 
        at javax1.media.j3d.JoglPipeline.createNewContext(JoglPipeline.java:6374) 
        at javax1.media.j3d.Canvas3D.createNewContext(Canvas3D.java:4611) 
        at javax1.media.j3d.Canvas3D.createNewContext(Canvas3D.java:2381) 
        at javax1.media.j3d.Renderer.doWork(Renderer.java:881) 
        at javax1.media.j3d.J3dThread.run(J3dThread.java:271) 
Caused by: java.lang.reflect.InvocationTargetException 
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) 
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) 
        at java.lang.reflect.Constructor.newInstance(Unknown Source) 
        at jogamp.nativewindow.NativeWindowFactoryImpl.getAWTNativeWindow(NativeWindowFactoryImpl.java:105) 
        ... 7 more 
Caused by: 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 com.jogamp.common.util.awt.AWTEDTExecutor.invoke(AWTEDTExecutor.java:48) 
        at com.jogamp.nativewindow.awt.JAWTWindow$JAWTComponentListener.<init>(JAWTWindow.java:166) 
        at com.jogamp.nativewindow.awt.JAWTWindow$JAWTComponentListener.<init>(JAWTWindow.java:127) 
        at com.jogamp.nativewindow.awt.JAWTWindow.<init>(JAWTWindow.java:119) 
        at jogamp.nativewindow.jawt.windows.WindowsJAWTWindow.<init>(WindowsJAWTWindow.java:60) 
        ... 12 more 

DefaultRenderingErrorListener.errorOccurred: 
CONTEXT_CREATION_ERROR: Renderer: Error creating Canvas3D graphics context 
graphicsDevice = Win32GraphicsDevice[screen=0] 
canvas = runiter.grapher.math.Sheet$CapturingCanvas3D[canvas0,0,0,386x429] 
Exception in thread "J3D-BehaviorScheduler-1" java.lang.IllegalStateException: zip file closed 
        at java.util.zip.ZipFile.ensureOpen(Unknown Source) 
        at java.util.zip.ZipFile.getEntry(Unknown Source) 
        at java.util.jar.JarFile.getEntry(Unknown Source) 
        at com.sun.deploy.cache.CachedJarFile.getEntry(Unknown Source) 
        at java.util.jar.JarFile.getJarEntry(Unknown Source) 
        at com.sun.deploy.security.DeployURLClassPath$JarLoader.getResource(Unknown Source) 
        at com.sun.deploy.security.DeployURLClassPath.getResource(Unknown Source) 
        at java.net.URLClassLoader$1.run(Unknown Source) 
        at java.net.URLClassLoader$1.run(Unknown Source) 
        at java.security.AccessController.doPrivileged(Native Method) 
        at java.net.URLClassLoader.findClass(Unknown Source) 
        at com.sun.jnlp.JNLPClassLoader.findClass(Unknown Source) 
        at java.lang.ClassLoader.loadClass(Unknown Source) 
        at java.lang.ClassLoader.loadClass(Unknown Source) 
        at java.lang.ClassLoader.loadClass(Unknown Source) 
        at javax1.media.j3d.BehaviorScheduler.doWork(BehaviorScheduler.java:167) 
        at javax1.media.j3d.J3dThread.run(J3dThread.java:271)

gouessej believes that the change between January 25 and January 31 caused a regression as a result of this change: 
https://github.com/sgothel/jogl/commit/b7fafd30ffc5eac73880b264043582d74175a394

https://github.com/hharrison/java3d-core/blob/master/src/classes/share/javax/media/j3d/JoglPipeline.java#L6374

The stack trace clearly shows that we pass in the constructor of JAWTComponentListener. Although we couldn't reproduce this problem in a pure Jogl application. As it stands this problem only occurs when Jogl is called from Java3D.

gouessej believes that this change just exposes us to this bug in Oracle Java: 
http://bugs.java.com/view_bug.do?bug_id=8017776
It also looks like the following Java bugs: 
http://bugs.java.com/view_bug.do?bug_id=8019274

He suggested the following workaround which worked in OpenJDK 1.7 update 45 and Icedtea Webstart but didn't work in Windows with Oracle JVM:

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

TESTCASE:
This problem is easy to reproduce. Even the HelloWorld of Java3D triggers this crash if run from webstart.

Testcase jnlp: 
http://www.runiter.com/webstart/test_gc3/grapher.jnlp

Testcase Source Code:

public class MainApplication { 

public static void main(final String[] args) { 
      final SimpleUniverse universe = new SimpleUniverse(); 
      final BranchGroup group = new BranchGroup(); 
      group.addChild(new ColorCube(0.3)); 
      universe.getViewingPlatform().setNominalViewingTransform(); 
      universe.addBranchGraph(group); 
} 
}
Comment 1 Julien Gouesse 2014-02-24 09:47:38 CET
(In reply to comment #0)
> He suggested the following workaround which worked in OpenJDK 1.7 update 45
> and Icedtea Webstart but didn't work in Windows with Oracle JVM:
> 
> if(AppContext.getAppContext() == null){ 
>     SunToolkit.createNewAppContext(); 
> } 
> 
Actually, OpenJDK supports your example as is without my workaround as it is NOT concerned by this bug.
Comment 2 Sven Gothel 2014-02-25 06:07:05 CET
(In reply to comment #1)
> Actually, OpenJDK supports your example as is without my workaround as it is
> NOT concerned by this bug.

This has been confirmed by 'runiter' as well, 
i.e. bug only affects Oracle's JRE/AWT.

Question now is whether we should and can provide a workaround
and why this only happens w/ Java3D.

I added Harvey to this bug (Java3D ..).

I changed the type to 'ANNOTATION', since it documents another bug.

Reduced to 'major' ..

At runiter, maybe you can go ahead and test certain remedies
and try a workaround ?
Comment 3 president 2014-02-27 06:03:13 CET
I couldn't find a workaround so far, but I'm going to try to remotely debug webstart+Java3D to see if I can find a cause or workaround that way. I'll keep you updated.
Comment 4 president 2014-02-27 18:32:03 CET
My debug result shows that in webstart mode, the value of AppContext.getAppContext() is indeed null at this line:

javax.media.j3d.JoglPipeline.createNewContext(JoglPipeline.java:6374)

Interestingly when JoglPipeline is created AppContext.getAppContext() is not null. But since JoglPipeline.createNewContext is called from javax.media.j3d.Renderer what matters is the context at the time of the creation of Renderer object.

My debug result shows that when Renderer is created, the value of AppContext.getAppContext() is indeed null. The cause of this can be traced back to javax.media.j3d.MasterControlThread which is in charge of creating Renderer. MasterControlThread has a non-null context at the time of its creation so I'm not sure why MasterControlThread.run() method ends up with a null context.
Comment 5 Harvey Harrison 2014-02-27 20:11:28 CET
Thank you very much for debugging this, I know exactly why this happens, I believe MasterControl puts all the threads it manages in its own ThreadGroup, I won't have time to fix this before Saturday, but I'm pretty sure it's a trivial fix....THANK YOU very much for the analysis.
Comment 6 president 2014-02-27 20:19:20 CET
Glad I could be of help :) I will await your update hopefully in the weekend.
Comment 7 president 2014-03-09 18:54:24 CET
Hi Harvey, any updates on this? I noticed Java3D hasn't change in github yet.
Comment 8 president 2014-05-02 04:46:14 CEST
diff --git a/src/classes/share/javax/media/j3d/JoglPipeline.java b/src/classes/share/javax/media/j3d/JoglPipeline.java
index 2d3c073..3f40b2c 100644
--- a/src/classes/share/javax/media/j3d/JoglPipeline.java
+++ b/src/classes/share/javax/media/j3d/JoglPipeline.java
@@ -36,6 +36,7 @@
 import java.awt.GraphicsDevice;
 import java.awt.GraphicsEnvironment;
 import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
@@ -46,6 +47,7 @@
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -99,10 +101,20 @@
     private static final int MIN_FRAME_SIZE = 1;
 
     private GLProfile profile;
+    
+    private Object mainThreadContext;	// Fix for Bug 983
+    
     /**
      * Constructor for singleton JoglPipeline instance
      */
     protected JoglPipeline() {
+    	// Fix for Bug 983
+		try {
+			// Retrieve main thread AppContext instance by reflection
+			mainThreadContext = Class.forName("sun.awt.AppContext").getMethod("getAppContext").invoke(null);
+		} catch (final Throwable ex) {
+			// Let's consider app context is not necessary for the program
+		}    	
     }
 
     /**
@@ -6350,6 +6362,29 @@
 			surface.unlockSurface();
 		}
     }
+    
+    // Fix for Bug 983
+	private void checkAppContext() {
+		if (mainThreadContext != null) {
+			try {
+				// Check by reflection that sun.awt.AppContext.getAppContext() doesn't return null
+				// (required by ImageIO.write() and other JMF internal calls) to apply workaround proposed at
+				// http://stackoverflow.com/questions/17223304/appcontext-is-null-from-rmi-thread-with-java-7-update-25
+				final Class<?> appContextClass = Class.forName("sun.awt.AppContext");
+				if (appContextClass.getMethod("getAppContext").invoke(null) == null) {
+					final Field field = appContextClass.getDeclaredField("threadGroup2appContext");
+					field.setAccessible(true);
+					final Map threadGroup2appContext = (Map)field.get(null);
+					final ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
+					threadGroup2appContext.put(currentThreadGroup, mainThreadContext);
+				}
+			} catch (final Throwable ex) {
+				// Let's consider app context is not necessary for the program
+			}
+			// Don't need mainThreadContext anymore
+			mainThreadContext = null;
+		}
+	}    
 
     // This is the native method for creating the underlying graphics context.
     @Override
@@ -6357,6 +6392,8 @@
             Context shareCtx, boolean isSharedCtx,
             boolean offScreen) {
         if (VERBOSE) System.err.println("JoglPipeline.createNewContext()");
+        
+        checkAppContext();
 
 	    GLDrawable	glDrawable 	= null;
 	    GLContext 	glContext	= null;
Comment 9 president 2014-05-02 04:55:47 CEST
Please apply the above patch which fixes this issue.

Also, it is possible that we no longer need the following commit:

https://github.com/hharrison/java3d-utils/commit/14206478b7a5bf628b59094c92d5727291c7c2bf

The above commit fixed the issue for JCanvas only. I think my patch which replicates the code in the above commit may cover both JCanvas and Canvas3D but I didn't test for that so can't be sure.

Either way the problem report in this bug is resolved with my patch. I tested in my Windows machine. Other tests are welcomed.
Comment 10 Harvey Harrison 2014-05-05 17:44:45 CEST
Applied to j3dcore
commit: bdda2ac20bfef85271da764d1989ec3434d5c67a

Applied removal of the old JCanvas3D workaround in j3dutils
commit: 1f025a8787edec6e2ae1eab3a3fd0eb59319e3c4
Comment 11 Sven Gothel 2014-05-12 01:13:29 CEST
(In reply to comment #10)
> Applied to j3dcore
> commit: bdda2ac20bfef85271da764d1989ec3434d5c67a
> 
> Applied removal of the old JCanvas3D workaround in j3dutils
> commit: 1f025a8787edec6e2ae1eab3a3fd0eb59319e3c4

Pls consider using workaround of Bug 1004 Comment 4, i.e.:
  jogl aa1c04ebee23d0803880d6d68ae73109c1a5c178,
  uses gluegen f39100b35d0833764f2220e487ea7ea05ed87352

This will be crucial especial in cases of multiple AppContext 
per ClassLoader .. see comment.
Comment 12 Sven Gothel 2014-08-04 19:05:45 CEST
(In reply to comment #11)
> (In reply to comment #10)
> > Applied to j3dcore
> > commit: bdda2ac20bfef85271da764d1989ec3434d5c67a
> > 
> > Applied removal of the old JCanvas3D workaround in j3dutils
> > commit: 1f025a8787edec6e2ae1eab3a3fd0eb59319e3c4
> 
> Pls consider using workaround of Bug 1004 Comment 4, i.e.:
>   jogl aa1c04ebee23d0803880d6d68ae73109c1a5c178,
>   uses gluegen f39100b35d0833764f2220e487ea7ea05ed87352
> 
> This will be crucial especial in cases of multiple AppContext 
> per ClassLoader .. see comment.

That would be _excellent_, i.e. the image format auto-detection!

IMHO it would be nice to have a static method returning the type,
as well as an overloaded 'load' texture method.

Nice!
Comment 13 Julien Gouesse 2014-08-04 19:35:21 CEST
(In reply to comment #12)
> (In reply to comment #11)
> > (In reply to comment #10)
> > > Applied to j3dcore
> > > commit: bdda2ac20bfef85271da764d1989ec3434d5c67a
> > > 
> > > Applied removal of the old JCanvas3D workaround in j3dutils
> > > commit: 1f025a8787edec6e2ae1eab3a3fd0eb59319e3c4
> > 
> > Pls consider using workaround of Bug 1004 Comment 4, i.e.:
> >   jogl aa1c04ebee23d0803880d6d68ae73109c1a5c178,
> >   uses gluegen f39100b35d0833764f2220e487ea7ea05ed87352
> > 
> > This will be crucial especial in cases of multiple AppContext 
> > per ClassLoader .. see comment.
> 
> That would be _excellent_, i.e. the image format auto-detection!
> 
> IMHO it would be nice to have a static method returning the type,
> as well as an overloaded 'load' texture method.
> 
> Nice!

Are you sure that you comment the good bug report?
Comment 14 Sven Gothel 2014-08-04 19:52:35 CEST
(In reply to comment #13)
> > 
> > That would be _excellent_, i.e. the image format auto-detection!
> > 
> > IMHO it would be nice to have a static method returning the type,
> > as well as an overloaded 'load' texture method.
> > 
> > Nice!
> 
> Are you sure that you comment the good bug report?

sorry for bug 982 .. pls dismiss