JBoss PojoCache configuration
Posted onEveryone knows that documentation is not one of JBoss strengths. This article is meant to fill this gap. It describes and exemplifies how to configure JBoss PojoCache as a MBean service, using loadtime transformations with JBossAop framework, so you don’t need precompiled instrumentation.
Introduction
This section gives you an introduction about PojoCache and its advantages over TreeCache (a plain cache system).
PojoCache is an in-memomy, transactional, and replicated POJO (plain old Java object) cache system that allows users to operate on a POJO transparently without active user management of either replication or persistency aspects. PojoCache, a component of JBossCache (uses PojoCache class as an internal implementation, the old implementation TreeCacheAop has been deprecated.), is the first in the market to provide a POJO cache functionality. JBossCache by itself is a 100% Java based library that can be run either as a standalone program or inside an application server.
TreeCache limitations:
- Users will have to manage the cache specifically; e.g., when an object is updated, a user will need a corresponding API call to update the cache content.
- If the object size is huge, even a single field update would trigger the whole object serialization. Thus, it can be unnecessarily expensive.
- The object structure can not have a graph relationship. That is, the object can not have sub-objects that are shared (multiple referenced) or referenced to itself (cyclic). Otherwise, the relationship will be broken upon serialization.
PojoCache advantages:
- No need to implement Serializable interface for the POJOs.
- Replication (or even persistency) is done on a per-field basis (as opposed to the whole object binary level).
- The object relationship and identity are preserved automatically in a distributed, replicated environment. It enables transparent usage behavior and increases software performance.
Use case
We had a project at Present Technologies where we needed to load small amounts of information from external resources and save it during short periods of time. This information was read-only and could have a lot of concurrent read accesses. So we wanted a solution that was simpler, faster, and more scalable than saving the data in a traditional database.
Some thoughts we had before choosing PojoCache:
- The amount of information was really small, so keeping it in memory wasn’t a problem.
- Since this data was mainly read-only, replication to other nodes wouldn’t kill our network.
- We needed the object relationships and identities to be preserved automatically in a distributed, replicated environment. PojoCache enables transparent usage behavior and increases software performance.
So the idea was simple. When JBoss AS starts and every once in a while, a service (PojoCache MBean) will kick in, retrieve the data from the external resources, and put it into the cache. As soon as the transaction commits the cache is replicated to all the other nodes in the cluster.
Requirements
This article is targeted to JBossCache version 1.4.1 “Cayenne”, which comes bundle with JBoss AS 4.2.3, and to JDK 5.
Configuration
How to configure PojoCache as a MBean service:
JBoss Cache with Java 5 annotations
Copy the jar file
jboss-cache-jdk50.jar
from<JBOSS_HOME>/server/all/lib
to<JBOSS_HOME>/server/default/lib
.PojoCache MBean service configuration
Download the configuration file bellow into the folder
<JBOSS_HOME>/server/default/deploy
.example-pojocache-service.xml
<?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="org.jboss.cache.aop.PojoCache" name="jboss.cache:service=PojoCache"> <depends>jboss:service=TransactionManager</depends> <!-- Configure the TransactionManager --> <attribute name="TransactionManagerLookupClass">org.jboss.cache.JBossTransactionManagerLookup</attribute> <!-- Isolation level : SERIALIZABLE REPEATABLE_READ (default) READ_COMMITTED READ_UNCOMMITTED NONE --> <attribute name="IsolationLevel">REPEATABLE_READ</attribute> <!-- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC --> <attribute name="CacheMode">REPL_SYNC</attribute> <!-- Just used for async repl: use a replication queue --> <attribute name="UseReplQueue">false</attribute> <!-- Replication interval for replication queue (in ms) --> <attribute name="ReplQueueInterval">0</attribute> <!-- Max number of elements which trigger replication --> <attribute name="ReplQueueMaxElements">0</attribute> <!-- Name of cluster. Needs to be the same for all clusters, in order to find each other --> <attribute name="ClusterName">TreeCache-Cluster</attribute> <!-- JGroups protocol stack properties. Can also be a URL, e.g. file:/home/bela/default.xml <attribute name="ClusterProperties"></attribute> --> <attribute name="ClusterConfig"> <config> <!-- UDP: if you have a multihomed machine, set the bind_addr attribute to the appropriate NIC IP address, e.g bind_addr="192.168.0.2" --> <!-- UDP: On Windows machines, because of the media sense feature being broken with multicast (even after disabling media sense) set the loopback attribute to true --> <udp mcast_addr="228.1.2.3" mcast_port="48866" ip_ttl="64" ip_mcast="true" mcast_send_buf_size="150000" mcast_recv_buf_size="80000" ucast_send_buf_size="150000" ucast_recv_buf_size="80000" loopback="false" /> <ping timeout="2000" num_initial_members="3" up_thread="false" down_thread="false" /> <merge2 min_interval="10000" max_interval="20000" /> <fd_SOCK /> <verify_SUSPECT timeout="1500" up_thread="false" down_thread="false" /> <pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800" max_xmit_size="8192" up_thread="false" down_thread="false" /> <unicast timeout="600,1200,2400" window_size="100" min_threshold="10" down_thread="false" /> <pbcast.STABLE desired_avg_gossip="20000" up_thread="false" down_thread="false" /> <frag frag_size="8192" down_thread="false" up_thread="false" /> <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true" print_local_addr="true" /> <pbcast.STATE_TRANSFER up_thread="true" down_thread="true" /> </config> </attribute> <!-- Whether or not to fetch state on joining a cluster --> <attribute name="FetchStateOnStartup">true</attribute> <!-- The max amount of time (in milliseconds) we wait until the initial state (ie. the contents of the cache) are retrieved from existing members in a clustered environment --> <attribute name="InitialStateRetrievalTimeout">5000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">15000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">10000</attribute> <!-- Name of the eviction policy class. --> <attribute name="EvictionPolicyClass" /> </mbean> </server>
Notes:
- This XML file was taken from PojoCache User Documentation for Release 1.4.1 “Cayenne“.
- A lot more examples can be found in the folder
etcMETA-INF
present in the JBoss Cache releases.
Loadtime Instrumentation
Copy the jar file
pluggable-instrumentor.jar
from<JBOSS_HOME>/server/default/deploy/jboss-aop-jdk50.deployer
to<JBOSS_HOME>/bin
.Prepare your POJOs
Declare your POJOs as “prepared” by adding the type level annotation
@org.jboss.cache.aop.annotation.PojoCacheable
to all that need to be put into cache management.AspectManager service configuration
Edit the
jboss-service.xml
file in<JBOSS_HOME>/server/default/deploy/jboss-aop-jdk50.deployer/META-INF
to enable loadtime transformations.jboss-service.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- ===================================================================== --> <!-- JBoss Server Configuration --> <!-- ===================================================================== --> <server> <!-- The code for the service is different for the different run scenarios *** JBoss 4.0 * JDK 1.4 - org.jboss.aop.deployment.AspectManagerService * JDK 5 (not using -javaagent switch) - org.jboss.aop.deployment.AspectManagerService * JDK 5 (using -javaagent switch) - org.jboss.aop.deployment.AspectManagerServiceJDK5 * BEA JRockit 1.4.2 - org.jboss.aop.deployment.AspectManagerService *** JBoss 3.2 * JDK 1.4 - org.jboss.aop.deployment.AspectManagerService32 * JDK 5 (not using -javaagent switch) - org.jboss.aop.deployment.AspectManagerService32 * JDK 5 (using -javaagent switch) - org.jboss.aop.deployment.AspectManagerService32JDK5 * BEA JRockit 1.4.2 - org.jboss.aop.deployment.AspectManagerService32 --> <mbean code="org.jboss.aop.deployment.AspectManagerServiceJDK5" name="jboss.aop:service=AspectManager"> <attribute name="EnableLoadtimeWeaving">true</attribute> <!-- only relevant when EnableLoadtimeWeaving is true. When transformer is on, every loaded class gets transformed. If AOP can't find the class, then it throws an exception. Sometimes, classes may not have all the classes they reference. So, the Suppressing is needed. (i.e. Jboss cache in the default configuration --> <attribute name="SuppressTransformationErrors">true</attribute> <attribute name="Prune">true</attribute> <!--attribute name="Include">org.jboss.test, org.jboss.injbossaop</attribute--> <attribute name="Include">com.samaxes.example</attribute> <attribute name="Exclude">org, com, net, bsh, javassist, antlr, com.arjuna</attribute> <!-- This avoids instrumentation of hibernate cglib enhanced proxies <attribute name="Ignore">*$$EnhancerByCGLIB$$*</attribute> --> <attribute name="Optimized">true</attribute> <attribute name="Verbose">false</attribute> </mbean> <mbean code="org.jboss.aop.deployment.AspectDeployer" name="jboss.aop:service=AspectDeployer"> </mbean> </server>
Be sure to only include the packages that contains the POJOs to be aspectized and exclude all others.
WARNING: Not doing so can really slowdown your server startup!
AOP configuration
Enable your POJOs to be aspectized copying the AOP configuration file bellow to
<JBOSS_HOME>/server/default/deploy
.example-aop.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE aop PUBLIC "-//JBoss//DTD JBOSS AOP 1.0//EN" "http://labs.jboss.com/portal/jbossaop/dtd/jboss-aop_1_0.dtd"> <aop> <!-- If a POJO has JDK5 PojoCacheable annotation, it will be aspectized. --> <prepare expr="field(* @org.jboss.cache.aop.annotation.PojoCacheable->*)" /> <!-- If a POJO has JDK5 InstanceOfPojoCacheable annotation, it will be aspectized. --> <prepare expr="field(* $instanceof{@org.jboss.cache.aop.annotation.InstanceOfPojoCacheable}->*)" /> </aop>
JAVA_OPTS
environment variableEdit
run.sh
orrun.bat
(depending on what OS you’re on) and add the following to theJAVA_OPTS
environment variable:set JAVA_OPTS=%JAVA_OPTS% -javaagent:pluggable-instrumentor.jar
.
Usage
How to use and interact with PojoCache.
Sample POJO
Create a simple POJO.
Person.java
package com.samaxes.example; import org.jboss.cache.aop.annotation.PojoCacheable; @PojoCacheable public class Person { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
Cache Helper Singleton
Create a singleton to interact with PojoCache.
CacheHelper.java
package com.samaxes.example; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import org.jboss.cache.aop.PojoCacheMBean; import org.jboss.mx.util.MBeanProxyExt; import org.jboss.mx.util.MBeanServerLocator; public class CacheHelper { public static final CacheHelper INSTANCE = new CacheHelper(); private static PojoCacheMBean pojoCache; public PojoCacheMBean getPojoCache() { return pojoCache; } private CacheHelper() { try { MBeanServer server = MBeanServerLocator.locate(); pojoCache = (PojoCacheMBean) MBeanProxyExt.create(PojoCacheMBean.class, "jboss.cache:service=PojoCache", server); } catch (MalformedObjectNameException e) { // log exception... } } }
Note: This step is optional but it allows to not have duplicated code and to have only one instance of the PojoCache MBean in the application context.
Using PojoCache
And that’s it! Give it a try.
// Puts Person object into cache management CacheHelper.INSTANCE.getPojoCache().putObject("/aop/person", person); // Gets Person object from cache CacheHelper.INSTANCE.getPojoCache().getObject("/aop/person");
Every time you change a Person property, cache will manage its replication or persistency automatically.
As cited in the introduction, this is done on a per-field basis.
No MBean support
It’s possible to use PojoCache without using MBeans. The examples available in the PojoCache User Documentation use the following strategy:
cache = new PojoCache();
PropertyConfigurator config = new PropertyConfigurator(); // configure tree cache.
config.configure(cache, "META-INF/replSync-service.xml"); // Search under the classpath
cache.start();
...
cache.stop();
Resources
Note: For questions about JBoss technologies please use JBoss forums.
Comments
comments powered by Disqus