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

  • Sivakumar Kailasam

    Insightful. Great report. Wish you guys would make more such posts.

  • arhan

    Thanks! We will come up with more posts for sure! You might want to check out the Scala adoption guide and Continuous Delivery report released in the same style

  • A fantastic read; well written and easy to understand.
    Classloader problems are like a baptism of fire for Java programmers, it’s nice to have someone explain their fickle nature in a well-presented manner.

  • sandeepbh

    Some developers get confused with ClassNotFoundException which is different from NoClassDefFoundError. This article can be used as reference for the difference between the two. some readers who are new to class loader stuff may want to see:
    various types class loaders

  • mnkartik

    Awesome article about classloaders.

  • george mournos

    I dont want to argue about details but maybe it is better to find the code location from the ProtectionDomain and not by casting to URLClassloader. The code (except from the basic java classes which are loaded by the primordial classloader) will always be loaded in a protection domain, containing the certificates and the code locations. But the classloader might not extend URLClassloader, although this is the common case…

  • mns

    Really helpful information in the pdf.Thanks for explaining it very well .
    I was trying out the egample inthe section “Down and Dirty”.I downloaded the source code and tried executing the same and got the expected result as mentioned in your pdf. i.e

    “Version 1 =3”

    “Version 1=0”

    and when i updated Counter.message() to “version 2” , againi got the desired result as mentioned. i.e

    “Version1=4”

    “Version 2=0″….etc

    But my question is when i tried to debug the code in eclipse. For some strange reason, if i update the Counter.message() to “version 3” while debugging, i got below weird result

    “version3=5”
    “Version3=0”

    but i was expecting
    “version1=5”
    “Version3=0”.

    To summarize, i see that behaviour is different when i debug the code(instead of runningit directly) in eclipse. Why is it like that?

  • Hasnan

    Hi Sir

    I write a class named test class. When i compile. I get test.class file
    Who is responsible to load this test.class file.