In this tutorial we will use Infinispan Data Grid Platform to store cached data from our clustered Java EE application.
Data grid products which are form of middleware that can be used to store large set of data across a network distributed applications. If you are pretty new to Infinispan we suggest reading the following tutorials which will introduce you to Infinispan platform: Introduction to Infinispan , Advanced Infinispan
WildFly bundles Infinispan which can be used as a Cache container either in clustered application or in standalone applications. Earlier versions of Infinispan shipped with a native Infinispan configuration file contains cache configurations for a single cache manager as you can see from the following snippet;
Compare the following which is Infinispan native configuration:
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd" xmlns="urn:infinispan:config:5.1"> <global> <transport clusterName="demoCluster"/> <globalJmxStatistics enabled="true"/> </global> <default> <jmxStatistics enabled="true"/> <clustering mode="distribution"> <l1 enabled="true" lifespan="60000"/> <hash numOwners="2" rehashRpcTimeout="120000"/> <sync/> </clustering> </default> </infinispan>
On the other hand, WildFly’s infinispan subsystem configuration defines multiple cache managers, each identified by a name. Cache managers can have one or more aliases….with this snippet which is (a part) of Infinispan configuration:
<subsystem xmlns="urn:jboss:domain:infinispan:4.0"> <cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server"> <transport lock-timeout="60000"/> <replicated-cache name="default" mode="SYNC"> <transaction mode="BATCH"/> </replicated-cache> </cache-container> <cache-container name="replicated_cache" default-cache="default" module="org.wildfly.clustering.server" jndi-name="infinispan/replicated_cache"> <transport lock-timeout="60000"/> <cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan"> <transport lock-timeout="60000"/> <distributed-cache name="dist" mode="ASYNC" l1-lifespan="0" owners="2"> <locking isolation="REPEATABLE_READ"/> <transaction mode="BATCH"/> <file-store/> </distributed-cache> <distributed-cache name="concurrent" mode="SYNC" l1-lifespan="0" owners="2"> <file-store/> </distributed-cache> </cache-container> <cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan"> <transport lock-timeout="60000"/> <distributed-cache name="dist" mode="ASYNC" l1-lifespan="0" owners="2"> <locking isolation="REPEATABLE_READ"/> <transaction mode="BATCH"/> <file-store/> </distributed-cache> </cache-container> <cache-container name="hibernate" default-cache="local-query" module="org.hibernate.infinispan"> <transport lock-timeout="60000"/> <local-cache name="local-query"> <eviction strategy="LRU" max-entries="10000"/> <expiration max-idle="100000"/> </local-cache> <invalidation-cache name="entity" mode="SYNC"> <transaction mode="NON_XA"/> <eviction strategy="LRU" max-entries="10000"/> <expiration max-idle="100000"/> </invalidation-cache> <replicated-cache name="timestamps" mode="ASYNC"/> </cache-container> </subsystem>
The above Cache Containers are used internally by the application server to manage Clustering capabilities. If you want to use an embedded Cache configured in WildFly, start at first by adding it to your configuration:
/subsystem="infinispan"/cache-container="replicated_cache":add(default-cache="default",jndi-name="infinispan/replicated_cache",module="org.wildfly.clustering.server") /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache":add(jndi-name="infinispan/replicated_cache/mycache",mode="SYNC") /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="expiration":add() /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="state-transfer":add() /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="eviction":add(strategy="NONE") /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="locking":add() /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="backups":add() /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="transaction":add(locking="OPTIMISTIC",mode="FULL_XA") /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="backup-for":add() /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/component="partition-handling":add() /subsystem="infinispan"/cache-container="replicated_cache"/replicated-cache="mycache"/store="none":add()
The above CLI commans will create the following Replicated Cache Container definition:
<cache-container name="replicated_cache" default-cache="default" module="org.wildfly.clustering.server" jndi-name="infinispan/replicated_cache"> <transport lock-timeout="60000"/> <replicated-cache name="mycache" jndi-name="infinispan/replicated_cache/mycache" mode="SYNC"> <transaction locking="OPTIMISTIC" mode="FULL_XA"/> <eviction strategy="NONE"/> </replicated-cache> </cache-container>
So, let’s see how you can use the above Cache in a clustered application. We will lookup the cache manager from JNDI, normally with the @Resource annotation such as:
@Resource(lookup="java:jboss/infinispan/replicated_cache") private org.infinispan.manager.CacheContainer container;
Now recall our Java EE 6 sample application which we have introduced at the beginning of our journey, and let’s change the Managed Beans so that you use the Infinispan Cache:
package com.sample.bean; import java.util.ArrayList; import java.util.List; import java.util.UUID; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.faces.bean.ManagedBean; import javax.inject.Named; import org.infinispan.manager.CacheContainer; import com.sample.model.Property; @ManagedBean(name="manager") public class PropertyManager { @Resource(lookup="java:jboss/infinispan/replicated_cache") private CacheContainer container; private org.infinispan.Cache<String, Property> cache; ArrayList cacheList; private String key; private String value; @PostConstruct public void start() { this.cache = this.container.getCache("mycache"); cacheList = new ArrayList<Property>(); } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public void save() { Property p = new Property(); p.setKey(key); p.setValue(value); this.cache.put(generateKey(),p); } public void clear() { this.cache.clear(); } public List getCacheList() { List<Property> dataList = new ArrayList<Property>(); dataList.addAll(cache.values()); return dataList; } public String generateKey() { String uuid = UUID.randomUUID().toString(); return uuid; } }
You can compile the above class by adding the required Maven dependency to your Maven project:
<dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-core</artifactId> <version>8.2.4.Final</version> </dependency> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-commons</artifactId> <version>8.2.4.Final</version> </dependency>
In order to get working with your Infinispan cache on Wildfly is triggering Infinispan dependencies, either using in your META-INF/MANIFEST.MF or jboss-deployment-structure.xml as follows:
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <deployment> <dependencies> <module name="org.infinispan" services="export"/> <module name="org.infinispan.commons" services="export"/> </dependencis> </deployment> </jboss-deployment-structure>
As you can see from the above jboss-deployment-structure.xml, we needed also to include a dependency to org.infinispan.commons: this is why, since Infinispan 7 some of the core classes have been moved to the module org.infinispan.commons.
In order to start your cluster nodes you need to provide an unique node name and, since you are running multiple servers on the same machine specify a port offset so that you don’t have port conflicts between servers.
So we will start up NodeA with:
standalone -c standalone-ha.xml -Djboss.node.name=nodeA
and NodeB with:
standalone -c standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=nodeB
Now deploy the application (see at the bottom for full code) and contact the first node:
Now move to the second node and verify that cache entries have been replicated to the second node. Add some data in it as well:
Replication vs Distribution
Entries added to any cache instance will be replicated to all other cache instances in the cluster, and can be retrieved locally from any instance. This clustered mode provides a quick and easy way to share state across a cluster, however replication practically only performs well in small clusters (under 10 servers), due to the number of replication messages that need to happen – as the cluster size increases.
For this reason, it is adviced to use a distributed cache which allows Infinispan to scale linearly by defining a number of cluster-wide replicas for each cache entry.
Defining a cache as distributed just requires using the <distributed-cache> tag. For example here we are defining a number of 3 cluster-wide replicas (parameter owners) for each entry. (Default is 2)
<distributed-cache name="dist" mode="ASYNC" batching="true" owners="3"> <file-store/> </distributed-cache>
You can download here the full code for this example .
Eager to learn more about Infinispan?
Configure and develop applications using the Infinispan Data grid platform
Follow a simple ticket booking example to easily learn the features of Infinispan in practice
Draw on the experience of Manik Surtani, the leader, architect and founder of this popular open source project