2

We started with one jar with one class with one method like:

boolean foo( int bar ) { ... }

However, the result of this method was useless (in fact, always true) and clients that used this result for anything fails in an error. For this reason, method was changed to:

void foo( int bar ) { ... }

and all items recompiled. Thus, we can assume all users of this jar calls the method as:

foo(14);

none uses the form (and is out of scope for this question if there are someone):

boolean x = foo(14);

Assume none client, new or older, uses the boolean result.

The problem was when, in target systems, the new jar is loaded without upgrading the clients. Not updated clients fails with an exception "NoSuchMethod" when looking for the "foo" method with result "boolean". That is:

  • client has an statment like "foo(14);" without usage of the method result
  • client is compiled using the jar with boolean method.
  • client and library is loaded in target system
  • library is updated with new jar with void method
  • client crashes with "NoSuchMethod"

The origin of the problem seems to be that both, Java and C/C++ doesn't allows two methods that differs only in result, but only Java "name mangling" includes the result type in the name that class loader (linker in C/C++) is looking for.

The question is: it is possible to trick in any way the library jar or the class loader to skip "NoSuchMethod" exception in this scenario?

2 Answers 2

3

This behaviour is fully expected. This is because client's compiled classes contain constant pool, which includes symbolic reference to a method.

Let's suppose we have class FooClass with two methods:

public boolean foo1(int bar) {
    return true;
}

public void foo2(int bar) {
    // ...
}

And let's suppose we have another class Main where we invoke both methods:

    FooClass fc = new FooClass();
    fc.foo1(1);
    fc.foo2(1);

If we will disassemble Main.class we will see that method references differ not only in their names, but in returned type (notice (I)Z and (I)V):

     7: astore_1
     8: aload_1
     9: iconst_1
    10: invokevirtual #4                  // Method q42340444/FooClass.foo1:(I)Z
    13: pop
    14: aload_1
    15: iconst_1
    16: invokevirtual #5                  // Method q42340444/FooClass.foo2:(I)V

Where constant pool contains:

   #4 = Methodref          #2.#25         // q42340444/FooClass.foo1:(I)Z
   #5 = Methodref          #2.#26         // q42340444/FooClass.foo2:(I)V

In other words, it preserves original method's return type and looks in linked libraries for exactly same method, which causes NoSuchMethod exception in your case.

More specifically, compiled user classes are linked to FooClass.foo:(I)Z method, while your new library doesn't contain method with such description, but only FooClass.foo:(I)V.

Conclusion: You can not solve this problem without keeping old method signature (may be it is better annotate that method with @Deprecated annotation), or by recompiling client's code.

Sign up to request clarification or add additional context in comments.

10 Comments

Yes, it is as you describe, but your the suggestion to solve current real problem is ?
You can not solve it without keeping old method signature or recompiling client's code.
Unfortunately I can not "keep" older method and newer one, because Java doesn't allows two methods that differs only in result type.
Rename it to foo2(int bar) :) There is no other way
What about a custom class loader with @Override of "findClass"?
|
1

Just my thoughts.

You can try to modify a class's bytecode after actual compilation using special tools as a hotfix. In bytecode such methods can coexist.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.