Java Load Class Dinamically in ChildClassLoader related to SystemClassLoader - java

I have a java web application that comes wiht a abstract class, (lets call it ClassA) deployed on an application server. This java web application also loads classes from external jars dinamically, by using the approach on this question's accepted response.
Everything works fine, until I get to load a class (call it, ClassB) that extends from ClassA. I get a java.lang.ClassNotFoundException: ClassA error message on my SysOut, pointing to the line that loads the class: urlClassLoader.loadClass(className);.
I assumed I wouldn't have a problem, since when I call URLClassLoader.newInstance(), it gets created from the default parent class loader, which I suppose should be the SystemClassLoader, which I assume contains the WebApp classes.
Is any of my assumptions wrong?
How can I load a class from an external Jar, which happens to extend from a parent class loaded in the WebApplication classloader?
I tried changing the deployment configuration of my WebApp from parent first to parent last and got the same error. I also tried doing a Class.forName("ClassA") before executing the URLClassLoader.loadClass("ClassB") line but got the same error again.

It seems that ClassA is loaded by some container's WebAppClassLoader which is a child of SystemClassLoader. Your custom URLClassLoader is a child of SystemClassLoader too and that's the reason why ClassB doesn't "see" ClassA.
To solve the problem, ClassB should be loaded by WebAppClassLoader or its child, it can be achieved with:
ClassLoader webAppCL = ClassA.class.getClassLoader();
URLClassLoader myCL = new URLClassLoader(urls, webAppCL);

Related

Glassfish java.lang.NoClassDefFoundError when using webapp as dependency

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.

LinkageError jdbc.datasource.DataSourceTransactionManager

I have a problem with this linkage error, we have projects with the same code and works :
this.plateformTransactionManager = new DataSourceTransactionManager();
this.plateformTransactionManager.setDataSource(dataSource);
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = plateformTransactionManager.getTransaction(def);
Stack trace at runtime :
java.lang.LinkageError: loader constraint violation: when resolving method org.springframework.jdbc.datasource.DataSourceTransactionManager.getTransaction(Lorg/springframework/transaction/TransactionDefinition;) Lorg/springframework/transaction/TransactionStatus;" the class loader (instance of org/apache/catalina/loader/WebappClassLoader) of the current class, com/as24/referentiel/daos/ExternalUserDAO, and the class loader (instance of rg/apache/catalina/loader/StandardClassLoader) for resolved class, org/springframework/jdbc/datasource/DataSourceTransactionManager, have different Class Objects for the type org/springframework/transaction/TransactionDefinition used in the Signature
I read it's a maven dependancies error but we used Spring only on version 3.0.7.REALEASE
I don't find any soutions on the web ...
This is a problem of shared libraries between between server and web application.
In java a class is identified by its name (including package name) and its class loader (which loaded it). So if you have the same MyBean.class inside web application WEB-INF/classes and also inside ${catalina.home}/lib, these will be viewed as two different classes (i.e. myBean instanceof MyBean == false). When JVM approaches situation where some class is using class XYZ in its method signatures and XYZ is loaded by a different classloader than a class XYZ known by the current class loader, then LinkageError is raised.
You problem is that you have Spring dependencies (at least spring-tx) on the shared / system class loader and also within web application dependencies.
To solve your issue you need to do one of the following:
remove Spring dependencies from server class-loaders
remove duplicate dependency from the web application (mark it as provided dependency in POM)
add duplicate dependencies on endorsed class-loader (by that web application class-loader will ignore the web application dependency).

Tomcat DLL already loaded in another classloader

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.

NoClassDefFoundError in a WAR in an EAR

I have an EAR that has a WAR inside, which has a jar inside it's WEB-INF/lib/. However, the class in that jar cannot be located (throws NoClassDefFoundError). I've verified that the class is indeed in that jar by doing javap -classpath
Any ideas why?
I'm on glassfish.
I figured it out. For some strange reason, the classes being loaded failed to get loaded due to some missing classes. i.e. I have a ClassA and it was loading ClassB. ClassB depends on ClassC. But since ClassC was not in my classpath, ClassB's class was not properly loaded. Thus, accessing ClassB from ClassA threw NoClassDefFoundError.

How to deal with LinkageErrors in Java?

Developing a heavily XML-based Java-application, I recently encountered an interesting problem on Ubuntu Linux.
My application, using the Java Plugin Framework, appears unable to convert a dom4j-created XML document to Batik's implementation of the SVG specification.
On the console, I learn that an error occurs:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;" the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader) of the current class, org/apache/batik/dom/svg/SVGOMDocument, and the class loader (instance of <bootloader>) for interface org/w3c/dom/Document have different Class objects for the type org/w3c/dom/Attr used in the signature
at org.apache.batik.dom.svg.SVGDOMImplementation.createDocument(SVGDOMImplementation.java:149)
at org.dom4j.io.DOMWriter.createDomDocument(DOMWriter.java:361)
at org.dom4j.io.DOMWriter.write(DOMWriter.java:138)
I figure that the problem is caused by a conflict between the original classloader from the JVM and the classloader deployed by the plugin framework.
To my knowledge, it's not possible to specify a classloader for the framework to use. It might be possible to hack it, but I would prefer a less aggressive approach to solving this problem, since (for whatever reason) it only occurs on Linux systems.
Has one of you encountered such a problem and has any idea how to fix it or at least get to the core of the issue?
LinkageError is what you'll get in a classic case where you have a class C loaded by more than one classloader and those classes are being used together in the same code (compared, cast, etc). It doesn't matter if it is the same Class name or even if it's loaded from the identical jar - a Class from one classloader is always treated as a different Class if loaded from another classloader.
The message (which has improved a lot over the years) says:
Exception in thread "AWT-EventQueue-0" java.lang.LinkageError:
loader constraint violation in interface itable initialization:
when resolving method "org.apache.batik.dom.svg.SVGOMDocument.createAttribute(Ljava/lang/String;)Lorg/w3c/dom/Attr;"
the class loader (instance of org/java/plugin/standard/StandardPluginClassLoader)
of the current class, org/apache/batik/dom/svg/SVGOMDocument,
and the class loader (instance of ) for interface org/w3c/dom/Document
have different Class objects for the type org/w3c/dom/Attr used in the signature
So, here the problem is in resolving the SVGOMDocument.createAttribute() method, which uses org.w3c.dom.Attr (part of the standard DOM library). But, the version of Attr loaded with Batik was loaded from a different classloader than the instance of Attr you're passing to the method.
You'll see that Batik's version seems to be loaded from the Java plugin. And yours is being loaded from " ", which is most likely one of the built-in JVM loaders (boot classpath, ESOM, or classpath).
The three prominent classloader models are:
delegation (the default in the JDK - ask parent, then me)
post-delegation (common in plugins, servlets, and places where you want isolation - ask me, then parent)
sibling (common in dependency models like OSGi, Eclipse, etc)
I don't know what delegation strategy the JPF classloader uses, but the key is that you want one version of the dom library to be loaded and everyone to source that class from the same location. That may mean removing it from the classpath and loading as a plugin, or preventing Batik from loading it, or something else.
Sounds like a classloader hierarchy problem. I can't tell what type of environment your application is deployed in, but sometimes this problem can occur in a web environment - where the application server creates a hierarchy of classloaders, resembling something like:
javahome/lib - as root
appserver/lib - as child of root
webapp/WEB-INF/lib - as child of child of root
etc
Usually classloaders delegate loading to their parent classloader (this is known as "parent-first"), and if that classloader cannot find the class, then the child classloader attempts to. For example, if a class deployed as a JAR in webapp/WEB-INF/lib tries to load a class, first it asks the classloader corresponding to appserver/lib to load the class (which in turn asks the classloader corresponding to javahome/lib to load the class), and if this lookup fails, then WEB-INF/lib is searched for a match to this class.
In a web environment, you can run into problems with this hierarchy. For example, one mistake/problem I've run into before was when a class in WEB-INF/lib depended on a class deployed in appserver/lib, which in turn depended on a class deployed in WEB-INF/lib. This caused failures because while classloaders are able to delegate to the parent classloader, they cannot delegate back down the tree. So, the WEB-INF/lib classloader would ask appserver/lib classloader for a class, appserver/lib classloader would load that class and try to load the dependent class, and fail since it could not find that class in appserver/lib or javahome/lib.
So, while you may not be deploying your app in a web/app server environment, my too-long explanation might apply to you if your environment has a hierarchy of classloaders set up. Does it? Is JPF doing some sort of classloader magic to be able to implement it's plugin features?
May be this will help someone because it works out pretty good for me. The issue can be solve by integrating your own dependencies. Follow this simple steps
First check the error which should be like this :
Method execution failed:
java.lang.LinkageError: loader constraint violation:
when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;"
the class loader (instance of org/openmrs/module/ModuleClassLoader) of the current class, org/slf4j/LoggerFactory,
and the class loader (instance of org/apache/catalina/loader/WebappClassLoader) for resolved class, org/slf4j/impl/StaticLoggerBinder,
have different Class objects for the type taticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory; used in the signature
See the two highlighted class. Google search for them like "StaticLoggerBinder.class jar download" & "LoggeraFactory.class jar download". This will show you first or in some case second link (Site is http://www.java2s.com ) which is one of the jar version you have included in your project. You can smartly identify it yourself, but we are addicted of google ;)
After that you will know the jar file name, in my case it is like slf4j-log4j12-1.5.6.jar & slf4j-api-1.5.8
Now the latest version of this file is available here http://mvnrepository.com/ (actually all version till date, this is the site from where maven get your dependencies).
Now add both file as a dependencies with the latest version (or keep both file version same, either chosen version is old). Following is the dependency you have to include in pom.xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
Can you specify a class loader? If not, try specifying the context class loader like so:
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(yourClassLoader);
callDom4j();
} finally {
thread.setContextClassLoader(contextClassLoader);
}
I'm not familiar with the Java Plugin Framework, but I write code for Eclipse, and I run into similar issues from time to time. I don't guarantee it'll fix it, but it's probably worth a shot.
The answers from Alex and Matt are very helpful. I could benefit from their analysis too.
I had the same problem when using the Batik library in a Netbeans RCP framework, the Batik library being included as a "Library Wrapper Module". If some other module makes use of XML apis, and no dependency on Batik is needed and established for that module, the class loader constraint violation problem arises with similar error messages.
In Netbeans, individual modules use dedicated class loaders, and the dependence relationship between modules implies suitable class loader delegation routing.
I could resolve the problem by simply omitting the xml-apis jar file from the Batik library bundle.
As specified in this question, enabling the -verbose:class will make the JVM log information about all classes being loaded, which can be incredibly helpful to understand where the classes are coming from in more complex scenarios & applications.
The output you get looks roughly like this (copied from that question):
[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
I find this class be loaded twice. Find the reason is that parallelWebappClassLoader load class by itself first rather than use it's parent classLoader.

Categories