2008/04/20

Watch out for Lazy Initialization when you schedule a job

http://www.jroller.com/habuma/entry/a_funny_thing_happened_while


Spring should probably initialize those Job Scheduler more actively. Lazily initialize things like that sounds not a good idea.

Spring Security by Craig Walls

Craig Walls presented the Spring Security Session at the latest NFJS Java Conference in Seattle.

What ACEGI Offers?

- Declarative Security, keeps security details out of your code
- Authentication and Authorization, against virtually any user store
- Support for anonymous sessions, concurrent sessions, remember-me, channel enforcement, and much more
- Spring-based, but can be used for non-Spring web framework

ACEGI's moving parts:

- Security Interceptors, aspects for methods, filters for servlets
- Managers, Authentication, Access Decision, Run-As, After-Invocation
- Authentication Providers
- Access Voters

Security Intercepter - First line of defense
Authentication Manager - Verifies user identity 
Access Decision Manager - Determines if the authenticated user has authority to access the secured resource, by aggregating the result from the Voters
Run-As Manager - Temporarily replaces user's Authentication object for the duration of the current secure invocation
After Invocation Manager - Reviews the object returned from a secured invocation, allows for 'after-the-fact' security

The problem of ACEGI

Every time you use Acegi... A fairy dies... It's a great framework but is very hard to use.

- Lots of moving parts
- Lots of options
- Everything is a <bean> with various options injected with <property>
- Requires lots of XML

Spring Security 2.0

- Released last week (Apr.15th)
- All the Same goodness with some new stuff with much less XML
- Provides a new security configuration namespace for Spring that hides <bean> <property>
- Provides auto-configuration

Method Security

- Intercepting method using Spring AOP
- Or, Annotation Driven

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.


I guess nobody likes it if his code's CRAP level is high. In fact I found this to be really useful when we talk about code quality. 

Download from: http://www.crap4j.org/. Ideally we'd like the build script to output a CRAP report automatically, just like it'll run the test cases automatically.

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?

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.
  1. 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.
  2. 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.
  3. 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.
Solutions.

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 here
For 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.