Create Stored Tasks on Infinispan 8

This tutorial will teach you how to use Stored Tasks in Infinispan 8 which can be executed from an Hot Rod Client, much the same way you can execute a Stored Procedure on a Database

Stored Tasks are functions which can be deployed on a Server cache and executed locally. They are especially useful in a client-server Hot rod communication since:

  • They allow using Transaction API within your scripts, so you can trigger transactions from an Hot Rod Client
  • Scripts are executed remotely on the Server, thus enhancing the performance of your applications

Creating your first Stored Task

Start your Infinispan 8 Server at first:

[francesco@localhost bin]$ ./standalone.sh

You will see from the server logs that a special cache, named “___script_cache” has been started:

INFO  [org.jboss.as.clustering.infinispan] (MSC service thread 1-2) DGISPN0001: Started ___script_cache cache from clustered container
INFO  [org.jboss.as.clustering.infinispan] (MSC service thread 1-2) DGISPN0001: Started ___hotRodTopologyCache cache from clustered container
INFO  [org.infinispan.rest.NettyRestServer] (MSC service thread 1-8) ISPN012003: REST server starting, listening on 127.0.0.1:8080
INFO  [org.infinispan.server.endpoint] (MSC service thread 1-8) DGENDPT10002: REST mapped to /rest
INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Infinispan Server 8.1.0.CR1 (WildFly Core 2.0.0.CR8) started in 21108ms - Started 186 of 255 services (139 services are lazy, passive or on-demand)

Now we will code a Stored Task. A Stored Task right now can be coded using Nashhorn’s Javascript engine available in JVM. In future versions you could use as well Java for your scripts. So here is a sample script:

// mode=local,language=javascript
var cache = cacheManager.getCache();
cache.get("k");

The above script does nothing fancy: it just recovers the default cache and retrieves the key “k” from it. The mode property on the top instructs the execution engine where we want to run the script: local for running the script on the node that is handling the request and distributed for running the script wrapped by a distributed executor.

Here is a more complex example:

mode=local,language=javascript
key*value
var cache = cacheManager.getCache("default");
cache.clear();
cache.put(marshaller.objectToByteBuffer(key)), marshaller.objectToByteBuffer(value));

In this example we have declared two parameters (key,value) which will be passed to the function. Next, since the current version of the Script Engine transfer parameters as byte streams, we need some Marshalling before inserting them into the cache. Here is a full Java example which can be used to trigger this example:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import java.util.Set;
import java.util.UUID;

import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;

public class Demo1 {

    private RemoteCacheManager cacheManager;
    private RemoteCache<String, Object> cache;

    public Demo1() {
        String script = "// mode=local,language=javascript\n"
                + "key*value\n"
                + "var cache = cacheManager.getCache(\"default\");\n"
                + "cache.clear();\n"
                + "cache.put(marshaller.objectToByteBuffer(key), marshaller.objectToByteBuffer(value));";

        ConfigurationBuilder builder = new ConfigurationBuilder();
        builder.addServer()
                .host("127.0.0.1")
                .port(Integer.parseInt("11222"));
        cacheManager = new RemoteCacheManager(builder.build());
        cache = cacheManager.getCache("default");

        RemoteCache<String, String> scriptCache = cacheManager.getCache("___script_cache");

        scriptCache.put("script.js", script);

        Map<String, Object> params = new HashMap<>();
        params.put("key", UUID.randomUUID().toString());
        params.put("value", UUID.randomUUID().toString());

        Object result = cache.execute("script.js", params);

        System.out.println("Dumping cache Data");
        System.out.println("==========================");

        Set set = this.cache.keySet();
        Iterator i = set.iterator();
        while (i.hasNext()) {
            String key = (String) i.next();
            System.out.println("[ key: " + key + " - value: " + cache.get(key));
        }

    }


    public static void main(String[] args) {
        new Demo1();
    }

}

So in this simple class we load the js function in the script_cache with the name “script.js”. Then we execute the script using cache.execute API which receives as input the Map of parameters. Pretty cool isn’t it?

Using the Transaction API in Script Tasks

Another advantage of using Stored Tasks is that you can manage Transaction boundaries from within your script. This allows in practice to make your Hot rod client-server communication transactional. Here follows a simple script which adds one key, then starts a transaction and rolls back the cache operations executed within the Transaction:

String script = "// mode=local,language=javascript\n"
        + "value * value2\n"
        + "var cache = cacheManager.getCache(\"default\");\n"
        + "var tm = cache.getTransactionManager();\n"
        + "cache.clear();\n"
        + "cache.put(marshaller.objectToByteBuffer(\"k0\"), marshaller.objectToByteBuffer(value));\n"
        + "tm.begin();\n"
        + "cache.put(marshaller.objectToByteBuffer(\"k1\"), marshaller.objectToByteBuffer(value2));\n"
        + "tm.rollback();";

Many thanks to Tristan Tarrant for providing details about Stored Script execution on the Infinispan blog: http://blog.infinispan.org/2015/10/stored-script-execution.html

Infinispan 8 Web Console

INFO: This tutorial is outdated. The recommended way to Manage Infinispan 10 or newer is by means of the Command Line Interface or the REST Interface. We recommend checking the following tutorial: Getting started with Infinispan Command Line Interface

One of the new features introduced in Infinispan 8 release is the web based management console through which you can monitor your caches and your Infinispan clusters.

Check this tutorial for a quick introduction on getting started with Infinispan 8: Infinispan 8 tutorial

In order to use the management console you need a management user, therefore enter the bin folder of your installation and execute the script:

$ add-user.sh
What type of user do you wish to add? 
 a) Management User (mgmt-users.properties) 
 b) Application User (application-users.properties)
(a): a

Enter the details of the new user to add.
Using realm 'ManagementRealm' as discovered from the existing property files.
Username : infini-user
Password : 
Re-enter Password : 
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: 
About to add user 'infini-user' for realm 'ManagementRealm'
Is this correct yes/no? yes

Now we can start the server.In the current release of Infinispan (8.1.0 Beta) the Management console works just with in Domain mode, therefore once installed Infinispan 8.1.0 Beta, start the server in domain mode:

$ domain

Start a browser an point to the management address which is by default http://127.0.0.1:9990

A login page will request your credentials to access the management console:

The current version of the management console is split into two tabs:

  • The Cache tab allows monitoring the available caches
  • The Clusters tab will let you manage the cluster of server that make up your data grid

Clicking on the Cache Tab you will see the list of available caches. As you can see, out of the box, 4 caches are exposed when starting in clustered mode:

You can click on the clustered cache link to see details about the available caches:

Statistics about the caches are available by clicking on the singe caches; you can also navigate through the cluster nodes and get statistics on the single nodes. For example, if you select the upper Clusters option:

From here you can add or remove Server nodes or query the cache statistics for that node. Let’s say you want to get statistics about the server-one:

Performance metrics are also available from this window:

This was just a quick overview to some of the features of the Web based UI of Infinispan, which as we said are available only in Domain mode. You can expect plenty of other useful UI for your Data Grid in the next releases of Infinispan.

Connecting to a Remote Infinispan Server using Hot Rod

In this tutorial you will learn how to configure and use Infinispan 8.1 and use some of its API.

The server version of Infinispan is available at: http://infinispan.org/download/

Behind the hoods the new server version is bundled into a WildFly installation, therefore you will have to configure Infinispan through the infinispan subsystem. The standalone server contains two configurations:

  • standalone.xml (non-clustered cache)
  • clustered.xml (clustered Infinispan distribution)

Here follows the clustered configuration:

<subsystem xmlns="urn:infinispan:server:core:8.1" default-cache-container="clustered">
    <cache-container name="clustered" default-cache="default" statistics="true">
        <transport lock-timeout="60000"/>
        <distributed-cache name="default" mode="SYNC" segments="20" owners="2" remote-timeout="30000" start="EAGER">
            <locking acquire-timeout="30000" concurrency-level="1000" striping="false"/>
            <transaction mode="NONE"/>
        </distributed-cache>
        <distributed-cache name="memcachedCache" mode="SYNC" segments="20" owners="2" remote-timeout="30000" start="EAGER">
            <locking acquire-timeout="30000" concurrency-level="1000" striping="false"/>
            <transaction mode="NONE"/>
        </distributed-cache>
        <distributed-cache name="namedCache" mode="SYNC" start="EAGER"/>
        <distributed-cache name="transactionalCache" mode="SYNC" start="EAGER">
            <transaction mode="NON_XA" locking="PESSIMISTIC"/>
        </distributed-cache>
    </cache-container>
</subsystem>

Basically, Infinispan offers two access patterns, both of which are available in any runtime:

  • Embedded into your application code
  • As a Remote server accessed by a client (REST, memcached or Hot Rod wire protocols are supported)

In the latter case, you will need some connectors which enable receiving calls from remote clients:

<subsystem xmlns="urn:infinispan:server:endpoint:8.0">
    <hotrod-connector socket-binding="hotrod" cache-container="clustered">
        <topology-state-transfer lazy-retrieval="false" lock-timeout="1000" replication-timeout="5000"/>
    </hotrod-connector>
    <memcached-connector socket-binding="memcached" cache-container="clustered"/>
    <rest-connector socket-binding="rest" cache-container="clustered" security-domain="other" auth-method="BASIC"/>
    <websocket-connector socket-binding="websocket" cache-container="clustered"/>
</subsystem>

Let’s see as an example an HotRod client application which connects to the “default” clustered caches, and adds some data to it. At first we will start a cluster of Infinispan nodes. This can be easily achieved starting the server in domain mode, which is bound to the “clustered” profile.

$ domain.sh

Check from the logs that the server started and that the clusted was correclty composed:

[Server:server-one] 19:47:03,254 INFO [org.infinispan.CLUSTER] (remote-thread--p2-t4) ISPN000310: Starting cluster-wide rebalance for cache ___hotRodTopologyCache, topology CacheTopology{id=1, rebalanceId=1, currentCH=ReplicatedConsistentHash{ns = 60, owners = (1)[master:server-one: 60]}, pendingCH=ReplicatedConsistentHash{ns = 60, owners = (2)[master:server-one: 30, master:server-two: 30]}, unionCH=null, actualMembers=[master:server-one, master:server-two]}

Now let’s connect to the Infinispan cluster with some code:

package com.mastertheboss;

import java.util.Iterator;
import java.util.Map;

import java.util.Set;
import java.util.UUID;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.ServerStatistics;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;

public class DemoHotRod {

    private RemoteCacheManager cacheManager;
    private RemoteCache<String, Object> cache;

    public DemoHotRod() {

        ConfigurationBuilder builder = new ConfigurationBuilder();
        builder.addServer()
                .host("127.0.0.1")
                .port(Integer.parseInt("11222"));
        cacheManager = new RemoteCacheManager(builder.build());

        cache = cacheManager.getCache("default");

        cache.put(UUID.randomUUID().toString(), UUID.randomUUID().toString());

        System.out.println("Dumping cache Data");
        System.out.println("==========================");
        Set set = this.cache.keySet();
        Iterator i = set.iterator();
        while (i.hasNext()) {
            System.out.println(i.next());
        }
        //Print cache statistics
        ServerStatistics stats = cache.stats();
        for (Map.Entry stat : stats.getStatsMap().entrySet()) {
            System.out.println(stat.getKey() + " : " + stat.getValue());
        }

    }

    public static void main(String[] args) {

        DemoHotRod manager = new DemoHotRod();

    }

}

In order to compile the class, you will need to incude the Infinispan core libraries and the Hot Rod client APIs, plus their dependencies. The simplest way to do that is by means of the following Maven’s pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mastertheboss</groupId>
    <artifactId>hotrod-demo</artifactId>
    <version>1.0.0</version>
    <name>Hot Rod Client Demo</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
 
        <version.org.infinispan>8.0.1.Final</version.org.infinispan>

        <!-- other plugin versions -->
        <exec.plugin.version>1.2.1</exec.plugin.version>
        <ant.plugin.version>1.7</ant.plugin.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.infinispan</groupId>
                <artifactId>infinispan-bom</artifactId>
                <version>${version.org.infinispan}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Import the infinispan core -->
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-core</artifactId>
            <scope>compile</scope>
        </dependency>

        <!-- Import the Infinispan client hotrod -->
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-client-hotrod</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <!-- Maven will append the version to the finalName (which is the 
            name given to the generated jar) -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- This plugin permits the execution of this quickstart 
                through mvn exec:java command -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>${exec.plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.mastertheboss.DemoHotRod</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
</project>

You can compile the class with:

$ mvn clean install

And run the application with:

$ mvn exec:java

As a result, you should see in your cache an entry named

Dumping cache Data
==========================
153ccf2e-5aee-4de5-95bc-afe9d941deb4

currentNumberOfEntries : 1
hits : 0
removeHits : 0
totalBytesRead : 15
timeSinceStart : 214
removeMisses : 0
totalNumberOfEntries : 0
stores : 0
misses : 0
retrievals : 0
totalBytesWritten : 0

Monitoring WildFly’s Infinispan caches

In this tutorial we will learn how to monitor the Embedded Infinispan Caches contained in JBoss EAP / WildFly using Infinispan Listeners.

An org.infinispan.notifications.Listener is a key element of Infinispan infrastructure. You can define a new Listener by means of the @org.infinispan.notifications.Listener annotation. Objects annotated with this annotation can be attached to a running Cache so users can be notified of Cache events. Let’s see an example of it:

package com.sample;

import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStopped;
import org.infinispan.notifications.cachemanagerlistener.event.CacheStartedEvent;
import org.infinispan.notifications.cachemanagerlistener.event.CacheStoppedEvent;

@Listener(clustered = true)
public class SimpleListener {

    @CacheEntryCreated
    public void addKey(CacheEntryCreatedEvent event) {
        System.out.println("New entry " + event.getKey() + " created in the cache with value "+event.getValue());
    }

    @CacheEntryRemoved
    public void removeKey(CacheEntryRemovedEvent event) {
        System.out.println("Entry " + event.getKey() + " removed from the cache");
    }

    @CacheStarted
    public void cacheStarted(CacheStartedEvent event) {
        System.out.println("Cache Started");
    }

    @CacheStopped
    public void cacheStopped(CacheStoppedEvent event) {
        System.out.println("Cache Stopped");
    }
}

As you can see, each method is annotated with the event it is interested in. The Listener methods annotated with these events must be public , return a void , and accept a single parameter representing the event type.

As an example, the method annotated with @CacheEntryCreated annotation will be invoked when a new entry is added to the cache, while the corresponding method

annotated with @CacheEntryRemoved will be invoked, and an entry will be removed from the cache.

Now that we have a listener we can apply it to a Cache used by the application server. Out of the box some embedded Cache Containers are available in the application server:

<cache-container name="server" aliases="singleton cluster" module="org.wildfly.clustering.server" default-cache="default">
. . .
</cache-container>

<cache-container name="web" module="org.wildfly.clustering.web.infinispan" default-cache="dist" statistics-enabled="true">
. . .
</cache-container>

<cache-container name="ejb" aliases="sfsb" module="org.wildfly.clustering.ejb.infinispan" default-cache="dist">
. . .
</cache-container>

<cache-container name="hibernate" module="org.hibernate.infinispan" default-cache="local-query">
. . .
</cache-container>

Discussing about the purpose of the above caches is out of the scope of this articles; we will show here how to monitor the “web” cache where the HTTP session is cached. We will create for this purpose a CDI producer.

package com.sample;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted;
import org.infinispan.notifications.cachemanagerlistener.annotation.CacheStopped;
 
@ApplicationScoped
public class Producer {

    @Resource(lookup = "java:jboss/infinispan/container/web")
    private EmbeddedCacheManager container;

    private org.infinispan.Cache<String, String> cache;

    @Inject
    SimpleListener listener;

    @PostConstruct
    public void initCache() {

        this.cache = container.getCache("myapp.war");

        System.out.println("Got cache " + cache.getName());
        cache.addListener(listener);

    }

    @Produces
    public org.infinispan.Cache&lt;String, String&gt; getCache() {
        return cache;
    }

    public void setCache(org.infinispan.Cache&lt;String, String&gt; cache) {
        this.cache = cache;
    }

}

The CDI producer references as a Resource the “web” Cache container which is available in the application server. Within the Cache container a Set of Caches are available: one for each application which registers on the Cache container. In our case we will pickup the Cache related to out application:

this.cache = container.getCache("myapp.war");

You can improve this piece of code with some application name’s discovery name or by means of a simple property.

What is important to note is that, once we have got the Cache, we register our Listener on it, so that we can produce a org.infinispan.Cache<String, String> which has got our Listener registered.

Now most of our effort is done. Include a <distributable /> stanza in the web.xml. Within the Web application you can verify with a Simple Servlet that attributes are “captured” by our listener:

@WebServlet(name = "TestServlet", urlPatterns = {"/TestServlet"})
public class TestServlet extends HttpServlet {

 protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         request.getSession().setAttribute("key", "value");
. . .            
        }
    }
}

As you can see from the logs, each attribute added in the HTTP Session is captured on the servers that got it replicated/distributed from the Cache:

13:03:46,430 INFO  [stdout] (default task-2) New entry irwU9ly0c8786WLA-Doz147jc79Slj8R1uuoyrNh created in the cache with value {key=value}

That’s all about Infinispan cache and JBoss/WildFly for now! 

BUILD SUCCESSFUL (total time: 2 seconds)

Using Infinispan Query API

Applications using a NoSQL storage often need to query data using a full-text search that can be hardly accomplished using traditional RDBMS. This tutorial shows how to use Infinispan Query module in order to search through data added to the cache using an Object oriented fluent API.

In order to do its job, Infinispan uses behind the scenes the popular Apache Lucene and Hibernate search framework.
Apache Lucene is a high-performance, extensible full-text search-engine library written in Java. Hibernate Search encloses Lucene indexing processes into the transaction contexts of Hibernate/JPA, and transparently manages the lifecycle of Lucene Document objects through the event handler mechanism of Hibernate Core.
Let’s see with a concrete example how to perform queries using Infinispan Query API. The first thing you need to do, is adding Infinispan query API library to your project, besides the infinispan-core artifactId:

<dependencies>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-core</artifactId>
            <version>${version.infinispan}</version>
        </dependency>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-query</artifactId>
            <version>${version.infinispan}</version>
        </dependency>
</dependencies>

Now you need to annotate your data that you are going to store in your cache. Here’s a minimalist model for our example:

package com.sample;

mport org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;

@Indexed
public class Customer {

	@Field(store = Store.YES, analyze = Analyze.NO)
	long id;

	@Field(store = Store.YES, analyze = Analyze.NO)
	String name;

	@Field(store = Store.YES, analyze = Analyze.NO, indexNullAs = Field.DEFAULT_NULL_TOKEN)
	String surname;

	public Customer(long id, String name, String surname) {
		this.id = id;
		this.name = name;
		this.surname = surname;
	}

	@Override
	public String toString() {
		return "Customer [id="+id+" name=" + name + ", surname=" + surname + "]";
	}
}

Most of these annotations are fairly straightforward to understand. @Indexed indicates that we want Hibernate Search to manage indexes for this entity. @Field indicates that a particular property is to be indexed. Now let’s write a simple class that stores and retrieves this object from the cache:   

package com.sample;

import java.util.List;

import org.infinispan.Cache;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.query.Search;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;

public class InfinispanQuery {

   public static void main(String[] args) {
      ConfigurationBuilder builder = new ConfigurationBuilder();
      builder.indexing().index(Index.ALL)
         .addProperty("default.directory_provider", "ram")
         .addProperty("lucene_version", "LUCENE_CURRENT");
      // Build a local cache manager with default configuration
      DefaultCacheManager cacheManager = new DefaultCacheManager(builder.build());
      // Retrieve the default cache
      Cache<String, Customer> cache = cacheManager.getCache();
      // Add some entries
      cache.put("acme", new Customer(1,"John", "Doe"));
      cache.put("lego", new Customer(2,"Pat", "Williams"));
      
      // Retrieve the QueryFactory for the cache
      QueryFactory factory = Search.getQueryFactory(cache);
      // Build up a query
      Query query = factory.from(Customer.class).having("name").eq("John").toBuilder().build();
      // Execute the query
      List<Customer> list = query.list();
      // List the data
      list.forEach(person -> System.out.printf("Match: %s", person));
      // Stop the cache manager and release resources
      cacheManager.stop();
   }

}

This code at first creates a Cache to be used for the example which has index enabled on it. This is mandatory in order to use Infinispan query API. Next we create a QueryFactory for the cache, which will be used to construct our Query.
As a side note, consider that you can also enable indexes using Infinispan configuration file:

<infinispan>
 <namedCache name="replicated">
   <clustering mode="replicated" />
   <indexing enabled="true" indexLocalOnly="false" />
  </namedCache>
</infinispan>

Using Infinispan with WildFly

The Infinispan subsystem provides caching support for HA services in the form of Infinispan caches such as high-performance, transactional caches which can operate in both non-distributed and distributed scenarios. WildFly application server ships with the Infinispan subsystem, however, it is recommended not to use the application server cache definition as repository for your Cache. This tutorial shows the available options for using Infinispan in an Enterprise application deployed on WildFly or any Java EE container.

As said, you are adviced not to use WildFly’s Infinispan Caches for application purpose as they are internally used for managing the application’s server own Caches. So which are our options? You can use these two patterns:

Library mode – When running in this mode, you have to provide all the binaries required to build and run your Cache. You can do it in two ways: either embedding the libraries in your application or installing the modules into the application server. This usage mode gives access to advanced functionalities such as Transactional support, Listeners and Interceptors, Batching, Lucene-based queries and index, Distributed execution framework via Map-Reduce.

Client-server mode: When running in this mode applications can remotely access the data grid server using Hot Rod, memcached or REST client APIs. You have to bundle a minimal set of API to connect to the Infinispan Server and this additional layer can provide you some benefits such as Connection pooling with cache stores, multiple interfaces (memcached, Hotrod, REST), authentication and encrypytion and cluster Rolling upgrades (without bringing the cluster down).

In the following tutorial we have described how to use a remote Infinispan Server through the Hot Rod protocol: Connecting to a Remote Infinispan Server using Hot Rod

Now we will learn how to code an application in Library mode.

Deploy and application on WildFly in Library Mode

The core part of it is the MyCacheManagerProvider which defines a DefaultCacheManager using the “jgroups-udp.xml” configuration based on a Clustered Synchronous Cache that uses Distribution as Cache mode:

 
package com.mastertheboss.producer;

import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.CacheMode;

@ApplicationScoped
public class MyCacheManagerProvider {

	private static final long ENTRY_LIFESPAN = 60 * 1000; // 60 seconds


	private DefaultCacheManager manager;

	public DefaultCacheManager getCacheManager() {
		if (manager == null) {

			GlobalConfiguration glob = new GlobalConfigurationBuilder().clusteredDefault() 
					.transport().addProperty("configurationFile", "jgroups-udp.xml")  
					.globalJmxStatistics().allowDuplicateDomains(true).enable() 
					.build();  
			Configuration loc = new ConfigurationBuilder().jmxStatistics().enable()  
					.clustering().cacheMode(CacheMode.DIST_SYNC)  
					.hash().numOwners(2)  
					.expiration().lifespan(ENTRY_LIFESPAN)  
					.build();
			manager = new DefaultCacheManager(glob, loc, true);
		}
		return manager;
	}

	@PreDestroy
	public void cleanUp() {
		manager.stop();
		manager = null;
	}

}


The above Cache is configure to expire its entries after 60 seconds.

In order to make available the DefaultCacheManager, we will create a producer Class that gets injected the MyCacheManagerProvider:

 
package com.mastertheboss.producer;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import org.infinispan.manager.DefaultCacheManager;
 

@ApplicationScoped
public class ApplicationBean {
  
 
    @Inject
    MyCacheManagerProvider cacheManagerProvider;

    @Produces
    DefaultCacheManager getDefaultCacheManager() {
        return cacheManagerProvider.getCacheManager();
    }
}

The class which manages your Cache is the following Manager class that contains some basic methods for adding or cleaning the cache:

package com.mastertheboss.service;

import java.util.ArrayList;
import java.util.List;
import javax.enterprise.inject.Model;
import javax.inject.Inject;

import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import com.mastertheboss.model.Item;

@Model
public class Manager {

	@Inject  
	DefaultCacheManager cacheManager;

	private String key;
	private String value;

	public Manager() {
	}

	public String save() {
		Cache<String, Item> cache = cacheManager.getCache();
		Item item = new Item();
		item.setKey(key);
		item.setValue(value);
		cache.put(item.getKey(), item);
		return "home";
	}

	public List getCacheList() {
		Cache<String, Item> cache = cacheManager.getCache();
		List list = new ArrayList();
		cache.entrySet().forEach(entry -> list.add(entry.getValue()));
		return list;

	}

	public void removeItem(String key) {
		Cache<String, Item> cache = cacheManager.getCache();   
		cache.remove(key);
	}

	public void clear() {
		Cache<String, Item> cache = cacheManager.getCache();
		cache.clear();
	}
	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;
	}
}

In order to complete the application, a simple xhtml page which displays data and let add new Items:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>

</h:head>
<h:body>
	<h2>Infinispan Demo - Library Mode</h2>
	<h:form id="jsfexample">
		<h:panelGrid columns="2" styleClass="default">

			<h:outputText value="Enter key:" />
			<h:inputText value="#{manager.key}" />

			<h:outputText value="Enter value:" />
			<h:inputText value="#{manager.value}" />

			<h:commandButton actionListener="#{manager.save}"
				styleClass="buttons" value="Save key/value" />
			<h:commandButton actionListener="#{manager.clear}"
				styleClass="buttons" value="Clear cache" />

			<h:messages />


		</h:panelGrid>


		<h:dataTable value="#{manager.cacheList}" var="item"
			styleClass="table" headerClass="table-header"
			rowClasses="table-odd-row,table-even-row">
			<h:column>
				<f:facet name="header">Key</f:facet>
				<h:outputText value="#{item.key}" />
			</h:column>
			<h:column>
				<f:facet name="header">Value</f:facet>
				<h:outputText value="#{item.value}" />
			</h:column>
		</h:dataTable>
	</h:form>
</h:body>
</html>

Finally, if we want to package our application and deploy it on WildFly, we need to specify in our pom.xml that Infinispan libraries are NOT provided so they will be packaged in our Web application. Here is the pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mastertheboss</groupId>
	<artifactId>infinispan-wildfly-library</artifactId>

	<version>1.0</version>

	<packaging>war</packaging>
	<name>Quickstart: Infinispan-WildFly</name>

	<licenses>
		<license>
			<name>Apache License, Version 2.0</name>
			<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
			<distribution>repo</distribution>
		</license>
	</licenses>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<infinispan.version>9.4.5.Final</infinispan.version>
		<version.server.bom>15.0.0.Final</version.server.bom>
	</properties>
	<dependencyManagement>
		<dependencies>

			<dependency>
				<groupId>org.wildfly.bom</groupId>
				<artifactId>wildfly-javaee8-with-tools</artifactId>
				<version>${version.server.bom}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>

		</dependencies>
	</dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>javax.enterprise</groupId>
			<artifactId>cdi-api</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.jboss.spec.javax.annotation</groupId>
			<artifactId>jboss-annotations-api_1.3_spec</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.jboss.spec.javax.ejb</groupId>
			<artifactId>jboss-ejb-api_3.2_spec</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.infinispan</groupId>
			<artifactId>infinispan-core</artifactId>
			<version>${infinispan.version}</version>
		</dependency>
		<dependency>
			<groupId>org.infinispan</groupId>
			<artifactId>infinispan-commons</artifactId>
			<version>${infinispan.version}</version>
		</dependency>
	</dependencies>
	
	<build>
		<finalName>${project.artifactId}</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

And here is the application in action:

You can check the source code for this tutorial on: https://github.com/fmarchioni/mastertheboss/tree/master/infinispan/infinispan-wildfly-library

Two options to deploy applications in library mode

The example discussed here bundles the infinispan libraries with your application. Another option for deploying applications in library mode is to install the Infinispan data Grid/JDG Modules in the WildFly/JBoss EAP application server. The Infinispan modules are available here: http://infinispan.org/download/ (Click on WildFly/EAP Modules)

To install the modules in JBoss EAP/WildFly, unzip the modules in the application server home directory. The user application needs to include dependencies on these modules via jboss-deployment-structure.xml file.

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
   <deployment>
      <dependencies>
         <module name="org.infinispan" slot="ispn-9.4" services="export"/>
      </dependencies>
   </deployment>
</jboss-deployment-structure>

Develop a clustered application with Infinispan Data Grid

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

Infinispan tutorial for beginners

Warning: this tutorial has been written for an old version of Infinispan and it's now obsolete.
We recommend to check the following tutorials to get started quickly with Infinispan:

What is Infinispan ? a quick introduction
Getting Started with Infinispan data grid -the right way
Clustering Infinispan made simple

In this tutorial we will cover some advanced aspects related to Infinispan cache stores, transactions, data eviction and clustering.

 Until now we have seen some basic examples of Infinispan’s Local Cache. You might wonder if it makes sense to use Infinispan as Local Cache instead of a simple Hashtable or CuncurrentHashtable. Even if Infinispan focuses on large scale systems, it does make sense to use Infinispan also as Local cache.
For example:

  • Infinispan cache has eviction/expiry policies which are built-in. You don’t have to worry about out-of-memory caused by collections growing too much.
  • Infinispan has the ability to store cache data on flat File or Jdbc, thus achieving data consistency in the event of system failure.
  • Infinispan can be configured to use and to participate in JTA compliant transactions.
  • Infinispan has MVCC-based concurrency. Thus it’s highly optimized for fast, non-blocking readers.
  • You can monitor and manage Infinispan using JMX or Jopr plugin.

I think we mentioned enough reason why you can benefit of Infinispan even if you are not ready for a Clustered environment.Let’s explore some of this great features:

Using Infinispan Cache Stores

Infinispan can be configured with one or several cache stores allowing it to store data in a persistent location such as shared JDBC database, a local filesystem and others. Using a Cache store requires to define a Cache with a loader element. For example the following define a cache which uses a Flat file Cache store:

<namedCache name="CacheStore">

     <loaders passivation="false" shared="false" preload="true">
          <loader class="org.infinispan.loaders.file.FileCacheStore" fetchPersistentState="true"
               ignoreModifications="false" purgeOnStartup="false">
            <properties>
                 <property name="location" value="C:\infinispan-4.0.0.FINAL\store"/>
               </properties>
          </loader>
     </loaders>
</namedCache>

This can be used to load the custom “CacheStore” configuration and persist one attribute:

CacheManager manager = new DefaultCacheManager("all.xml");   
Cache cache = manager.getCache("CacheStore");  
                
cache.put("key", "value");
        
cache.stop();
manager.stop();

This would produce a file, as soon as the CacheManager is stopped:

By default Cache stores are configured to work synchronously. You can however configure updates to the cache as asynchronously written to the Cache store.
This means that updates to the cache store are done by a separate thread to the client thread interacting with the cache. You can achieve this by adding the async attribute to your loader:

<loader

          class="org.infinispan.loaders.file.FileCacheStore"
          fetchPersistentState="true" ignoreModifications="false"
          purgeOnStartup="false">
         . . . . . .        
        <async enabled="true" threadPoolSize="10"/>

</loader>

Using Transactions with InfiniSpan

Infinispan can be also configured to participate in JTA compliant transactions. All you need is including in your configuration file one of the TransactionManager classes that ship with Infinispan. For example the following is taken from the sample all.xml configuration:

<transaction
     transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup"
     syncRollbackPhase="false"
     syncCommitPhase="false"
     useEagerLocking="false"/>
    

Implementing a JTA tx requires referencing the “Advanced Cache” from Cache instance as follows:

CacheManager manager = new DefaultCacheManager("all.xml");

Cache cache = manager.getCache();  
TransactionManager tm = cache.getAdvancedCache().getTransactionManager();
tm.begin();
cache.put("key", "value");
tm.commit();

Evicting Data from the Cache

Another reason why you should consider using Infinispan over a simple Hashtable caching is the ability to manage the amount of items in the cache using two simple strategies:

Expiration: data is removed from the cache when it reaches a lifespan or it’s idle for too long
Eviction: data is removed from the cache when too many items are added to the cache.

This is a sample configuration which uses eviction:

<namedCache name="evictionCache">

  <eviction wakeUpInterval="500" maxEntries="5000" strategy="FIFO" />
  <expiration lifespan="60000" maxIdle="1000"/>
</namedCache>

Let’s see a trivial example how the cache evicts data if the number of items exceeds maxEntries:

CacheManager cm = new DefaultCacheManager("all.xml");

Cache cache = cm.getCache("evictionCache");
        
for (int i=0;i<5000;i++) {
   cache.put("element"+i, "value"+i);    
}
System.out.println("First In Element is " + cache.get("element0"));
cache.put("element5000", "value5000");
System.out.println("Eviction Thread is waking up......");    
Thread.sleep(1000);
System.out.println("First In Element is " + cache.get("element0"));

This will result in:
First In Element is value0
Eviction Thread is waking up……
First In Element is null


How to Cluster Infinispan

Note: There is an updated tutorial about Clustering infinispan which is available here: Clustering Infinispan – a complete example

Infinispan borrows many clustering aspects from JBoss Cache. As a matter of fact, also Infinispan uses JGroups as a network transport and JGroups handles a lot of the hard work of discovery new nodes.
The simplest way to start a cluster node is by means of a simple configuration which includes in the global configuration the cluster name and defines into the default element, the clustering section:

<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">
         <hash numOwners="2" rehashRpcTimeout="120000"/>
         <sync/>
      </clustering>
   </default>
</infinispan>

Save this file with the name let’s say cluster.xml. Now let’s create a simple application which loads this configuration file and handles a Ticket Booking System:

package com.sample.main;

import java.util.Set;

import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;

import com.sample.model.Ticket;
import com.sample.utils.IOUtils;

public class SimpleCache {

    public void start() throws Exception {
        DefaultCacheManager m = new DefaultCacheManager("cluster.xml");
        Cache<Integer, Ticket> cache = m.getCache();
        String command = null;
        int ticketid = 1;
        
        IOUtils.dumpWelcomeMessage();
        
        while (true){
            command = IOUtils.readLine("> ");
            if (command.equals("book")) {
                
                String name = IOUtils.readLine("Enter name ");
                String show = IOUtils.readLine("Enter show ");
                
                Ticket ticket = new Ticket(name,show);
                cache.put(ticketid, ticket);
                log("Booked ticket "+ticket);
                ticketid++;
            }
            else if (command.equals("pay")) {
                Integer id = new Integer(IOUtils.readLine("Enter ticketid "));
                Ticket ticket = cache.remove(id);
                log("Checked out ticket "+ticket);
            }
            else if (command.equals("list")) {
                Set <Integer> set = cache.keySet();
                for (Integer ticket: set) {
                    System.out.println(cache.get(ticket));
                }
            }
            else if (command.equals("quit")) {
                m.stop();
                break;
            }        
            else {
                log("Unknown command "+command);
                IOUtils.dumpWelcomeMessage();
            }
        }
            
    }
    
    public static void main(String[] args) throws Exception {
        new SimpleCache().start();

    }
    public static void log(String s){
        System.out.println(s);
    }
}

Now start the application in two separate JVM consoles and verify that once booked a ticket will be visible also on the other JVM console (conversely, if you pay for the ticket, the entry will be removed from the cluster node caches):

Console 1 view:

Console 2 view:

You can download here the full code which includes the Ticket and IOUtils class

Customising Cluster configuration

By using the default configuration you have the basics to start playing with clustering: however you should consider tuning your Cache with a custom cluster configuration. The all.xml sample configuration file (which can be found in the /etc/config-samples dir) contains some templates. For example:

<namedCache name="distributedCache">

      <clustering mode="distribution">
         <sync/>
 
         <hash numOwners="2" rehashWait="120000" rehashRpcTimeout="600000"/>
         <l1 enabled="true" lifespan="600000"/>
      </clustering>
</namedCache>

Here we have defined a custom Cache named “distributedCache” which is targeted for a clustered environment. Data is distributed among cluster members using a synchronous mode (See element <sync/>).

One interesting configuration parameter is “numOwners” which states the number of owners for each key.  The larger this number is, the safer your data will be, but the slower the cluster will be.

In our sample configuration, we have defined to replicate the keys on two nodes of the cluster, so in an hypotetical 4 cluster nodes we would have the following Cache:

The last element in the configuration refers to the L1 cache also known as near cache.
An L1 cache is a distributed cache which is held locally to prevent unnecessary remote fetching of entries. Caches with L1 enabled will consult the L1 cache before fetching an entry from a remote cache. 
By default, entries in L1 have a lifespan of 60,000 milliseconds (though you can configure how long L1 entries are cached for).  L1 entries are also invalidated when the entry is changed elsewhere in the cluster so you are sure you don’t have stale entries cached in L1.

Here we end our second tutorial about Infinispan. Hope you found this stuff interesting and you want to learn more from the Community documentation. Any feedback as usual is highly welcome!