Thursday, October 17, 2013

Debugging a java.lang.NoClassDefFoundError


java.lang.NoClassDefFoundError is one of the most common problem while developing Java Enterprise applications. Sometimes debugging a NoClassDefFoundError can be very tedious as you might have to look for missing class dependencies in jar files and may even have to decompile sometimes.

A NoClassDefFoundError is thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found. The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found. In simple words, the class was available at compile time but missing during runtime.

Purpose of this article:
To discuss how to debug multiple scenarios of NoClassDefFoundError. And find possible resolution for each scenario.



1.  NoClassDefFoundError Caused By ClassNotFoundException

1.1 Jar file(s) missing from classpath


This is the most comman case of NoClassDefFoundError. Take a close look at the stacktrace and figure out which class is giving a ClassNotFoundException. Now search for the jar which contains that class. If you are using eclipse you can search using the class name in pattern *.jar. Or you can go online and check which all jars contain that class.

For example refer the stacktrace below:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
    at org.springframework.context.support.AbstractApplicationContext.<init>(AbstractApplicationContext.java:119)
    at org.springframework.context.support.AbstractXmlApplicationContext.<init>(AbstractXmlApplicationContext.java:55)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:77)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:65)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:56)
    at com.client.StoryReader.main(StoryReader.java:15)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClassInternal(Unknown Source)
    ... 6 more

The above shows that the class org.apache.commons.logging.LogFactory is missing in classpath. When you search for it, you will find that commons-logging.jar. Now check if this jar is present in classpath. If yes, verify the jar name and path,  if not adding it would resolve the error.

1.2 Some dependent jar is missing which not allowing the class constructor to initialize the class object.

There might be a case where few classes in a jar file are referring to other classes in another jar. For example, the CallerClass in Caller.jar is shown below. It creates an instance of ReferenceClass which is present in another jar - Reference.jar


public class CallerClass {
        public static void main(String[] args) {                             
               try {                 
                       // Create a new instance of Class ReferenceClass
                         
                       ReferenceClass referenceClass = new ReferenceClass();
                      
               } catch (Throwable t) {
                       t.printStackTrace();
               }             
        }
}


Now if Reference.jar is not present in the classpath, the dependent class(CallerClass) in Caller.jar won’t be initialized as it will not find ReferenceClass during runtime.

The resolution for this case requires similar debugging. Take a close look at the stacktrace and figure out which class is giving a ClassNotFoundException. Now search for the jar which contains that class. If you are using eclipse you can search using the class name in pattern *.jar. Or you can go online and check which all jars contain that class. Now check if this jar is present in classpath. If yes, verify the jar name and path, if not adding it would resolve the error.

1.3 Parent and Child classloaders loading dependent jars separately



If the above two don’t work then you are in the most complex case of NoClassDefFoundError with ClassNotFoundException. Suppose Caller.jar is in the classpath and Reference.jar is in your Web-Archive (ear) file and classes in Caller.jar refer to class in Reference.jar. While loading classes in Caller.jar the system classloader attempts to use a class found in Web archive and we get a ClassNotFoundException/NoClassDefFoundError. The reason is that the classes in the parent classloader cannot refer directly to classes in child classloaders.

This problem is resolved by moving the depended classes to the system classpath, e.g. moving Reference.jar to classpath in our case. The vice-versa is not usually recommended but if the architecture permits, moving the Caller.jar to web archive can also help.


2. NoClassDefFoundError caused by an ExceptionInInitializerError



When you see NoClassDefFoundError with an ExceptionInInitializerError in stacktrace then a dependent class initialization has failed. NoClassDefFoundError is just a symptom. The actual issue is ExceptionInInitializerError which we need to find and fix.

Consider the scenario explained in 1.2 above. Suppose there is a static initializer in a ReferenceClass in Reference.jar and it failed. There would be an ExceptionInInitializerError for this class. Below, I am manually throwing an exception to explain this:

public class ReferenceClass {       
               
        // Static initializer block executed at Class loading time
        static {
              
               // Static block code execution...
              throw new Exception("Throwing error manually");
        }
}


Now if another CallerClass in Caller.jar is using an object of ReferenceClass it would fail with a NoClassDefFoundError.


public class CallerClass {
        public static void main(String[] args) {                             
               try {                 
                       // Create a new instance of Class ReferenceClass
                       ReferenceClass referenceClass = new ReferenceClass();
                      
               } catch (Throwable t) {
                       t.printStackTrace();
               }             
        }
}


It is tricky to find resolution of such cases. You need to look at the stacktrace and find out where exactly ExceptionInInitializerError is thrown. If it’s your code packed into the jar, you can change, recompile and pack again. If you see this error in any class of library you are using then you need to check what condition is causing the ExceptionInInitializerError.


3. Fixed NoClassDefFoundError but now getting NoSuchMethodError?



You are probably using a different version of jar in your classpath then what is used during compilation. It usually occurs when you start using a new version of a jar then what was used earlier.
For example, suppose the initial version of your Reference.jar has a ReferenceClass with method updateContact(String address) as shown below.


public class ReferenceClass {       
             public void updateContact(String address){
                       // some logic to update contact
             }  
}


Now you changed the method to updateContact(String address, String email) and repacked it into Reference2.jar  


public class ReferenceClass {       
             public void updateContact(String address, String email){
                       // some logic to update contact & email
             }  
}


The CallerClass is calling the method updateContact as below:


public class CallerClass {
        public static void main(String[] args) {                             
               try {                 
                       // Create a new instance of Class ReferenceClass
                       ReferenceClass referenceClass = new ReferenceClass();
                       // Call updateContact
                       referenceClass.updateContact(“Somewhere in World”,”gv@text.com”);
                      
               } catch (Throwable t) {
                       t.printStackTrace();
               }             
        }
}



While compiling, your code refers to the new method but your classpath is still having Reference.jar. The system tries to find updateContact(String, String) in Reference.jar but is unable to find it. Hence NoSuchMethodError would be thrown.

For resolution of this case, take a close look at the stacktrace and figure out which class method is giving a NoSuchMethodError. Now search for the jar which contains that class. If you are using eclipse you can search using the class name in pattern *.jar. Or you can go online and check which all jars contain that class. Now check if the correct version of this jar is present in classpath. If not, remove the old jar from class path and add the newer version.

6 comments:

  1. Thanks for the post. Its really helpful. =)

    ReplyDelete
  2. Hi,

    I'm facing a NoSuchMethodError in my plugin project. I got this error :

    java.lang.NoSuchMethodError: org.codehaus.jackson.map.ObjectMapper.getTypeFactory()

    The two jars' are : org.codehauas.jackson.mapper 1.0.6 and org.codehauas.jackson.core 1.0.6

    The version are the same, but I still have the error.

    Any idea ?

    Thanks in advance

    ReplyDelete
    Replies
    1. @Ismail : I see on stackoverflow that you were able to resolve it by adding plugin dependencies. I hope this article was helpful.

      Delete
  3. avr. 17, 2014 10:08:34 PM org.apache.catalina.core.StandardContext listenerStart
    Grave: Exception lors de l'envoi de l'évènement contexte initialisé (context initialized) à l'instance de classe d'écoute (listener) org.quartz.ee.servlet.QuartzInitializerListener
    java.lang.NoClassDefFoundError: javax/transaction/UserTransaction
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
    at java.lang.Class.privateGetPublicMethods(Unknown Source)
    at java.lang.Class.getMethods(Unknown Source)
    at java.beans.Introspector.getPublicDeclaredMethods(Unknown Source)
    at java.beans.Introspector.getTargetMethodInfo(Unknown Source)
    at java.beans.Introspector.getBeanInfo(Unknown Source)
    at java.beans.Introspector.getBeanInfo(Unknown Source)
    at org.quartz.impl.StdSchedulerFactory.setBeanProps(StdSchedulerFactory.java:1393)
    at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:1057)
    at org.quartz.impl.StdSchedulerFactory.getScheduler(StdSchedulerFactory.java:1519)
    at org.quartz.ee.servlet.QuartzInitializerListener.contextInitialized(QuartzInitializerListener.java:171)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4701)
    at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5204)
    at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5199)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    Caused by: java.lang.ClassNotFoundException: javax.transaction.UserTransaction
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1676)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1521)
    ... 19 more

    ReplyDelete
  4. Thank you. The explanation helps.

    ReplyDelete