I have a DLL that provides access to Window's LoadLibrary and FreeLibrary (to get around Java not being able to unload it). I need this DLL to persist between each webapp as it cannot be loaded twice.
I've followed these steps:
http://wiki.apache.org/tomcat/HowTo#I.27m_encountering_classloader_problems_when_using_JNI_under_Tomcat
and placed a singleton class (as a jar) with the static { System.loadLibrary() } call under ${TOMCAT}/lib. When my Tomcat webapp accesses this Singleton, it still throws the error
UnsatisfiedLinkError: Native Library ${TOMCAT}\lib\Native.dll already loaded in another classloader
Is this wrong in thinking that Tomcat's common classloader is loading this class, instantiating it (as per: http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html) and then passing the reference to the webapp thus bypassing the webapp classloader?
Is there a way to tell Tomcat to instantiate my singleton (forcing the common classloader to do it) and then provide that instance to satisfy my webapp's dependency?
Any discussion much appreciated.
Follow this link to unload dll:
http://www.codethesis.com/blog/unload-java-jni-dll
(web archive version)
I tried it with my web application running on Tomcat, and it works!
Briefly, I call System.loadLibrary from the class which is loaded by my custom class loader using reflection. This class is then removed by garbage collector.
Related
I am running into an issue deploying a Quarkus App that uses an SPI implementation injected by our deployment system.
In our pom, we specify the SPI interface (which calls to ServiceLoader.load(class) in it's static initializer). When we deploy the Quarkus app, we decompose the QuarkusRunner jar, extract the Main-Class from the MANIFEST and construct a command line similar to "java -cp ... io.quarkus.bootstrap.runner.QuarkusEntryPoint". The class path includes everything in quarkus-app/app, lib/boot and lib/main plus the SPI implementation we intend to use.
When we run the app, and try to use code that invokes our SPI ServiceLoader code, we get the following error:
java.util.ServiceConfigurationError: : not a subtype.
I read this as the ClassLoader used by Quarkus (which contains the SPI-interface) and the ClassLoader that loads the SPI-Implementation, are somehow not connected (i.e., isolated from one another).
Things of interest:
We are using Quarkus 1.13.2-Final
I have tried to make our SPI Interface a parentFirstArtifact (it has no dependencies), with no luck.
Looking at the code for QuarkusEntryPoint, it looks like it loads all the classes placed into quarkus/quarkus-application.dat, which is created during the maven build, into the Quarkus RunnerClassLoader, whose parent is the System ClassLoader. My assumption was items on the classpath were added to the System ClassLoader.
Question:
At this point, I am completely lost as to what is actually happening. How do I get my SPI-Implementation to work with Quarkus?
When using Quarkus's fast-jar, almost everything is loaded into the JVM via the RunnerClassLoader (the exceptions are the classloader itself, and a tiny number of supporting classes and utility libraries).
What you would consider the classpath (that is User code, code generated or transformed by Quarkus and dependencies) are indexed in the quarkus-application.dat file which is built at build time and cannot be modified.
I have a very simple POC setup where I deploy a JEE7 webapp on a wildfly 9.
Via a jaxRs Resource endpoint I can trigger a "plugin loader".
The PluginLoader does use a directory and scans for jar files in the directory, which URLs then will be fed into a URLClassLoader.
Afterwards I use the ServiceLoader to load implmementations of a simple interface from those URLs.
When the ServiceLoader starts iterating over the found implementations, I get this error:
Caused by: java.util.ServiceConfigurationError: com.test.MyIface: Provider com.test.MyImpl not a subtype
The structure is also very simple:
MyIface.jar is the interface.
MyImpl.jar is a implementation of MyIface, while it contains a META-INF/services file with the correct naming and content for MyIface..
The webapp itself only knows MyIFace of course.
In JavaSE using a simple main entry point and invoking the loader from there, everything works.
In JavaEE the services file seems to be ignored though..at least that is what I get from the exception.
I put it in src/main/resources/META-INF/services
and in src/main/resource/WEB-INF/classes/META-INF/services (as I read that already in context with SPI and webapps)
In order for this to work, the following 2 steps must be followed:
Instantiate a ClassLoader (the stock URLClassLoader will do) that knows both the targeted jar AND the classloader of the web application.
It needs to know the targeted jar obviously to load the service implementation
It needs to have the classloader of the web app as parent so that all the classloaders share the interface class; otherwise, even if the custom classloader loads the interface, you will run to ClassCastExceptions like "MyIface is not an instance of MyIface"
Specify the classloader you created using the ServiceLoader.load(Class, ClassLoader) method
I'm trying to extend webapp functionality without redeploying webapp archive. App runs under Glassfish 3.
Basically, what I did is the following:
webapp.war contains some part.jar which is a part of webapplication. It contains some class SomeClass. Webapp allows some custom configuration where descendant of SomeClass may be loaded dynamically using Class.forName.
I derived ExtensionClass from SomeClass (ExtensionClass extends SomeClass), compiled it using part.jar and got some extension.jar.
I tried to put extension.jar into domain/lib and domain/lib/ext. But then app when loading ExtensionClass says java.lang.NoClassDefFoundError: SomeClass (note that it says about parent class). It seems that classloader which loads library cannot find the base class, which is contained in the webapp.
Goal: I'd like to extend app using dependencies from it but without rebuilding.
Question: what can be done in this case?
Edit
If I just put my extension.jar into applications/mywebapp/WEB-INF/libs it obviously works, as if I'd put it into webapp.war itself. But it is very dirty, I want to solve it without doing this dirtiness and without touching webapp.war.
I am in the process of migrating a legacy application from weblogic to Tomcat 6.
The application needs to access ejbs; to achieve that, I added wlclient.jar to the classpath.
When the methods in the legacy jars responsible for communicating with the ejbs are called, I get the following exception :
javax.naming.NamingException: Unhandled exception in lookup
[Root exception is org.omg.CORBA.MARSHAL: vmcid: SUN minor code: 211 completed: Maybe]
which cause is :
Caused by: java.lang.IllegalArgumentException: interface com.xxx.xxx.InterfaceName is not visible from class loader
at java.lang.reflect.Proxy.getProxyClass(Proxy.java:353)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581)
at weblogic.iiop.ProxyDesc.readResolve(ProxyDesc.java:45)
That Interface seems to be used to define the method called from the client to return some informations from the server where the ejbs are deployed.
When running with verbose:class, I find that the Interface is actually loaded from the local jar. The corresponding interface on the server is loaded too :
[Loaded com.xx.xx.InterfaceName_t3s99q_InterfaceNameIntf from http://192.168.x.xx:port/path/classes/]
Even though I do not have any idea how that works internally, I assumed the lookup had gone well since it finds the correct class on the server.
When putting breakpoints in Proxy.getProxyClass, I found the used classloader when the exception occurs was a Launcher$AppClassLoader; the URL's it used to lookup were those in my local classpath (a.k.a. src/main/java and such) and not the webapp's path (a.k.a. WEB-INF/lib and such).
So my question is : is it possible that the wrong ClassLoader gets used for that specific lookup (local one instead of Tomcat's webapp level one)?
Can I specify a specific ClassLoader?
Am I looking in the completely wrong direction to resolve that issue ?
Well I lost 1.5 days on this, so I'll post an answer hoping it will eventually be useful to someone.
The problem was that I included wlclient.jar in eclipse classpath, and since the oracle implementation uses super.getClass().getClassLoader() , it would return the ClassLoader used to load the Class in which the call is made a.k.a. the local ClassLoader and not the one from the Webapp, so it did not have visiblity of the webapp dependencies.
I updated my project to include the jar in WEB-INF/lib so it would be loaded by the webapp and thus super.getClass().getClassLoader() would return the right ClassLoader.
An other possibility would have been to modify that line to use Thread.currentThread.getContextClassLoader instead.
I have two war file app1.war and app2.war deployed in a single JBoss instance. Package names for java classes for both war files starts with com.myapp
To add further, there are some Classes that are common between the two apps while there are some that have same fully qualified class names but are different (Source Code has changed).
I want to know, if this could pose threat of any kind to the deployment scenario?
You could get class loading problems if your applications are not isolated, i.e. have their own class loading repository and class loaders. If you configure JBoss to isolate the applications from each other you should be fine (I don't know what is the default for your version but 4.2.3 that we use does not isolate apps by default).
To clarify that a bit:
If you have two classes with different implementations but the same FQCN you could get the wrong class from the class loader for the application that is loaded second. Even if the implementation was the same you could get class cast exceptions or other strange behavior if one app gets the class from the other app.
I had a similar situation with multiple apps.Look at my solution here
Best way is to isolate class loading for your application archives.
For JBoss 5.1.0 GA following worked for me.
Create jboss-classloading.xml file in WEB-INF folder.
Added following lines to this file
Here,
export-all="NON_EMPTY" => Makes sure the classes loaded for this app is not exported
import-all="true" => Imports and uses all of the class definition available.
parent-first="false" => If more than one class with same name is found, one defined under the application will be used first.
FYI. This also helped me embedding the log configuration of log4j in the application war file. Will need to place log4j.xml in WEB-INF/classes and have a log4j.jar in WEB-INF/lib folder.
There will be one class loader instance for each application or standalone module. In other words, classes in app1.war will be loaded in different class loader than the classes in app2.war. This is the default behavior of any Java EE server; So it really doesn't matter about having classes with the same package/names and/or different content. This is the default behavior of any Java EE server.
Having said that, if you tweak the class loader policy of the server or try to load classes (reflect) using anything other than Thread.currentThread().getContextClassLoader(), you could be asking for trouble.