ClassNotFoundException: how to find dependency conflict in Java

2.5k views Asked by At

In a test WebSocket application using Atmosphere servlet I'm getting the following exception:

SEVERE: Servlet.service() for servlet AtmosphereServlet threw exception
java.lang.ClassNotFoundException: javax.servlet.AsyncContext
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1645)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1491)
    at org.atmosphere.cpr.AtmosphereServlet.doPost(AtmosphereServlet.java:191)
    at org.atmosphere.cpr.AtmosphereServlet.doGet(AtmosphereServlet.java:177)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)

From the below posts I understand that this might be caused by a Servlet container version older than Servlet 3.0:

ClassNotFoundException: javax.servlet.AsyncContext in Jetty hello world

ClassNotFoundException: javax.servlet.AsyncContext in Jetty hello world in eclipse

Grails project - Servlet call - ClassNotFoundException: javax.servlet.AsyncContext

However the application is running on Tomcat7, and the following dependency is added in pom.xml:

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
</dependency>

I've checked all other dependensies in the project and was not able to find anything else related to Servlet. Still I'm getting the exception.

Questions: How to find a jar file which is actually used by the application? How to find the dependency which is causing the usage of an old version?

2

There are 2 answers

0
Alexey On BEST ANSWER

I was finally able to solve the dependency conflict by doing the following.

To find the jar file which is used by application I used the below simple code:

public void listJarFilesAndClassVersions() {    
    Class classToCheck = javax.servlet.ServletRequestWrapper.class;
    URL location = classToCheck.getResource('/'
        + classToCheck.getName().replace('.', '/') + ".class");

    System.out.println(location.toString());

    for(Package p : Package.getPackages()) {
        if (p.getName().startsWith("javax.servlet")) {
            System.out.println("Class: " + p.getName()
                + ", version: " + p.getSpecificationVersion());
        }
    }
}

The class javax.servlet.ServletRequestWrapper was chosen because it does exist in the old Servlet 2.5.

The execution of the above script gives me the following:

jar:file:/C:/Users/[username]/.m2/repository/org/apache/tomcat/servlet-api/6.0.29/servlet-api-6.0.29.jar!/javax/servlet/ServletRequestWrapper.class
Class: javax.servlet.jsp, version: 2.1
Class: javax.servlet, version: 2.5
Class: javax.servlet.http, version: null

So, first, it confirms that the Servlet of version 2.5 is used, and second, the "bad" jar is located in the maven repository under the tomcat directory.

After a short reasearch I was finally able to find the root cause of that: when deploying and running the maven application I need to specify the concrete version of tomcat, otherwise maven uses the libraries from tomcat of version 6. So the fix for me was to change

mvn -Dmaven.tomcat.port=8080 tomcat:run-war

to

mvn -Dmaven.tomcat.port=8080 tomcat7:run-war

Now if I execute the above script it gives the following result:

jar:file:/C:/Users/[username]/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/7.0.47/tomcat-embed-core-7.0.47.jar!/javax/servlet/ServletRequestWrapper.class
Class: javax.servlet.jsp, version: 2.2
Class: javax.servlet, version: 7.0
Class: javax.servlet.http, version: 7.0
Class: javax.servlet.annotation, version: 7.0
Class: javax.servlet.descriptor, version: 7.0

Hope it helps others who is running into the same issue.

7
Jenson On

There're two ways in which I usually find a conflict.

  1. If you are using Eclipse, open the pom.xml in IDE, and switch to "Dependency Hierarchy" tab. In the left panel is the dependency tree, and in the right panel are the dependencies actually getting used. Right after each dependency it's its scope (eg. compile / test / runtime / omitted for conflict)

enter image description here

  1. In terminal run the command. mvn dependency:tree It will print out the dependency like in Eclipse.