Friday, November 11, 2011

Error: "java.lang.NoSuchMethodError"

Dear reader,
I am explaining a very good topic here on Java. You may be aware about this error 
"java.lang.NoSuchMethodError" which comes while developing java applications.

The reason to write this tiny topic in my blog is because we have faced a major issue in production 
because of this. We have deployed a Hotfix (a JAR) in production which has dependency on another jar. 
The Hotfix was taken from a QA/Testing env where the Method Signature was different than production 
(I have explained below) and since there were no compilation issues, we deployed the hotfix in 
production which caused problem after deployment at runtime.

We noticed this after 1 day and you know: Shit happens as again Re-deploying in production for 
same issue will cause a trust deficit between client and Developer organization and necessary 
Approvals for production RESTART makes things worse...

Here is the below simple explanation of code failure:
I am writing two SIMPLE java classes, one is having main method calling other's method.

----------------------------------
//Topup.java
public class Topup {
    public static void main(String[] args) {
        Nokia nk=new Nokia();   //See this class below
        System.out.println("Before");
        nk.reverse();
        System.out.println("After");
    }
}
-----------------------------------
//Nokia.java (NOTE: Note the return type of reverse() method in below class)
public class Nokia {
public void reverse(){  //void return type
        System.out.println("Nokia before, with Void return type.");
}
}
-----------------------------------
//Compile these two files, both are in the same directory
    javac Nokia.java
    
    javac Topup.java

//Now run the main file
    java Topup

//Output:
Before
Nokia before, with Void return type.
After
--------------------------------------------------

Now change the Nokia.java like this (NOTE: Note the return type of reverse() method in below class):
public class Nokia {
    public String reverse(){  //String return type
        String ret="Old reverse";
        System.out.println("Nokia before, with String return type");
        return ret;
    }
}
--------------------------------------------------
Now again compile only Nokia.java, so that Nokia.class gets replaced with new one.
Now run again Topup.java

[dmodi@nbodevapp1 Confusion]$ javac Nokia.java
[dmodi@nbodevapp1 Confusion]$ java Topup
Before
Exception in thread "Main Thread" java.lang.NoSuchMethodError: reverse
        at Topup.main(Topup.java:5)
[dmodi@nbodevapp1 Confusion]$

!!!!!!!!!!!!!!You see exception here, please note there is no compilation issues,if you open this code 
base in MyEclipse. As in java calling a method with any return type doesn't make any sense if you 
don't require the returned value.

!!!!!!!!!!!!!!It means next time onwards if you do any hotfix, deployment into production, 
make sure the Other supporting jars are not modified in TESTING env compare to Production, 
else you will face the same big issue, what we have faced.

----------------------------------------------------
HOW to Confirm that the Hotfix Jar (Particular class or classes) are compiled with Other Method signatures 
rather than expected:
For this, there is a command in JAVA: javap -verbose. Here is example:

In Previous case (Topup, compiled with void reverse() in Nokia.java)
============
[dmodi@nbodevapp1 Confusion]$ javap -verbose Topup
Compiled from "Topup.java"
public class Topup extends java.lang.Object
  SourceFile: "Topup.java"
  minor version: 0
  major version: 49
  Constant pool:
const #5 = String       #23;    //  Before
const #6 = Method       #24.#25;        //  java/io/PrintStream.println:(Ljava/lang/String;)V
const #7 = Method       #2.#26; //  Nokia.reverse:()V
const #8 = String       #27;    //  After
const #12 = Asciz       ()V;
const #26 = NameAndType #36:#12;//  reverse:()V
const #27 = Asciz       After;
   17:  invokevirtual   #7; //Method Nokia.reverse:()V
   23:  ldc     #8; //String After
   25:  invokevirtual   #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   28:  return

========================In Modified case (Topup, compiled with String reverse() in Nokia.java)
[dmodi@nbodevapp1 Confusion]$ javap -verbose Topup
Compiled from "Topup.java"
public class Topup extends java.lang.Object
  SourceFile: "Topup.java"
  minor version: 0
  major version: 49
const #5 = String       #23;    //  Before
const #6 = Method       #24.#25;        //  java/io/PrintStream.println:(Ljava/lang/String;)V
const #7 = Method       #2.#26; //  Nokia.reverse:()Ljava/lang/String;
const #8 = String       #27;    //  After
const #26 = NameAndType #36:#37;//  reverse:()Ljava/lang/String;
const #27 = Asciz       After;
const #36 = Asciz       reverse;
const #37 = Asciz       ()Ljava/lang/String;;
   11:  ldc     #5; //String Before
   17:  invokevirtual   #7; //Method Nokia.reverse:()Ljava/lang/String;
   24:  ldc     #8; //String After
   29:  return

============================================================
Please see the difference, you will notice JAVA says that Topup.java is compiled with what type of
method signatures (reverse()) in Nokia.java


========================================End=======================================