Everyone 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.jarfrom<JBOSS_HOME>/server/all/libto<JBOSS_HOME>/server/default/lib. -
PojoCache MBean service configuration
Download the configuration file bellow into the folder
<JBOSS_HOME>/server/default/deploy.<?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
etc\META-INFpresent in the JBoss Cache releases.
-
Loadtime Instrumentation
Copy the jar file
pluggable-instrumentor.jarfrom<JBOSS_HOME>/server/default/deploy/jboss-aop-jdk50.deployerto<JBOSS_HOME>/bin. -
“prepare” your POJOs
Declare your POJOs as “prepared” by adding the type level annotation
@org.jboss.cache.aop.annotation.PojoCacheableto all that need to be put into cache management. -
AspectManager service configuration
Edit the
jboss-service.xmlfile in<JBOSS_HOME>/server/default/deploy/jboss-aop-jdk50.deployer/META-INFto enable loadtime transformations.<?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.<?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_OPTSenvironment variableEdit
run.shorrun.bat(depending on what OS you’re on) and add the following to theJAVA_OPTSenvironment variable:
set JAVA_OPTS=%JAVA_OPTS% -javaagent:pluggable-instrumentor.jar.
Usage
How to use and interact with PojoCache.
-
Sample POJO
Create a simple POJO.
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.
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.
Hey, first of all, great article! I’m sure many people will find this useful.
One thing to note, there’s no need for step 6 because HTTP field level session replication (a feature provided in AS 4.2 alreday) has the same needs as you (see http://www.jboss.org/community/docs/DOC-9982), hence, server/all/deploy/jboss-web-cluster.sar/jboss-web-cluster.aop already contains a META-INF/jboss-aop.xml containing those two bind points you mentioned. See http://anonsvn.jboss.org/repos/jbossas/tags/JBoss_4_2_3_GA/tomcat/src/resources/META-INF/jboss-aop.xml
Finally, I’d suggest that you try out PojoCache 3.x that is distributed with AS 5!
Hi Galder,
I don’t use the server configuration
All.Using the
Defaultconfiguration requires that step.And yes, I should try the AS 5 but the company I work for is not yet ready to take that step
.
Hi,
You have to put jgroups.jar to ../default/lib directory to get it woking without any errors.
How to use jboss cache in web based application.
I am trying to use the PojoCache without the AS in a J2EE env using Tomcat. I have to use it in a clustered env which makes Pojocache as a valuable component.
What I want to know is how to conf the configuration file that will work in the J2EE env and how to make use that conf file?
Please reply in detail.
Thanks,
JAM
Unfortunately, I may not be of great help to you.
I’ve never tried to use JBoss Cache without JBoss AS.
You may try Infinispan, since all future effort will be on Infinispan and not JBoss Cache. Or, try to get help on the JBoss Cache user forum.
Hello
I would say thanks for the whole information and for the samples you have provided.
I have implemented the same steps in my current project.
Due to JAR or I don’t know I am having an issue in the following piece of Code only and hence couldn’t able to proceed.
CacheHelper.INSTANCE.getPojoCache().putObject(“/aop/person”, person);
I am not finding the putObject method and hence Compile error.
I have added all the required JARs but still the issue is coming.
Please help me out.
Regards
Ramkumar
In your
CacheHelperclass are you usingPojoCacheMBeanorTreeCacheMBean?Only the
PojoCacheMBeanhas theputObject()method.Thanks for your kind reply.
I am using PojoCacheMBean only and added the required JARs also like:
jboss-cache-1.4.1.SP9.jar,
oscache-2.3.jar,
pojocache-3.0.jar,
jbosscache-core.jar,
jboss-cache-jdk50.jar
Please help out me regarding the same.
Regards
Ramkumar
The JAR file
jboss-cache-jdk50.jarcontains the classorg.jboss.cache.aop.PojoCacheMBean. Just be sure to copy it from<JBOSS_HOME>/server/all/libto<JBOSS_HOME>/server/default/lib.