Since POJO Cache uses Core Cache for the underlying node replication, transaction, locking, and passivation behavior, the configuration is mostly the same.
When a PojoCache instance is obtained from a PojoCacheFactory, it is required that the either a org.jboss.cache.config.Configuration object is passed, or more typically a String indicating the location on the classpath or filesystem of an xml configuration file is provided. In the latter case, PojoCacheFactory will parse the xml to create a Configuration. PojoCache will simply pass the resulting Configuration to the underlying Core Cache implementation. For details on the configuration please see the "Configuration" chapter in the the JBoss Cache User Guide.
A common use-case is to configure the underlying Core Cache to enable passivation. Passivation is a feature used to reduce cache memory usage by evicting stale data that can later be reloaded. In JBoss Cache, it is done via a combination of an eviction policy and a cache loader. That is, when a node is evicted from the Cache's in-memory store, it will be stored in a persistent store by the cache loader. When the node is requested again, it will be loaded from the persistent store and stored into memory.
There is a restriction, however. Since POJO Cache maps object data into an internal area, there are two places that have object information. One is under the regular String ID that the user specifies, and the other is located under /__JBossInternal__. Therefore, to maintain consistentency, when you specify the eviction region, you can only specify one global (i.e., /_default_) region. This way, when the nodes associated with a POJO are passivated, they will do so across the whole region.
Below is a snippet from a cache configuration xml illustrating how the eviction policy along with cache loader can be configured. Please note that this is simply an aspect of the underlying Cache. That is, PojoCache layer is agnostic to this behavior.
<attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">3</attribute> </region> </config> </attribute> <attribute name="CacheLoaderConfiguration"> <config> <passivation>true</passivation> <preload>/</preload> <shared>false</shared> <!-- we can now have multiple cache loaders, which get chained --> <cacheloader> <class>org.jboss.cache.loader.FileCacheLoader</class> <!-- whether the cache loader writes are asynchronous --> <async>false</async> <!-- only one cache loader in the chain may set fetchPersistentState to true. An exception is thrown if more than one cache loader sets this to true. --> <fetchPersistentState>true</fetchPersistentState> <!-- determines whether this cache loader ignores writes - defaults to false. --> <ignoreModifications>false</ignoreModifications> </cacheloader> </config> </attribute>
Another way to support multiple regions in eviction is to use region-based marshalling. See the "Architecture" chapter in the JBoss Cache User Guide for more information on region-based marshalling. When the Cache uses region-based marshalling, POJO Cache will store internal node data on the region that is specified. This allows for a more flexible eviction policy.
POJO Cache supplies a pojocache-aop.xml that is required to be set via a system property: jboss.aop.path during compile- or load-time, or placed in the user's classpath. The file now consists of the interceptor stack specification, as well as annotations for POJO instrumentation. It is listed fully in the Appendix section. Note that the file should not normally need to be modified. Only an advanced use-case would require changes.
There are a number of ways to deploy POJO Cache:
Simply instantiate a PojoCacheFactory and invoke one of the overloaded createCache methods shown in the API Overview.
If PojoCache is run in JBoss AS then your cache can be deployed as an MBean simply by copying a standard cache configuration file to the server's deploy directory. The standard format of PojoCache's standard XML configuration file (as shown in the Appendix) is the same as a JBoss AS MBean deployment descriptor, so the AS's SAR Deployer has no trouble handling it. Also, you don't have to place the configuration file directly in deploy; you can package it along with other services or JEE components in a SAR or EAR.
In AS 5, if you're using a server config based on the standard all config, then that's all you need to do; all required jars will be on the classpath. Otherwise, you will need to ensure pojocache.jar, jbosscache.jar and jgroups-all.jar are on the classpath. You may need to add other jars if you're using things like JdbmCacheLoader. The simplest way to do this is to copy the jars from the PojoCache distribution's lib directory to the server config's lib directory. You could also package the jars with the configuration file in Service Archive (.sar) file or an EAR.
It is possible, to deploy a POJO Cache 2.0 instance in JBoss AS 4.x However, the significant API changes between the 2.x and 1.x releases mean none of the standard AS 4.x clustering services (e.g. http session replication) that rely on the 1.x API will work with PojoCache 2.x. Also, be aware that usage of PojoCache 2.x in AS 4.x is not something the cache developers are making any significant effort to test, so be sure to test your application well (which of course you're doing anyway.)
Note in the example the value of the mbean element's code attribute: org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper. This is the class JBoss Cache uses to handle JMX integration; the PojoCache itself does not expose an MBean interface. See the JBoss Cache MBeans section for more on the PojoCacheJmxWrapper.
Once your cache is deployed, in order to use it with an in-VM client such as a servlet, a JMX proxy can be used to get a reference to the cache:
MBeanServer server = MBeanServerLocator.locateJBoss(); ObjectName on = new ObjectName("jboss.cache:service=PojoCache"); PojoCacheJmxWrapperMBean cacheWrapper = (PojoCacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(server, on, PojoCacheJmxWrapperMBean.class, false); PojoCache cache = cacheWrapper.getPojoCache();
The MBeanServerLocator class is a helper to find the (only) JBoss MBean server inside the current JVM. The javax.management.MBeanServerInvocationHandler class' newProxyInstance method creates a dynamic proxy implementing the given interface and uses JMX to dynamically dispatch methods invoked against the generated interface to the MBean. The name used to look up the MBean is the same as defined in the cache's configuration file.
Once the proxy to the PojoCacheJmxWrapper is obtained, the getPojoCache() will return a reference to the PojoCache itself.
Beginning with AS 5, JBoss AS also supports deployment of POJO services via deployment of a file whose name ends with -beans.xml. A POJO service is one whose implementation is via a "Plain Old Java Object", meaning a simple java bean that isn't required to implement any special interfaces or extend any particular superclass. A PojoCache is a POJO service, and all the components in a Configuration are also POJOS, so deploying a cache in this way is a natural step.
Deployment of the cache is done using the JBoss Microcontainer that forms the core of JBoss AS. JBoss Microcontainer is a sophisticated IOC framework (similar to Spring). A -beans.xml file is basically a descriptor that tells the IOC framework how to assemble the various beans that make up a POJO service.
The rules for how to deploy the file, how to package it, how to ensure the required jars are on the classpath, etc. are the same as for a JMX-based deployment.
Following is an abbreviated example -beans.xml file. The details of building up the Configuration are omitted; see the "Deploying JBoss Cache" chapter in the JBoss Cache User Guide for a more complete example. If you look in the server/all/deploy directory of an AS 5 installation, you can find several more examples.
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <!-- First we create a Configuration object for the cache --> <bean name="ExampleCacheConfig" class="org.jboss.cache.config.Configuration"> ... details omitted </bean> <!-- The cache itself. --> <bean name="ExampleCache" class="org.jboss.cache.pojo.impl.PojoCacheImpl"> <constructor factoryClass="org.jboss.cache.pojo.PojoCacheFactory factoryMethod="createCache"> <parameter><inject bean="ExampleCacheConfig"/></parameter> <parameter>false</false> </constructor> </bean> </deployment>
An interesting thing to note in the above example is the difference between POJO Cache and a plain Cache in the use of a factory to create the cache. (See the "Deploying JBoss Cache" chapter in the JBoss Cache User Guide for the comparable plain Cache example.) The PojoCacheFactory exposes static methods for creating a PojoCache; as a result there is no need to add a separate bean element for the factory. Core Cache's DefaultCacheFactory creates caches from a singleton instance, requiring a bit more boilerplate in the config file.
POJO Cache provides an MBean that can be registered with your environment's JMX server to allow access to the cache instance via JMX. This MBean is the org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper. It is a StandardMBean, so it's MBean interface is org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean. This MBean can be used to:
See the PojoCacheJmxWrapperMBean javadoc for more details.
It is important to note a significant architectural difference between PojoCache 1.x and 2.x. In 1.x, the old TreeCacheAop class was itself an MBean, and essentially exposed the cache's entire API via JMX. In 2.x, JMX has been returned to it's fundamental role as a management layer. The PojoCache object itself is completely unaware of JMX; instead JMX functionality is added through a wrapper class designed for that purpose. Furthermore, the interface exposed through JMX has been limited to management functions; the general PojoCache API is no longer exposed through JMX. For example, it is no longer possible to invoke a cache attach or detach via the JMX interface.
If a PojoCacheJmxWrapper is registered, the wrapper also registers MBeans for the underlying plain Cache and for each interceptor configured in the cache's interceptor stack. These MBeans are used to capture and expose statistics related to cache operations; see the JBoss Cache User Guide for more. They are hierarchically associated with the PojoCacheJmxWrapper MBean and have service names that reflect this relationship. For example, a plain Cache associated with a jboss.cache:service=PojoCache will be accessible through an mbean named jboss.cache:service=PojoCache,cacheType=Cache. The replication interceptor MBean for that cache will be accessible through the mbean named jboss.cache:service=PojoCache,cacheType=Cache,cache-interceptor=ReplicationInterceptor.
The best way to ensure the PojoCacheJmxWrapper is registered in JMX depends on how you are deploying your cache:
Simplest way to do this is to create your PojoCache and pass it to the PojoCacheJmxWrapper constructor.
// Build but don't start the cache // (although it would work OK if we started it) PojoCache cache = PojoCacheFactory.createCache("cache-configuration.xml", false); PojoCacheJmxWrapperMBean wrapper = new PojoCacheJmxWrapper(cache); MBeanServer server = getMBeanServer(); // however you do it ObjectName on = new ObjectName("jboss.cache:service=PojoCache"); server.registerMBean(wrapper, on); // Invoking lifecycle methods on the wrapper results // in a call through to the cache wrapper.create(); wrapper.start(); ... use the cache ... on application shutdown // Invoking lifecycle methods on the wrapper results // in a call through to the cache wrapper.stop(); wrapper.destroy();
Alternatively, build a Configuration object and pass it to the PojoCacheJmxWrapper. The wrapper will construct the PojoCache:
Configuration config = buildConfiguration(); // whatever it does PojoCacheJmxWrapperMBean wrapper = new PojoCacheJmxWrapper(config); MBeanServer server = getMBeanServer(); // however you do it ObjectName on = new ObjectName("jboss.cache:service=TreeCache"); server.registerMBean(wrapper, on); // Call to wrapper.create() will build the Cache if one wasn't injected wrapper.create(); wrapper.start(); // Now that it's built, created and started, get the cache from the wrapper PojoCache cache = wrapper.getPojoCache(); ... use the cache ... on application shutdown wrapper.stop(); wrapper.destroy();
When you deploy your cache in JBoss AS using a -service.xml file, a PojoCacheJmxWrapper is automatically registered. There is no need to do anything further. The PojoCacheJmxWrapper is accessible through the service name specified in the cache configuration file's mbean element.
PojoCacheJmxWrapper is a POJO, so the microcontainer has no problem creating one. The trick is getting it to register your bean in JMX. This can be done by specifying the org.jboss.aop.microcontainer.aspects.jmx.JMX annotation on the PojoCacheJmxWrapper bean:
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <!-- First we create a Configuration object for the cache --> <bean name="ExampleCacheConfig" class="org.jboss.cache.config.Configuration"> ... build up the Configuration </bean> <!-- The cache itself. --> <bean name="ExampleCache" class="org.jboss.cache.pojo.impl.PojoCacheImpl"> <constructor factoryClass="org.jboss.cache.pojo.PojoCacheFactory factoryMethod="createCache"> <parameter><inject bean="ExampleCacheConfig"/></parameter> <parameter>false</false> </constructor> </bean> <!-- JMX Management --> <bean name="ExampleCacheJmxWrapper" class="org.jboss.cache.jmx.CacheJmxWrapper"> <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX( name="jboss.cache:service=ExamplePojoCache", exposedInterface=org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean.class, registerDirectly=true) </annotation> <constructor> <parameter><inject bean="ExampleCache"/></parameter> </constructor> </bean> </deployment>
As discussed in the Programatic Registration section, PojoCacheJmxWrapper can do the work of building, creating and starting the PojoCache if it is provided with a Configuration:
<?xml version="1.0" encoding="UTF-8"?> <deployment xmlns="urn:jboss:bean-deployer:2.0"> <!-- First we create a Configuration object for the cache --> <bean name="ExampleCacheConfig" class="org.jboss.cache.config.Configuration"> ... build up the Configuration </bean> <bean name="ExampleCache" class="org.jboss.cache.pojo.jmx.PojoCacheJmxWrapper"> <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX( name="jboss.cache:service=ExamplePojoCache", exposedInterface=org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean.class, registerDirectly=true) </annotation> <constructor> <parameter><inject bean="ExampleCacheConfig"/></parameter> </constructor> </bean> </deployment>
As mentioned above, the cache exposes a variety of statistical information through its MBeans. It also emits JMX notifications when events occur in the cache. See the JBoss Cache User Guide for more on the statistics and notifications that are available.
The only PojoCache addition to the plain JBoss Cache behavior described in the User Guide is that you can register with the PojoCacheJmxWrapper to get the notifications. There is no requirement to figure out the ObjectName of the underlying cache's CacheJmxWrapper and register with that.