carloslozano / javachromiumembedded

Automatically exported from code.google.com/p/javachromiumembedded
0 stars 0 forks source link

Changes needed in native code to run from within JavaWebStart (FindClass cannot find org.cef classes from CEF callbacks) #99

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Get the latest (e.g. r93), package it into a JavaWebStart app
2. Run JavaWebStart app
3. Java console logs many ClassNotFoundException coming from CEF callbacks in 
particular:

java.lang.NoClassDefFoundError: org/cef/network/CefRequest_N
Caused by: java.lang.ClassNotFoundException: org.cef.network.CefRequest_N
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 java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)

What is the expected output? What do you see instead?

No stack trace and things work fine.

What version of the product are you using? On what operating system?

Latest JCEF r93, Windows 7, Java 1.7 64 bit

Please provide any additional information below.

This appears to be a side effect of how env->FindClass works under 
JavaWebStart. E.g. in request_handler.cpp, CEF callbacks execute the following.

  jobject jrequest = NewJNIObject(env, "org/cef/network/CefRequest_N");

This invokes env->FindClass.
But under JavaWebStart, this thread uses a classloader that doesn't have access 
to the jar's of the JavaWebStart app. It can find classes such as java.* 
packages but not our own org.cef.* classes. 

This doesn't affect threads that come from Java e.g. CefCookieManager_N.cpp 
JNIEXPORT jobject JNICALL 
Java_org_cef_network_CefCookieManager_1N_N_1GetGlobalManager
  (JNIEnv *env, jclass cls) is fine because it's called from Java and uses  a different classloader.

Here are some links that describe this javawebstart behavior.

https://community.oracle.com/thread/1307947?start=0&tstart=0
http://forums.netbeans.org/ptopic8237.html
http://stackoverflow.com/questions/20851342/using-qt-c-to-call-java-code-through
-jni-findclass-does-not-find-class

I'm reporting this to give a heads-up about this special behavior. I'm looking 
into a workaround, probably by creating global references to the class'es from 
Java_org_cef_CefApp_N_1Initialize. Then CEF callbacks could use these global 
references instead of relying on env->FindClass.

Again the interesting part is that it does not affect all JNI calls, just CEF 
callbacks that don't come from Java.

Original issue reported on code.google.com by christop...@gmail.com on 2 Jul 2014 at 1:50

GoogleCodeExporter commented 9 years ago
The best explanation of that behavior I have found so far is below. If anyone 
knows better, please speak up...

http://forums.netbeans.org/ptopic8237.html
Quote:
/* Find classes used in the on... methods. The classes can't be looked up 
locally since these methods are called from a system thread and FindClass 
therefore uses the system class loader to find these classes. This fails if 
used with Java Web Start, which uses its own class loader. Looking the classes 
up in the constructor works because the constructor is called from a Java -> 
Native call and the class loader of the calling Java class is used. We need to 
create global refs since the class objects are used from a different thread. */

Original comment by christop...@gmail.com on 2 Jul 2014 at 2:05

GoogleCodeExporter commented 9 years ago
Yes, that approach worked. Creating a cache of global refs to jclass'es and 
using it instead of FindClass from CEF threads avoids this JavaWebStart 
limitation.
If anyone has a better idea, happy to hear it... Preparing a patch that I will 
attach here.

Original comment by christop...@gmail.com on 2 Jul 2014 at 4:07

GoogleCodeExporter commented 9 years ago
A good side effect of this is that I discovered 
https://code.google.com/p/javachromiumembedded/issues/detail?id=100

Original comment by christop...@gmail.com on 2 Jul 2014 at 5:42

GoogleCodeExporter commented 9 years ago
Here's the patch. In jni_util.cpp, we look up a global cache of class 
references before attempting to invoke env->FindClass. The global cache is 
created from the Java thread that invokes CefApp_N.Initialize. That thread can 
load CEF classes in both JavaWebStart and non JavaWebStart deployments.

Let me know what you think. If you prefer not to take it, we'll have to keep 
patching this over. Hopefully this will be useful to anyone doing JavaWebStart 
deployments in future.

Original comment by christop...@gmail.com on 4 Jul 2014 at 5:42

Attachments:

GoogleCodeExporter commented 9 years ago
I wouldn't want to maintain this giant array of class names by hand. If there 
isn't a way to auto-discover them in the code then I'd suggest writing a python 
script to generate the array (as separate h/cpp files). The script could be run 
from gyp_cef similar to how the make_version_header.py script is run.

Original comment by magreenb...@gmail.com on 10 Jul 2014 at 5:11

GoogleCodeExporter commented 9 years ago
I had the same issue when trying to embed jcef to a NetBeans module. AFAIK the 
NetBeans platform uses a separate class loader for each module. I came up with 
a more general solution by first storing a global reference to the correct 
classloader in CefApp.N_Initialize and then using the stored classloader 
everywhere instead of env->FindClass() .

I attached the patch, please merge it if you agree. I tested it on Win32.

Original comment by janos.br...@gmail.com on 20 Oct 2014 at 7:25

Attachments: