Mukesh
Mukesh

Reputation: 182

NoSuchMethodError for diff version jars

There is a jar which has 2 versions in version 1 there are majorly 2 classes

Class A
Class B

Class A has a constructor as
A(X,B)

in version 2 there are majorly 2 classes and an interface

Class A
Interface C
Class B implements C

Class A has a constructor as
A(X,C)

In my util class I call the constructor of A

A a= new A(x,new B());

Above code is ant build using jar 1 which builds fine.

Now in production env the jar in classpath is of version 2. and I am getting error as

NoSuchMethodError A.<init>(x;B)

Not able to understand that in version 2 jar B is implemention of C so why this error? Please help me understand

Upvotes: 2

Views: 176

Answers (3)

Mukesh
Mukesh

Reputation: 182

When the code was complied with version1 jar then class file was generated with constructor having argument as class file (class B) and it didnot had the interface C. When the same jar was moved to production env, constructor was having interface C (type C) which was not matching with class B (type B) hence the error of nosuchmethoderror.When code was complied with version 2 jar the error got resolved.

Upvotes: 0

Gergely Bacso
Gergely Bacso

Reputation: 14651

It is due to the fact that you are changing the function signature of a constructor . If you check the bytecode of your Util class, you will see that it is using invokespecial, not invokevirtual opcode (constructors and private methods are called via invokespecial). invokespecial is really special in a sense that it does static binding. For a longer explanation I suggest reading this: article

Invokespecial differs from invokevirtual primarily in that invokespecial selects a method based on the type of the reference rather than the class of the object. In other words, it does static binding instead of dynamic binding. In each of the three situations where invokespecial is used, dynamic binding wouldn't yield the desired result.

By that version change you make after compilation, you are actually erasing the only constructor that your Util class can call, and you cannot rely on dynamic binding this time.

Upvotes: 2

gerrytan
gerrytan

Reputation: 41133

My bet is version 1 is still lurking somewhere in your production classpath alongside version 2 and it's the first one your classloader encounter.

In fact you should treat this as a blessing because otherwise you have a tiny silent bug which never cause havoc until some random time in the future when your classloader give you v1 (happens in my previous company and production goes into a grinding halt for a full week).

This is often difficult to debug because version 1 can be injected by another jar, war, by the container, transitively by maven, accidentally when packing the jar/war, osgi bundle, or worse: by a rogue classloader (good luck with this)

If it's taking you forever to solve this I'd rename A version 2 into A2 if I can so it has deterministically different name (yes this is an ugly solution).

Another common cause is JBoss not cleaning itself between multiple redeploys. So when you deploy app version 567, classpath still has junk from version 566. Typically this can be solved by doing a clean deploy (kill JBoss process, delete temp folder, start it again)

Upvotes: 1

Related Questions