In this chapter, we explain how to instrument (or "aspectize") the POJOs via JBoss Aop. There are two steps needed by JBoss Aop: 1) POJO declaration, 2) instrumentation. But depends on the instrumentation mode that you are using, you may not need to pre-process your POJO at all. That is, if you use JDK5.0 (required) and load-time mode, then all you need to do is annotating your POJO (or declare it in a xml file). This makes your PojoCache programming nearly transparent.
For the first step, since we are using the dynamic Aop feature, a POJO is only required to be declared "prepare". Basically, there are two ways to do this: either via explicit xml or annotation.
As for the second step, either we can ask JBoss Aop to do load-time (through a special class loader, so-called load-time mode) or compile-time instrumentation (use of an aopc pre-compiler, so-called precompiled mode). Reader can read the JBoss Aop introduction chapter for more details.
To declare a POJO via XML configuration file, you will need a
META-INF/jboss-aop.xml
(or in the PojoCache case, it is
the equivalent pojocache-service.xml
file located under the class
path or listed in the jboss.aop.path
system
property. JBoss AOP framework will read this file during startup to make
necessary byte code manipulation for advice and introduction. Or you
can pre-compile it using a pre-compiler called
aopc
such that you won't need the XML file during load time. JBoss Aop
provides a so-called
pointcut
language where it
consists of a regular expression set to specify the interception
points (or
jointpoint
in aop parlance). The
jointpoint can be constructor, method call, or field. You will need to
declare any of your POJO to be "prepared" so that AOP framework knows
to start intercepting either method, field, or constructor invocations
using the dynamic Aop.
For PojoCache, we only allow all the fields (both read and write) to be intercepted. That is, we don't care for the method level interception since it is the state that we are interested in. So you should only need to change your POJO class name. For details of the pointcut language, please refer to JBoss Aop.
The standalone
JBoss Cache
distribution
package provides an example declaration for the tutorial classes,
namely,
Person
and
Address
.
Detailed class declaration for
Person
and
Address
are provided in the Appendix section. But here
is the snippet for
pojocache-aop.xml
:
<aop> <prepare expr="field(* $instanceof{@org.jboss.cache.pojo.annotation.Replicable}->*)" /> </aop>and then notice the annotation @Replicable used in the
Person
and
Address
POJOs. Also note that @Replicable is now inheritant. For example,
sub-class of Person
such as Student
will also
be aspectized by JBoss Aop as well. If you want to stop this inheritance behavior,
you can simply remove the
$instanceof
declaration in the prepare statement, e.g.,
<aop> <prepare expr="field(* @org.jboss.cache.pojo.annotation.Replicable->*)" /> </aop>
Detailed semantics of
pojocache-aop.xml
(or equivalently pojocache-aop.xml
)
can again
be found in JBoss Aop. But above statements basically declare all field
read and write operations in classes
Address
and
Person
will be "prepared" (or "aspectized"). Note
that:
instanceof
operator declares any sub-type or sub-class of the specific POJO will also be "aspectized". For
example, if a
Student
class is a subclass of
Person
, JBossAop will automatically instrument it as well!
private
,
protected
,
public
, etc.) The main reason being that we consider all fields as stateful data. However, we can relax this
requirement in the future if there is a use case for it.
final
and
transient
though. That is, field with these modifiers are not stored in cache and is not replicated either. If
you don't want your field to be managed by the cache, you can declare them with these modifiers, e.g.,
transient.
Annotation is a new feature in Java 5.0 that when declared can contain metadata at compile and run time. It is well suited for aop declaration since there will be no need for external metadata xml descriptor.
To support annotation (in order to
simplify user's development effort), the JBoss Cache distribution ships with a
pojocache-aop.xml
under the
resources
directory. For
reference, here is annotation definition from
pojocache-aop.xml
again
:
<aop> <prepare expr="field(* @org.jboss.cache.pojo.annotation.Replicable->*)" /> </aop>
Basically, it simply states that any annotation with both marker interfaces will be "aspectized" accordingly.
Here is a code snippet that illustrate the declaration:
@org.jboss.cache.pojo.annotation.Replicable public class Person {...}The above declaration will instrument the class
Person
and all of its sub-classes. That is, if
Student
sub-class from Personal
, then it will get instrumented automatically without
further annotation declaration.
In Release 2.0, we have added two additional field level annotations for customized behavior.
The first one is @org.jboss.cache.pojo.annotation.Transient
. When applied to a field
variable, it has the same effect as the Java language transient
keyword. That is, PojoCache
won't put this field into cache management (and therefore no replication).
The second one is @org.jboss.cache.pojo.annotation.Serializable
, when applied to a field
variable, PojoCache will treat this variable as Serializable
, even when it is
Replicable
. However, the field will need to implement the Serializable
interface such that it can be replicated.
Here is a code snippet that illustrates usage of these two annotations. Assuming that you have a Gadget class:
public class Gadget { // resource won't be replicated @Transient Resource resource; // specialAddress is treated as a Serializable object but still has object relationship @Serializable SpecialAddress specialAddress; // other state variables }
Then when we do:
Gadget gadget = new Gadget(); Resource resource = new Resource(); SepcialAddress specialAddress = new SpecialAddress(); // setters gadget.setResource(resource); gadget.setSpecialAddress(specialAddress); cache1.putObject("/gadget", gadget); // put into PojoCache management Gadget g2 = (Gadget)cache2.getObject("/gadget"); // retrieve it from another cache instance g2.getResource(); // This is should be null because of @Transient tag so it is not replicated. SepcialAddress d2 = g2.getSpecialAddress(); d2.setName("inet"); // This won't get replicated automatically because of @Serializable tag ge.setSpecialAddress(d2); // Now this will.
As already mentioned, a user can use the aop precompiler
(aopc
) to precompile the POJO classes such that,
during runtime, there is no additional system class loader needed. The
precompiler will read in
pojocache-aop.xml
and weave
the POJO byte code at compile time. This is a convenient feature to
make the aop less intrusive.
Below is an Ant snippet that defines the library needed for the various Ant targets that we are
listing here. User can refer to the build.xml
in the distribution for full details.
<path id="aop.classpath"/> <fileset dir="${lib}"/> <include name="**/*.jar" //> <exclude name="**/jboss-cache.jar" //> <exclude name="**/j*unit.jar" //> <exclude name="**/bsh*.jar" //> </fileset/> </path/>
In JDK5.0, you can use the javaagent
option that does not require a
separate Classloader. Here are the ant snippet from one-test-pojo
, for example.
<target name="one.test.pojo" depends="compile" description="run one junit test case."> <junit printsummary="yes" timeout="${junit.timeout}" fork="yes"> <jvmarg value="-Djboss.aop.path=${output}/resources/pojocache-aop.xml"/> <jvmarg value="-javaagent:${lib}/jboss-aop-jdk50.jar"/> <classpath path="${output}/etc" /> <sysproperty key="log4j.configuration" value="file:${output}/etc/log4j.xml" /> <classpath refid="lib.classpath"/> <classpath refid="build.classpath"/> <formatter type="xml" usefile="true"/> <test name="${test}" todir="${reports}"/> </junit> </target>
Below is the code snippet for the aopc
Ant target. Running this target will do
compile-time weaving of the POJO classes specified.
<taskdef name="aopc" classname="org.jboss.aop.ant.AopC" classpathref="aop.classpath"/> <target name="aopc" depends="compile" description="Precompile aop class"> <aopc compilerclasspathref="aop.classpath" verbose="true"> <src path="${build}"/> <include name="org/jboss/cache/aop/test/**/*.class"/> <aoppath path="${output}/resources/pojocache-aop.xml"/> <classpath path="${build}"/> <classpath refid="lib.classpath"/> </aopc> </target>
Below is a snapshot of files that are generated when aopc is applied. Notice that couple extra classes have
been generated because of aopc
.