http://www.jroller.com/habuma/entry/a_funny_thing_happened_while
2008/04/20
Watch out for Lazy Initialization when you schedule a job
Spring Security by Craig Walls
2008/04/19
How CRAP is your java source code?
Crap4j, a Java implementation of the CRAP (Change Risk Analysis and Predictions) software metric - a mildly offensive metric name to help protect you from truly offensive code.
2008/04/15
Spring, Hibernate, and GWT Tutorial
GWT is hot at client side but we still needs Spring/Hibernate to support it on the server side. Here is how. Courtesy of eggsylife blog.
Part 1: http://eggsylife.blogspot.com/2007/10/well-this-tutorial-aims-at-helping.html
Part 2: http://eggsylife.blogspot.com/2007/11/hibernate-spring-google-web-toolkit.html
Part 3: http://eggsylife.blogspot.com/2008/02/hibernate-spring-google-web-toolkit.html
And this GWT crash course is helpful too: http://google.wikia.com/wiki/Jump_Start_Your_AJAX_Development_with_the_Google_Web_Toolkit
A lot more here:
http://www.j2eeblogs.blogspot.com/2008/03/gwt-tutorials.html
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;It does nothing except log a message at trace level. The question here is, will this compile?
/**
* A.java
*/
public class A {
static Logger logger = Logger.getLogger(A.class);
public static void main(String[] args){
logger.trace("blah blah");
}
}
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\*.javaNow swap the classpath entries and try it again:
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.
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\*.javaSurprise, huh? Try it yourself.
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
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?
2008/04/03
Microsoft favours Enterprise Java Platform
Microsoft is using Tomcat and JSP rather than IIS+.Net
Today I happened to catch an error screen from MSN china.
What an ironic screen of Microsoft WEB toolkits marketing policy!
Someone may say it's not true and it was a Fools day joke! O.K, see the further picture that what's the link of http://msn.china.ynet.com:
And more, you can right click any of the url on its page to see the detail of the link properties. What, something like:
http://msn.china.ynet.com/view.jsp?oid=32306246
Although you can say it's just a partner, who cares! Totally wrong, it's Microsoft's Chinese MSN portal which is branded as MSN and Microsoft!
However, it may give Microsoft a chance to say "you see our demos of how unstable of a free software like Tomcat, JSP, Linux, MySql combination! We are considering migrate them to IIS2020+.NET2020+SqlServer2020!".
Wait, one more footnote "until we can make even of MSN in China!".
2008/04/02
TypeSafe Enum and equals()
At first it seems a No Brainer Problem
If you assign the same pre-initialized instance of the same class to two variables, do they always equal to each other? Note that this is the typical pre-jdk1.5 typesafe enum pattern.
public class ReagentType implements Serializable{
private static final String PROTEIN_TYPE = "PROTEIN";
// more types here
public ReagentType PROTEIN = new ReagentType(PROTEIN_TYPE);
// more pre-initialized instance here
private String type;
public String getType(){
return this.type;
}
private ReagentType(String type){
this.type = type;
}
public void checkEquals(ReagentType a, ReagentType b){
System.out.println(a.equals(b));
}
public static void main(String[] args){
ReagentType a = ReagentType.PROTEIN;
ReagentType b = ReagentType.PROTEIN;
checkEquals(a,b);
}
}
Sounds like a no brainer. The answer obviously should be yes. checkEquals() method will print out "true". Always. That is, in an ideal world.However...
In real world, assuming a & b are both assigned to ReagentType.PROTEIN, in many cases we will see checkEquals() method prints out "false". The standard java equals() compares object identity, which is nothing but the memory location of the object instance, which will most certainly fail in a real world deployment environment.
- Consider Distributed Computing. Either a or b has travelled the wire before checkEquals() method gets it, a.equals(b) is false. They resides in two different memory location.
- Even in a single server, after serializable/deserialization, it's guranteed that an object will have a different memory location. a equals() the serialize/deserialized copy of itself, is false.
- Consider an EJB invoking a method in another EJB, if they're packaged in different deployment JARs and both contains the ReagentType class, depends how the class loading mechanism in app-server works, there might exists many pre-initialized instance of the REAGENT object and they have different memory location.
Oh yeah, there's always solutions. For one, implement the equals() method properly.
public boolean equals(Object obj){
if (obj!=null && obj instanceof ReagentType){
return getType().equals( ((ReagentType)obj).getType());
} else {
return false;
}
}
// hashcode() methods omitted hereFor two, for those who're not big fan of the typesafe enum pattern, they can simply fall back to the good old set-of-static-primitive-values enumeration.A note is that while implementation of the RMI readResolve() method might seem to solve this problem, it actually will fail in scenario 3, since the container may decide not to perform serialize/deserialize if the EJBs resides in the same server. However the readResolve() solution should be safe in non-EJB situation where the class loading situation is simpler.
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.