Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.

Do You Really Get Classloaders?

NoSuchMethodError

In another scenario with the same example we might encounter the following exception:

java.lang.NoSuchMethodError: Util.sayHello()Ljava/lang/String;
	HelloServlet:doGet(HelloServlet.java:17)
	javax.servlet.http.HttpServlet.service(HttpServlet.
	java:617)
	javax.servlet.http.HttpServlet.service(HttpServlet.			java:717)

NoSuchMethodError represents a different problem. In this case, the class that we’re referring to exists, but an incorrect version of it is loaded, hence the required method is not found. To resolve the issue, we must first of all understand where the class was loaded from. The easiest way to do so is to add ‘-verbose:class’ command line parameter to the JVM, but if you can change the code quickly (e.g. with JRebel) then you can use the fact that getResource searches the same classpath as loadClass.

public class HelloServlet extends HttpServlet {
   protected void doGet(HttpServletRequest request, 
                                    HttpServletResponse response) 
                                    throws ServletException, IOException {
       PrintWriter out = response.getWriter();
    out.print(HelloServlet.class.getClassLoader().getResource(
           Util.class.getName.replace(‘.’, ‘/’) + “.class”));  

}

Assume, the result of the request execution for the example above is as follows: file:/Users/myuser/eclipse/workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/demo/WEB-INF/lib/demo-lib.jar!/Util.class

Now we need to verify our assumption about the incorrect version of the class. We can use javap utility to decompile the class – then we can see if the required method actually exists or not.

$ javap -private Util
Compiled from “Util.java”
public class Util extends java.lang.Object {
   public Util();
}

As you can see, there isn’t sayHello method in the decompiled version of the Util class. Probably, we had the initial version of the Util class packaged in the demo-lib.jar but we didn’t rebuild the package after adding the new sayHello method.

Variants of the wrong class problem

What if you no longer had to redeploy your Java code to see changes? The choice is yours. In just a few clicks you can Say Goodbye to Java Redeploys forever.

NoClassDefFoundError and NoSuchMethodError are very typical when dealing with Java EE applications and it is the required skill for Java developers to be able to understand the nature of those errors in order to effectively solve the problems.

There are plenty of variants of these problems: AbstractMethodError, ClassCastException, IllegalAccessError – basically all of them encounter when we think that application uses one version of a class but it actually uses some other version, or the class is loaded in some different way than is required.

ClassCastException

Let’s demonstrate a case for ClassCastException. We’ll modify the initial example to use a factory in order to provide the implementation of a class that provides the greeting message. Sounds contrived but this is quite a common pattern.

public class HelloServlet extends HttpServlet {
   protected void doGet(HttpServletRequest request, 
                                    HttpServletResponse response) 
                                    throws ServletException, IOException {
       PrintWriter out = response.getWriter();
    out.print(((Util)Factory.getUtil()).sayHello());
}

class Factory {
     public static Object getUtil() {
          return new Util();
     }
}

The possible result of the request is the unexpected ClassCastException:

java.lang.ClassCastException: Util cannot be cast to Util
	HelloServlet:doGet(HelloServlet.java:18)
	javax.servlet.http.HttpServlet.service(HttpServlet.			java:617)
	javax.servlet.http.HttpServlet.service(HttpServlet.			java:717)

It means that HelloServlet and Factory classes operate in different context. We have to figure out how these classes were loaded. Let’s use -verbose:class and figure out how the Util class was loaded in regards to HelloServlet and Factory classes.

[Loaded Util from file:/Users/ekabanov/Applications/ apache-tomcat-6.0.20/lib/cl-shared-jar.jar]
[Loaded Util from file:/Users/ekabanov/Documents/workspace-javazone/.metadata/.plugins/org.eclipse.wst. server.core/tmp0/wtpwebapps/cl-demo/WEB-INF/lib/cl-demo- jar.jar]

So the Util class is loaded from two different locations by different classloaders. One is in the web application classloader and the other in the application container classloader.

Util class web application classloader

But why are they incompatible? Turns out that originally every class in Java was identified uniquely by its fully qualified name. But in 1997 a paper was published that exposed an expansive security issue cause by this–namely, it was possible for a sandboxed application (i.e. applet) to define any class including java.lang.String and inject its own code outside the sandbox.

The solution was to identify the class by a combination of the fully qualified name and the classloader! It means that Util class loaded from classloader A and Util class loaded from classloader B are different classes as far as the JVM is concerned and one cannot be cast to the other!

The root of this problem is the reversed behavior of the web classloader. If the web classloader would behave in the same way as the other classloaders, then the Util class would have been loaded once from the application container classloader and no ClassCastException would be thrown.


DOWNLOAD THE PDF