2008/04/09

Will it Compile?

This small sample project uses two libraries, apache-log4j-1.2.15 from www.apache.org and jpos-1.6.0-r2465 from www.jpos.org. Both are latest version as of the date I write this blog - freshly downloaded. The jpos library comes with no pre-packaged distribution jar file, thus we used ant to build the jar file from it's source files and the build.xml.

Besides that, we have a source file of our own, A.java:

import org.apache.log4j.Logger;

/**
* A.java
*/
public class A {
static Logger logger = Logger.getLogger(A.class);

public static void main(String[] args){
logger.trace("blah blah");
}
}
It does nothing except log a message at trace level. The question here is, will this compile?

The answer? Yes and No. Depends how you compile it.

To demonstrate the problem, we'll just try to use the command line to compile it, instead of using apache-ant or any IDE. It helps us to identify the root of the problem. (Assume lib is where you put your libraries, also we have two version of j2sdk installed, 1.4.2_16 under c:\j2sdk1.4.2_16, 1.5.0_14 under c:\jdk1.5.0_14.)
c:\j2sdk1.4.2_16\bin\javac -classpath lib\apache-log4j-1.2.15\log4j-1.2.15.jar;lib\jpos-1.6.0-r2465\build\jpos.jar src\*.java

Result: Compile success.

c:\jdk1.5.0_14\bin\javac -classpath lib\apache-log4j-1.2.15\log4j-1.2.15.jar;lib\jpos-1.6.0-r2465\build\jpos.jar src\*.java

Result: Compile Success.
Now swap the classpath entries and try it again:
c:\j2sdk1.4.2_16\bin\javac -classpath lib\jpos-1.6.0-r2465\build\jpos.jar;lib\apache-log4j-1.2.15\log4j-1.2.15.jar src\*.java

Result: Compile success.

c:\jdk1.5.0_14\bin\javac -classpath lib\jpos-1.6.0-r2465\build\jpos.jar;lib\apache-log4j-1.2.15\log4j-1.2.15.jar src\*.java

Result:
src\A.java:12: cannot find symbol
symbol : method trace(java.lang.String)
location: class org.apache.log4j.Logger
logger.trace("blah blah");
^
1 error
Surprise, huh? Try it yourself.

Everything comes with a reason. Since jdk1.5, both java and javac recurse the manifest classpath of a jar on the java classpath. Looking into jpos.jar, we found that its manifest file says to use log4j-1.2.8.jar and the trace method wasn't supported till log4j-1.2.12 or above.
jpos.jar manifest file entry:

Class-Path: lib/bsh-2.0b4.jar lib/commons-cli.jar lib/jdbm-1.0.jar lib
/jdom-1.0.jar lib/log4j-1.2.8.jar lib/mx4j-impl.jar lib/mx4j-jmx.jar
lib/mx4j-tools.jar lib/classes

That's the reason why the compilation error happened - javac was instructed to used log4j-1.2.8.jar come with the jpos distribution, instead of the the 1.2.15 that we want it to use.

Conclusion? Well, I don't know what to say here, but this looks something similiar to the windows DLL hell problem. And it was called DLL 'Hell' for a reason.

Further reading: How classes are found?

Well well... why another J2EE blog? I benefited from other people's technical blogs, and guess what, it's a good idea to contribute some of my works too. Hope it's helpful and useful, to all of your folks.