Search code examples
javamaventomcatservletsjavax

ClassNotFoundException: how to find dependency conflict in Java


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?


Solution

  • 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.