JBoss classloading IncompatibleClassChangeError - java

I'm migrating a project from JBoss 5 to 6.4. In the process I've updated several jar files in my build. I had several ClassCastExceptions which I resolved by taking the version of the specific classes from a jar in the JBoss system modules and using that in my app build. I've done so with two jars, jbossweb-7.5.26.Final-redhat-1.jar and jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-2.jar which contain the classes org.apache.catalina.connector.Request and javax.servlet.http.HttpServletRequest respectively. I'm using both of those jars as libs in my app build and all is compiling without error or warning. I've confirmed that when I remove those libs the app fails to compile as it can't find the included refernced classes, so I know I'm not actually referencing some other copy of those classes in my build. The app is deploying fine, but when I try hitting it I get an exception
JBWEB001018: An exception or error occurred in the container during
the request processing: java.lang.IncompatibleClassChangeError: Class
org.apache.catalina.connector.Request does not implement the requested
interface javax.servlet.http.HttpServletRequest
When I look at the class definitions from the jars in my editor (JDeveloper 12c) I can see that org.apache.catalina.connector.Request as defined in jbossweb-7.5.26.Final-redhat-1 does in fact implement javax.servlet.http.HttpServletRequest as defined in jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-2.jar.
I turned on verbose logging of classloading (JAVA_OPT -verbose:class), and confirmed that as JBoss is loading those two classes they are in fact being loaded from those two jar files, not some other one I didn't know about. I can see in my developer tool that the .class files I'm including in my project are from those same two jar files.
Possibly relevant, the classloading logs say JBoss is loading org.apache.catalina.connector.Request from RedHat/JBoss/EAP-6.4.0/modules/system/layers/base/.overlays/layer-base-jboss-eap-6.4.18.CP/org/jboss/as/web/main/. I'm not sure why it's using that copy/location rather than RedHat\JBoss\EAP-6.4.0\modules\system\layers\base\org\jboss\as\web\main.
So what gives? What I read on the error would suggest to me that jboss-servlet-api_3.0_spec-1.0.2.Final-redhat-2.jar was compiled against a different version of HttpServletRequest than jbossweb-7.5.26.Final-redhat-1 includes.
Is that interpretation or the error correct? Does JBoss 6.4 really ship with incompatible jar files in its system modules? If so what versions of these jars do I need and how to I check compatibility? And can I safely switch to a different jar file without introducing more incompatibilities with other jars? What other causes might I be missing? Any help much appreciated here.

1/ You shouldn't depend on servlet API implementation
2/ You shouldn't include those implementation classes in your application are they are provided by the server itself
3/ You shouldn't put API jars or their implementation in your application or if you do this you need to exclude those modules from your application using a jboss-deployment.xml

Looks like the issue was that I was not using the modules.xml correctly. I needed to add dependencies for the two packages in my module.xml file
<dependencies>
<module name="org.jboss.as.web"/>
<module name="javax.servlet.api"/>
</dependencies>
I had previously added those modules to my war file's jboss-deployment-structure.xml and that did not do the trick and left me with ClassNotFoundExceptions. My Jboss-deployment-structure.xml now just references my own custom module as a dependency, which in turn has dependencies of its own.
I still don't understand what was causing the previous error, as it's all the same versions of the jar files in question so I don't know where a conflict was, but this got the error to go away and no more ClassNotFoundExceptions.

Related

'Correct the classpath of your application' in a Springboot Configuration in IntelliJ

I have such an application with an IntelliJ-Idea IDE that has a Springboot configuration that errors out and gives the following recommendation:
The following method did not exist:
org.springframework.context.ConfigurableApplicationContext.setApplicationStartup(Lorg/springframework/core/metrics/ApplicationStartup;)V
The method's class, org.springframework.context.ConfigurableApplicationContext, is available from the following locations:
jar:file:/C:/Users/tlmitch/.m2/repository/org/springframework/spring-context/5.2.8.RELEASE/spring-context-5.2.8.RELEASE.jar!/org/springframework/context/ConfigurableApplicationContext.class
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.context.ConfigurableApplicationContext
I'm relatively new to IntelliJ-Idea and Springboot. If this were say Eclipse - I would probably just go to the Java Build Path and add the jar file that contains the class.
I'm less familiar with IntelliJ. So far if something is missing, I've just been running 'npm install' and that usually takes care of it.
Any suggestions?
Thanks much
If you've added Spring dependencies in your pom.xml file, they might be conflicting with the Spring dependencies in the lib folder in your project directory. Deleting the lib folder will fix this. (just make sure your project does not depend on other libraries in the lib folder before deleting).
As error suggests:
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.context.ConfigurableApplicationContext
you need to make sure your classpath contains only one such class. You can use Navigate | Class action to see if there are multiple versions of this class exists in project and exclude the duplicated library dependency in Maven pom.xml file.

Jersey Startup Issue : java.lang.NoSuchMethodError: com.google.common.collect.Sets.newIdentityHashSet()

I am using Jersey 2.5.1. I have my .war file packaged inside my .ear file. In the WEB-INF\lib folder of the .war module I have guava-14.0.1.jar. jersey-common-2.5.1.jar has dependency on guava.
Outside, in the ear/lib folder I have another guava jar --> guava-17.0 which is required by my project.
Both the jars have the method newIdentityHashSet(), but still I face the following error:
java.lang.NoSuchMethodError: com.google.common.collect.Sets.newIdentityHashSet()Ljava/util/Set;
at org.glassfish.jersey.model.internal.CommonConfig.(CommonConfig.java:220)
at org.glassfish.jersey.server.ResourceConfig$State.(ResourceConfig.java:110)
at org.glassfish.jersey.server.ResourceConfig.(ResourceConfig.java:351)
at org.glassfish.jersey.servlet.WebComponent.createResourceConfig(WebComponent.java:444)
at org.glassfish.jersey.servlet.WebComponent.(WebComponent.java:302)
Truncated. see log file for complete stacktrace
I have tried removing one of the guava jars, but the error is same. I have tried keeping same version in both the places. But didn't work.
I am deploying in Weblogic 12C.
Any help on this will be great.
It is not good idea to have duplicated jar files into the same ear/war file. Because errors of this type will appear. The best practice is to have only one version by library.
With that being said, you can check where is trying to load the class that don't have that method:
One way to check from which jar file are loaded specific classes, is setting the parameter: -verbose:class to the JRE.
This will give you a hint on this classloading issue.
Also WLS has bundled Classloader Analysis Tool (CAT) that helps you to identify duplicated jar files (conflicts, etc.)

Spring jars in tomcat lib folder

I am completely confused over how the class loading happens in tomcat. So please bear with me if my question sounds stupid.
We deploy multiple spring webapps on single tomcat server. To reduce the memory footprint, we thought of having spring, hibernate and database driver jars in tomcat lib folder so that those are shared by all webapps.
I started first by marking spring dependencies as provided and copied those jars to tomcat lib. But on server startup I started getting multiple ClassNotFoundErrors, like for commons-logging, commons-fileupload, jackson, so I had to move these jars as well to tomcat lib.
But then this started feeling fishy. What if in future I add another spring dependency to my project, say spring-data-cassandra. Would I need to move it's dependent jars as well? This might be unending. Also I might get CNF errors at runtime.
I tried to follow this link and brought back spring-context and spring-web back to application war. But it didn't work, got ClassNotFound on some class during WebApplicationIntializer initialization. I tried to understand the order of classes getting loaded in tomcat, but could not understand much.
Then I found a complete different explanation for JDBC driver loading which kinda contradicts to all other explanations and left me completely confused.
As I read more, I think it is not a right approach to move spring jars to tomcat lib, but still haven't got a good reasoning. And then why JDBC driver works? Can someone please explain? Also does classloader for each webapp creates a copy of each class?
Edit 1: I came to know that few dependencies are optional in spring jars, and will be required if are in use in my webapp. So spring-web depends on jackson libraries but is optional for my app. So I need to find out which all jars are required for my project and are also required by spring, those jars need to be moved to tomcat lib.
I will try to explain what I know.
When you deploy a WAR on your tomcat, the class loading will happen this way :
look for the class to load in your WAR classLoader
if not found move to parent (tomcat /lib folder)
What happen in your case is that spring also have a lot of dependencies, if you package it in your war, its dependencies would have been packaged as well and everything would have worked fine. But since you defined spring as provided, all its dependencies are considered provided as well, and when you put it in /lib folder, spring is accessible, but its dependencies are not.
What you need to do is put all spring dependencies and the dependencies of dependencies (etc.) in lib folder as well. Another solution is to define an intermediary WAR in your classloading hierarchy which will contains all your common libs.
If you prefer to create this type of thin WAR, one solution would be to first gather all runtime/provided dependencies and then copy them explicitly under Tomcat's common (or shared) library directory:
If under a Maven project, gather everything (including transitive dependencies) under target/dependency:
mvn dependency:copy-dependencies -DincludeScope=runtime
mvn dependency:copy-dependencies -DincludeScope=provided
Afterwards, copy to library. For example:
cp target/dependency/*.jar /usr/local/tomcat/lib/

NoClassDefFoundError using JAR from other project

There seem to be a number of questions on SO in this area, but I haven't been able to find one with exactly my scenario:
I have two projects in Eclipse: Core and Interface.
In the Core project I have 5 .jar files that contain useful classes and so on. Let's say that one of the jars is called DAO.
In the Interface project I have gone into the Build Path (via Right-Click > Build Path > Configure Build Path...) and gone to the Projects section, then added the Core project.
At first the Interface project still couldn't see the classes within the DAO.jar, which confused me, but I found that by going into the Core project's build path and checking the box next to DAO.jar (for export) the compiler stopped complaining and I was able to use classes from DAO.jar.
(Just to be specific, I'm making a Java webapp with Servlets and JSPs and so on, running on a local Tomcat server.)
I build the project, but when I navigate to a Servlet where the Get method involves a class from DAO.jar, I get a NoClassDefFoundError. Here's the stack trace:
java.lang.ClassNotFoundException: dao.DirectSqlVisitorImpl
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1702)
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547)
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(ClassLoader.java:792)
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2928)
org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1174)
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1669)
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1547)
interface.DonationsServletController.doGet(DonationsServletController.java:79)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
What I've gathered is that the JRE can't find the definition of the DirectSqlVisitorImpl class at runtime, even though the Eclipse error-checking lets it fly now that I've configured my Build Path in the (seemingly) correct fashion.
So what do I need to do to make sure that the jar gets pulled into the Interface project via the Core project?
As a note, I'd prefer not to do something like adding the jars directly to the Interface project, as the Core project could be used by other projects, and I'd like to have modifications to the Core project cascade to all dependent projects.
Thanks!
Your issue is the difference between the classpath used by eclipse as it compiles your code, and the classpath used at runtime by tomcat. I have dealt with this in an OSGi-based desktop application, though I haven't, yet, learned how to package a JavaEE application as separate components. However, I can tell you that the path to the answer is to learn how to build the WAR and/or EAR files so that the libraries in one are exported by the bundle and the other can import them.
One solution may be to add the dependent jars to the tomcat lib folder. i.e /lib.
Although the other answers here may be useful in some respect, overall they are incomplete.
I did find a way to resolve my problem and get rid of this error, though it does not quite accomplish the cascading system that I had hoped for.
It seems that dsh was right in that my issue is the difference between the classpath used by eclipse as it compiles my code, and the classpath used at runtime by tomcat. Or more specifically, the DAO jar file exists within my project, but doesn't get deployed to the tomcat server.
To solve this, I went to my Project's Properties > Web Deployment Assembly > Add... > Archives from Workspace > Add...
I then selected the DAO jar and added it in. The project then built and ran fine, without the NoClassDefFoundError. So my problem was solved, essentially.
However, I'm not sure what would happen if I changed/updated the jar files in the Core project.

Where to put a shared library in JBoss AS 7

I have multiple web applications each using spring-hibernate and other open source libraries and portlets, so basically now each war file includes those jar files. How do I move these jars to a common location so that I don't have to put these in each war file? My jars are places in D:/ directory.
I tried creating modules but no success. e.g. if I added jar
<resources>
<resource-root path="mylib.jar"/>
</resources>
and mylib.jar needs another ABC class. That ABC class is in my WAR class-path. Here I get exception while loading this module. mylib.jar could not find ABC class and throws exception.
If those libraries are reused in several applications, probably the best solution would be to create JBoss modules.
For example, OJDBC library is used in several projects I'm developing. So, I added a new module to JBoss 7: https://community.jboss.org/wiki/CreateAModuleForOracleDatasourceInJBoss711Final (it's just an example).
But you said, that you tried creating modules, but with no luck. What was the problem? Did you get some errors?
EDIT
Answer updated in connection with updated question.
So, if I understood correctly, we can divide your libraries into two categories:
First category is "standard libraries": Spring, Hibernate, Log4j etc. So, these libraries might be added as modules into JBoss AS and reused in every WAR (scope=provided in Maven's dependency).
Any other non-standard libraries (i.e. written by yourself) might be added as modules as well. If these libraries require some other dependencies - these dependencies must be listed in module's XML file, as described in: https://docs.jboss.org/author/display/MODULES/Module+descriptors
Hope this helps at least a bit :)

Categories

Resources