In an previous post, I showed how I got Railo to deploy under JBoss. Railo is an open source CFML engine that can be deployed as a web application under any servlet container.
Railo compiles CFML down to Java bytecode just like Tomcat does with JSPs. So it is possible to get a better view of what is happening inside of your code using JBoss’ AOP. Aspect-Oriented Programming (AOP), is a programming paradigm where common “cross cutting” concerns are pulled out of the primary code and placed into “aspects.”
The common example is removing all of your Log4J statements sprinkled though out your code and having a separate XML file that describes what activities you want to log. Conversely, if you wanted to go back and add a logging statement to a peice of code, you wouldn’t have to. You would describe what aspect of your code you wanted to log in the XML and be done with it.
There are a lot of Java AOP solutions, but they fall into compile time and runtime. JBoss can do both. Compile time solutions add aspects into the bytecode when you compile it. It runs without any AOP overhead, but has to be recompiled to change the behavior.
JBoss’ runtime AOP uses dynamic code weaving. As the classloader loads classes, it check AOP descriptions and injects new bytecode into classes as it loads them. Since we don’t have the Java source for the CFML based classes, we cannot use AOP at compile time. We will have to use dynamic code weaving.
1) Setup Deployment Directories for AOP
Previously I had exploded the Railo.war into its own deployment directory:
/server/default/deploy/railo_3.0.1.war
In the JBoss deploy directory (/server/default/deploy/), I want to create a new parent for railo and move the orginal WAR directory into it.
mkdir railo
mv railo_3.0.1.war railo
I found it was important that the parent and the children directory did not have the same name. Orginally, I had tried both named railo_3.0.1 and JBoss AOP had errors with it. So I settled for the parent to be a generic “railo,” which turned out better anyways.
2) Create a BasicLogger
package test.aop;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;import org.jboss.aop.metadata.SimpleMetaData;
import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.joinpoint.MethodInvocation;public class BasicLogger implements org.jboss.aop.advice.Interceptor {
public Object invoke(Invocation invocation) throws Throwable
{
//Do something before
try
{
System.out.println(“===START BASIC LOGGING===”);
SimpleMetaData smd = invocation.getMetaData();if (invocation instanceof org.jboss.aop.joinpoint.MethodInvocation) {
MethodInvocation mi = (MethodInvocation) invocation;System.out.println(” METHOD INVOKED…”);
System.out.println(” NAME: ” + mi.getMethod().getName());Object[] args = mi.getArguments();
for (int i=0; i < args.length; i++) {
System.out.println(” ARG[” + i + “]: ” + args[i].getClass().getName() + “=” + args[i].toString());
}}
Iterator tagIt = smd.tags().iterator();
Object tag = null;
while(tagIt.hasNext()) {
tag = tagIt.next();System.out.println(” INVOCATION META DATA[” + tag.toString() + “]” + smd.tag(tag.toString()));
}
System.out.println(“===END BASIC LOGGING===”);
return invocation.invokeNext();}
finally
{
//Do something after
}
}public String getName() {
return ” test.aop.BasicLogging”;
}}
3) Setup AOP Package
I found it easiest to just deploy the AOP package parallel to the WAR. I wanted to make another “exploded” package. JBoss can deploy AOP packaged just like WARs, so it expects either a jar compressed package or a directory that mimics one.
3a) Make the AOP directories
So while in the /railo directory:
mkdir railo_3.0.1.aop
cd railo_3.0.1.aop
mkdir META-INF
3b) Create a Manifest
Not sure if that part is really necessary, but I wanted to make it as “jar” like as possible. In the railo/railo_3.0.1.aop/META-INF directory, I create a basic MANIFEST.MF by copying one from a blank jar:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: 1.5.0_16-133 (Apple Inc.)
3c) Create the jboss-aop-not.xml
Next back up to the main AOP package directory (deploy/railo/railo_3.0.1.aop).
Now we have to create a file to describe to JBoss how we want to weave our BasicLogger into railo. A complete discussion about how the AOP configuration works is well beyond this posting. For more details, check their documentation. I just want to get you a working example.
<aop xmlns=”urn:jboss:aop-beans:1.0″>
<!–
THIS SETS UP OUR BASIC LOGGER AS
AN INTERCEPTOR. WE CAN NOW WEAVE
THIS CODE INTO OTHER OBJECTS AT
LOAD TIME.
—>
<interceptor class=”test.aop.BasicLogger” scope=”PER_VM”/><!–
THIS TELLS JBOSS AOP TO PREPARE THE MAIN RAILO CFML
ENGINE OBJECT FOR CODE WEAVING. YOU CAN PREPARE
ANY CLASS LIKE THIS
–>
<prepare expr=”all(railo.loader.engine.CFMLEngine)” /><!–
YOU HAVE A LOT OF CONTROL OVER WHERE TO WEAVE IN THE INTERCEPTOR. YOU
CAN WRAP SPECIFIC METHODS, FIELDS, CONSTRUCTORS, ETC.
HERE I AM ASKING TO REPORT EACH TIME AN INSTANCE OF THE
RAILO CFMLEngine CALLS THE serviceCFML METHOD
—>
<bind pointcut=”execution(void $instanceof{railo.loader.engine.CFMLEngine}->serviceCFML())” >
<interceptor-ref name=”com.userplane.aop.logging.BasicLogger” />
</bind></aop>
4) Active JBoss Dynamic AOP
Dynamic code weaving is not activated by default. You have to enable it. To do this open:
/server/default/conf/bootstrap/aop.xml
Look for the property:
<property name=”enableLoadtimeWeaving”>false</property>
And set it to true.
Now when you start JBoss you should see a lot more in server.xml and when you first hit your Railo servlet you will see all the binding in action.
Gotchas
JBoss has a couple of gotchas. First was that “urn:jboss:aop-beans:1.0” needed in the XML. It isn’t in a lot of their documented examples. It is a new requirement for JBoss 5
Second was the whole point of step #4. Most of the AOP documentation is still based on JBoss 4. Dynamic AOP is not enabled by default and the activation sequence is different in JBoss 5.
in aop.xml values of
interceptor class and interceptor-ref name are not equal…is right??
Thanks