chromiumembedded / java-cef

Java Chromium Embedded Framework (JCEF). A simple framework for embedding Chromium-based browsers in other applications using the Java programming language.
https://bitbucket.org/chromiumembedded/java-cef
Other
655 stars 147 forks source link

Native Library jawt.dll already loaded in another classloader #317

Open magreenblatt opened 5 years ago

magreenblatt commented 5 years ago

Original report by Leah (Bitbucket: Leah, GitHub: Leah).


I'm trying to write a plugin for IDEA which integrates chromium as a webview, not sure if this error is on me, but any help would be appreciated

java.lang.UnsatisfiedLinkError: Native Library C:\Users\harmony\.gradle\caches\modules-2\files-2.1\com.jetbrains\jbre\jbrex8u152b1343.15_windows_x64\jre\bin\jawt.dll already loaded in another classloader
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1907)
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1845)
    at java.lang.Runtime.loadLibrary0(Runtime.java:870)
    at java.lang.System.loadLibrary(System.java:1122)
    at org.cef.CefApp.<init>(CefApp.java:138)
    at org.cef.CefApp.getInstance(CefApp.java:213)
    at org.cef.CefApp.getInstance(CefApp.java:200)
    at org.panda_lang.pandomium.wrapper.PandomiumCEF.initialize(PandomiumCEF.java:16)
    at org.panda_lang.pandomium.Pandomium.lambda$initialize$0(Pandomium.java:34)
    at org.panda_lang.pandomium.loader.PandomiumLoader.callListeners(PandomiumLoader.java:56)
    at org.panda_lang.pandomium.loader.PandomiumLoaderWorker.load(PandomiumLoaderWorker.java:46)
    at org.panda_lang.pandomium.loader.PandomiumLoaderWorker.run(PandomiumLoaderWorker.java:21)
    at java.lang.Thread.run(Thread.java:745)

It's loaded from here https://bitbucket.org/chromiumembedded/java-cef/src/d64cd6c266d5a9fc485bdfb6d5b672201dc5dfc7/java/org/cef/CefApp.java?at=master#lines-138

magreenblatt commented 5 years ago

Original comment by Simon Haisz (Bitbucket: sihaisz, GitHub: sihaisz).


This issue happens because another classloader already loaded jawt.dll for some purpose. We ran into this issue because the Java Access Bridge (used for accessibility) loaded the dll first. Likely IDEA itself that is doing it. The annoying thing is that there is no way to check if a native dll is already loaded apart from trying and catching this exception. This is made more annoying because UnsatisfiedLinkError is used for a variety of errors, some of which should cause your program to crash, so you have to resort to checking the exception message.

This is the work around that we ended up using:

#!java
try {
    System.loadLibrary("jawt"); //$NON-NLS-1$
} catch(UnsatisfiedLinkError e) {
    String message = e.getMessage();
    if(!message.startsWith("Native Library") || !message.endsWith("already loaded in another classloader")) { //$NON-NLS-1$ //$NON-NLS-2$
        throw e;
    }
    System.out.println("Failed loading jawt.dll. It is already loaded by another classloader."); //$NON-NLS-1$
}

Something like this should probably be promoted to master. We didn't create a PR because we have other changes around initialization that only apply to our application. If you are concerned about trusting your program flow to an exception message that could be changed in any release (and you should be...) please note that our workaround has been in production for 3 years and we haven't had to touch it since.

magreenblatt commented 5 years ago

Original comment by Leah (Bitbucket: Leah, GitHub: Leah).


It's kinda confusing to me that java wouldn't handle that by itself

magreenblatt commented 5 years ago

Original comment by Nol Moonen (Bitbucket: nolmoonen, GitHub: nolmoonen).


Hello, I am having the exact same issue with IDEA. However, I stumbled upon a way to check if the library is loaded, without relying on this exception message flow. It basically comes down to:

try {
    java.lang.reflect.Field libraryNames;
    libraryNames = ClassLoader.class.getDeclaredField("loadedLibraryNames");
    libraryNames.setAccessible(true);
    Vector<String> libraries = (Vector<String>) libraryNames.get(ClassLoader.getSystemClassLoader());
    if (!libraries.contains("jawt")) {
        System.loadLibrary("jawt");
    }
} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

I am quite unsure whether this is something you'd want on the master branch, since it seems like a hack. However, it is still a serious issue to me. Should I make a pull request for this?

magreenblatt commented 5 years ago

Original comment by Max Senft (Bitbucket: Max Senft).


Hmhm, as there’s a pull request (https://bitbucket.org/chromiumembedded/java-cef/pull-requests/39/fix-loading-already-loaded-native-library/diff) now, I wonder if this is actually the correct solution. It looks more like a workaround to me, as the Java documentation states that subsequent calls of loadLibrary() will be ignored (see https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#loadLibrary-java.lang.String-).

I tested this problem by myself, by loading the "jawt" library immediately in Main() of my program and did not get an exception later, when loading the JCEF…

magreenblatt commented 4 years ago

I believe applications should now be able to work around this issue with the SystemBootstrap class added in fbfa9202 (bb).

magreenblatt commented 4 years ago

If SystemBootstrap is insufficient we can re-open.

magreenblatt commented 4 years ago
magreenblatt commented 10 months ago

Related PR: https://bitbucket.org/chromiumembedded/java-cef/pull-requests/71