views:

141

answers:

6

Generally speaking one set of code (the client code) links against another (the API code). Java linking is typically verified between .java & .class at compilation time or between .class & .class at runtime. However in the latter case verification is as and when bad references are encountered (i.e. it is lazy).

Is there a way to force verification of all links between the client code and the API code at once with compiled code? The purpose would be to verify that the client code will work with the given version of the API - even though it has been compiled against another.

(Of course one way would be to decompile and recompile against the API, but is there a more direct method?)

A: 

Perhaps you can run junit tests against the API specified by a jar in the ClassPath, then just switch out the jar for different versions of the api and run tests again. You'd be able to automate this easily.

Gary
A: 

It is possible to analyse code from java classes by reflexivity. See the package java.reflect. Some analyse tools use this functionality to get information about the compiled code. The best example is probably FindBugs.

This API as limits, and I don't think you can do what you want with it. If a dependency is called in a method, the reflect API can find the method, its parameters, but can't give you the dependent functions used in that method.

So I think such an analyse tool doesn't exist in Java.

A solution is to provide once the code with all its dependencies. In development, lifecycle management tools like Maven can help you, managing the dependencies of your project.

Benoit Courtine
+1  A: 

Force verification of links is difficult, due to the nature of the language and the implementation of the JVM.

I believe the rationale for this question is to prevent linkage errors at runtime, and the intention to do so is very much valid. However, when one looks at the cause of linkage errors, from the point of view of the JVM, then it is more or less difficult to peform verification forcefully without any performance impact.

Linkage errors are thrown at runtime, when the JVM executes bytecode corresponding to the method invocation instructions. It is usually at this point in that the deferred final pass of the JVM's bytecode verifier is kicked in, which could result in linkage errors. Needless to say, this is expensive from the point of view of performance.

(It is my assumption that) Most projects (including commercial ones) therefore avoid forced verification, and instead rely on build and dependency management systems to avoid the pain. More commentary in this SO question; the answer of choosing OSGi framework might help.

Vineet Reynolds
Performance is not necessarily a major issue as the result can be remembered. Also in some situations the amount of code will not necessarily be huge.
mike g
Alright, fine. You might then want to use a bytecode engineering library like ASM (refer to the use of CheckClassAdapter) or BCEL (which has the JustIce verifier in it) to perform verification if they're sufficient, or extend them if needed. I'm not sure if Javassist has any similar feature.
Vineet Reynolds
A: 

For what you need, you can use a tool like JarJarDiff[1] and get the differences between the new and the old version of the API jars. Then the task is transformed to verifying that you don't use any of the incompatible API's. Though not automatic, this approach draws your attention to the changes and not only to the binary compatibility.

If you just want to check binary compatibility, the easiest is to recompile the project against the new JAR. A bit more difficult (and much more brittle) is to scan the classpath and invoke each method of each class, looking for linkage exceptions. Note that this naive approach will not test all possible paths and offers little benefit.

[1] http://depfind.sourceforge.net/tasks/jarjardiff.html

ddimitrov
A: 

Taking the idea from this question and Alfresco Wiki, you may try -Xcomp to start a test JVM and precompile the classes to check if there's a link error.

antispam
A: 

I would recommend using a bytecode tool such as asm to "visit" the code, and have your visitor override visitMethodInsn. Then use reflection on the owner class looking for a method with the indicated name and signature. You'll also need to look at the opcode of the invocation instruction - it will be one of invokevirtual, invokeinterface, invokespecial and invokestatic. Note to pay attention to the distinction between invokevirtual and invokeinterface; a call to invokevirtual will not successfully invoke an interface method, and a call to invokeinterface will not successfully invoke a method which is not defined on an interface.

Ian Robertson