Clustering Infinispan made simple

This tutorial will discuss how to configure and start an Infinispan cluster. As next step, we will show how to connect to the cluster remotely using a Java application.

Infinispan uses the JGroups library to provide network communication capabilities. JGroups is a toolkit for reliable group communication and provides many useful features and enables cluster node discovery, point-to-point and point-to-multipoint communication, failure detection, and data transfer between cluster nodes.

You can configure Infinispan to either be only on local JVM or clustered. Its real world scenario is the use in cluster mode, where all nodes act as a single cache, providing a large amount of memory heap.

If in a cluster, the cache can be configured for full replication or for distribution on mode.

Replication is the simplest clustered mode and comprises of one or more cache instances that replicate its data to all cluster nodes; in the end, all cluster nodes will end up with the same data.

In the Distribution mode, you can define, via configuration, the number of replicas that are available for a given data grid. The distribution strategy is designed for larger in-memory data grid clusters and it’s the most scalable data grid topology. Distribution strategy improves the application’s performance dramatically and outperforms the replication mode on traditional networks.

Setting up Infinispan cluster

Firstly, create two folders, each one to be used for an Infinispan cluster node:

$ mkdir nodeA

$ mkdir nodeB

Then, download Infinispan latest distribution from https://infinispan.org/download/

Next, unzip the Infinispan distribution in both folders:

$ unzip infinispan-server-12.1.7.Final.zip -d nodeA

$ unzip infinispan-server-12.1.7.Final.zip -d nodeB

Then, start the Infinispan cluster, using the UDP stack. Start the first node:

bin/server.sh --cluster-stack=udp

Next, start the second node:

bin/server.sh --cluster-stack=udp -Dinfinispan.socket.binding.port-offset=100

As you can see, we have used a port offset for the second node, as we are starting the cluster on the same machine.

From the Console, you will see that a new Cluster view has been generated, including both Infinispan servers:

2021-09-07 12:58:48,010 INFO  (ForkJoinPool.commonPool-worker-3) [org.infinispan.SERVER] ISPN080018: Started connector HotRod (internal)
2021-09-07 12:58:48,170 INFO  (main) [org.infinispan.SERVER] ISPN080018: Started connector REST (internal)
2021-09-07 12:58:48,394 INFO  (main) [org.infinispan.SERVER] ISPN080004: Connector SINGLE_PORT (default) listening on 127.0.0.1:11222
2021-09-07 12:58:48,395 INFO  (main) [org.infinispan.SERVER] ISPN080034: Server 'fedora-47059' listening on http://127.0.0.1:11222
2021-09-07 12:58:48,452 INFO  (main) [org.infinispan.SERVER] ISPN080001: Infinispan Server 12.1.7.Final started in 5229ms
2021-09-07 12:58:49,118 INFO  (jgroups-6,fedora-47059) [org.infinispan.CLUSTER] ISPN000094: Received new cluster view for channel cluster: [fedora-47059|1] (2) [fedora-47059, fedora-25875]
2021-09-07 12:58:49,123 INFO  (jgroups-6,fedora-47059) [org.infinispan.CLUSTER] ISPN100000: Node fedora-25875 joined the cluster

Finally, we will add an user to both servers so that we can access the Admin Console and reach the cache remotely:

./bin/cli.sh user create admin -p "password"

Create a sample Infinispan application

To test our Infinispan cluster, we will create a basic application which adds an Entry to a remote cache. This application is derived from a sample in the Infinispan quickstarts (https://github.com/infinispan/infinispan-simple-tutorials/tree/main)

Firstly, add an utility Class to connect to the Remote Infinispan Cluster:

package infinispan.demoremote;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.configuration.ClientIntelligence;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.impl.ConfigurationProperties;

public class InfinispanUtil {
	   public static final String USER = "admin";
	   public static final String PASSWORD = "password";
	   public static final String CACHE_NAME = "test";
 

	   public static final String CACHE_CONFIG =
	         "<distributed-cache name=\"CACHE_NAME\">\n"
	         + "    <encoding media-type=\"application/x-protostream\"/>\n"
	         + "</distributed-cache>";


	   public static final ConfigurationBuilder connectionConfig() {
	      ConfigurationBuilder builder = new ConfigurationBuilder();
	      builder.addServer().host("127.0.0.1").port(11222).security()
	            .authentication()
	            .username(USER)
	            .password(PASSWORD);
	        
	      builder.addServer().host("127.0.0.1").port(11322).security()
          .authentication()
          .username(USER)
          .password(PASSWORD);


	      builder.clientIntelligence(ClientIntelligence.BASIC);

	      // Make sure the remote cache is available.
	      // If the cache does not exist, the cache will be created
	      builder.remoteCache(CACHE_NAME)
	            .configuration(CACHE_CONFIG.replace("CACHE_NAME", CACHE_NAME));
	      return builder;
	   }


	   public static final RemoteCacheManager connect() {
	     ConfigurationBuilder builder = connectionConfig();

	      RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());

	      // Clear the cache in case it already exists from a previous running tutorial
	      cacheManager.getCache(CACHE_NAME).clear();

	      // Return the connected cache manager
	      return cacheManager;
	   }
}

As you can see from the code, we have used the ConfigurationBuilder to create a fluent Configuration with the server details of both Infinispan nodes. Within this utility Class, we have also defined the Cache name to be used, named “test”.

Next, let’s code a standalone Java class which uses the InfinispanUtil class to insert an Entry:

package infinispan.demoremote;

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

public class InfinispanRemoteCache {

   public static void main(String[] args) {
      // Connect to the server
      RemoteCacheManager cacheManager = InfinispanUtil.connect();
      // Obtain the remote cache
      RemoteCache<String, String> cache = cacheManager.getCache(InfinispanUtil.CACHE_NAME);
      /// Store a value
      cache.put("key", "value");
      // Retrieve the value and print it out
      System.out.printf("key = %s\n", cache.get("key"));
      // Stop the cache manager and release all resources
      cacheManager.stop();
   }

}

Building and running the application

To build and run the example application it is sufficient to include the HotRod library which is used to connect remotely to the Infinispan server and a Bill of Material to align with an Infinispan version:

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

<dependencies>

	<dependency>
		<groupId>org.infinispan</groupId>
		<artifactId>infinispan-client-hotrod</artifactId>
	</dependency>
</dependencies>

By running the application will show up in the Console:

Sep 07, 2021 12:59:24 PM org.infinispan.client.hotrod.RemoteCacheManager actualStart
INFO: ISPN004021: Infinispan version: Infinispan 'Taedonggang' 12.1.7.Final
key = value

Checking our Cluster in the Admin Console

Lastly, let’s have a look at the Web console of our cluster. Access one of the cluster nodes, for example the first one at: http://127.0.0.1:11222

Login with the credentials “admin” / “password”.

As you can see from the cluster-wide statistics, there’s one entry in relation to our Cache:

If you select the Cache “test”, you will see that the Entry we have just added is there:

The source code for this example application is available at: https://github.com/fmarchioni/mastertheboss/tree/master/infinispan/infinispan-remote

Conclusion

We have covered how to install and configure a basic Infinispan cluster, using the UDP protocol stack. As proof of concept, we have created a sample application which connected to the Cluster and added an Entry

What is Infinispan ? a quick introduction

What is Infinispan ? In a nutshell, Infinispan is an open source data grid platform written in Java which exposes a JSR-107 compatible cache interface in which you can store objects.

There are several use cases for Infinispan: let’s discuss the three most common use cases:

Using Infinispan as a Cache

Firstly, Infinispan can be used as a cache in front of your database, disk-based NoSQL store to offload your DB.

Database offloading is the process of removing or reducing the database load, mainframes which is a common bottleneck of applications.

Often, however, a simple cache isn’t enough – for example if your application is clustered and cache coherency is important to data consistency. A distributed cache like Infinispan can greatly help in such scenarios.

Using Infinispan as Data Store

Infinispan can also be used as a high-performance NoSQL data store. In addition to being in memory, Infinispan can also persist data to a more permanent store. We call this a cache store. Cache stores are pluggable, you can easily write your own, and many already exist for you to use. Learn more about cache stores – and existing implementations you can use today – on the cache stores section of this website.

Using Infinispan to add HA

Another common use case is adding clusterability and high availability to frameworks. You can configure Infinispan to replicate every new object to all nodes (Infinispan instances), which will result in simply maintaining a copy of each object in every node, so that if one of the nodes fails, state is not lost

This is a non-exhaustive list of features of Infinispan:

  • Data partitioning across a cluster
  • Work with domain objects rather than only bytes, arrays, or strings
  • Synchronous and asynchronous operations throughout
  • Distributed ACID transactions
  • Data distribution through the use of a consistent hash algorithm to determine where keys should be located in the cluster
  • Write-through/behind cache store support
  • Eviction support
  • Elastic scaling
  • Multiple access protocols
  • Support for compute grids
  • Persisting state to configurable cache stores

A first taste of Infinispan

Here is now a one minute example to get started with Infinispan.

  1. First, download the latest version from https://infinispan.org/download/

2. Unzip the Infinispan Server to a folder on your hard drive

3. Next, create an user to access the Management console. From the bin folder run:

./cli.sh user create
Specify a username: admin
Set a password for the user: password
Confirm the password for the user: password

4. Then, we will enable the REST Endpoint so that we can fetch some data using cURL. Open the conf/infinispan.xml and edit the endpoints section to include BASIC and DIGEST authentication:

<endpoints socket-binding="default" security-realm="default">
   <rest-connector>
      <!-- Specifies SASL mechanisms to use for authentication. -->
      <authentication mechanisms="DIGEST BASIC"/>
   </rest-connector>
</endpoints>

5. Finally, start the server from the bin folder:

$ ./server.sh

Adding a cache with some Data

To connect to Infinispan Management console, reach http://localhost:11222/ and log in with the user name admin/password:

Click on Open Console. From there choose to Create Cache:

Next, add a cache named “democache” using the example Cache template provided:

Then, add a simple cache entry to your cache:

Well done. We have just added a cache entry for our first cache. As we have enabled the REST endpoint, we can access our data from any tool like cURL:
curl  -u "admin:password" http://localhost:11222/rest/v2/caches/democache/name

{
   "_type": "string",
   "_value": "Sherlock Holmes"
}

Conclusion

We have just covered what is Infinispan and provided a basic quickstart for it. To continue learning check this resource: Getting Started with Infinispan data grid

Infinispan REST API made simple

Infinispan servers provide RESTful HTTP access to data via a REST module built on Netty. In this tutorial we will learn how to use it to manage Cache data with regards to all major Infinispan versions starting from the latest version.

Using Infinispan REST API (latest version)

REST endpoints listen on port 11222 by default.

First off, you can configure authentication to the REST endpoint with the cli.sh script in the bin folder of Infinispan server distribution.

./cli.sh user create
Specify a username: qwer1234!
Set a password for the user: qwer1234!
Confirm the password for the user: qwer1234!

Please note that older Infinispan versions used the “user-tool.sh” script to create users. This is now deprecated in Infinispan 12. Here is an example how to use usertool.sh for older Infinispan versions:

./user-tool.sh
Set a password for the user: qwer1234!
Confirm the password for the user: qwer1234!

Enabling BASIC REST authentication

Before starting, it is required that you enable BASIC authentication for your REST Endpoints. Open the conf/server.xml file and include the authentication mechanisms:

<rest-connector>
      <!-- Specifies SASL mechanisms to use for authentication. -->
      <authentication mechanisms="DIGEST BASIC"/>
   </rest-connector>
</endpoints>

Managing Caches with REST API

Let’s begin to show how to create a new Cache, passing as argument the XML Cache configuration and using the following REST URI:

POST /rest/v2/caches/{cacheName}

Which translates into:

curl -u myuser:qwer1234! -X POST -H "Content-type:application/xml" -d "
<infinispan>
    <cache-container>
        <distributed-cache name=\"cacheName\" mode=\"SYNC\"/>
    </cache-container>
</infinispan>" http://localhost:11222/rest/v2/caches/cacheName

Here is how you can verify the Cache configuration:

curl -u myuser:qwer1234! http://localhost:11222/rest/v2/caches/cacheName?action=config
{"distributed-cache":{"mode":"SYNC","transaction":{"mode":"NONE"}}}

On the other hand, you can retrieve both the configuration and th runtime parameters as follows:

curl -u myuser:qwer1234! http://localhost:11222/rest/v2/caches/cacheName

{"stats":{"current_number_of_entries_in_memory":-1,"time_since_start":-1,"time_since_reset":-1,"current_number_of_entries":-1,"total_number_of_entries":-1,"off_heap_memory_used":-1,"data_memory_used":-1,"remove_misses":-1,"evictions":-1,"average_read_time":-1,"average_read_time_nanos":-1,"average_write_time":-1,"average_write_time_nanos":-1,"average_remove_time":-1,"average_remove_time_nanos":-1,"required_minimum_number_of_nodes":-1,"retrievals":-1,"remove_hits":-1,"hits":-1,"stores":-1,"misses":-1},"size":0,"configuration":{"distributed-cache":{"mode":"SYNC","transaction":{"mode":"NONE"}}},"rehash_in_progress":false,"bounded":false,"indexed":false,"persistent":false,"transactional":false,"secured":false,"has_remote_backup":false,"indexing_in_progress":false,"statistics":false}

Finally, in order to delete your Cache, you can issue a DELETE command against the Cache name as follows:

curl -u myuser:qwer1234! -X DELETE http://localhost:11222/rest/v2/caches/cacheName

Managing Cache Data

In order to add a new Entry to our cache, we can use the following URI:

POST /rest/v2/caches/{cacheName}/{cacheKey}

which translates into:

curl -X POST -i  -u "myuser:qwer1234!" -H "Content-type:application/xml" -d "<catalog>
    <book id="book01">
       <author>Gambardella, Matthew</author>
       <title>XML Developer's Guide</title>
      <genre>Computer</genre>
       <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
       <description>An in-depth look at creating applications 
       with XML.</description>
    </book>
 </catalog>" http://localhost:11222/rest/v2/caches/cacheName/book01

In order to retrieve a Cache Entry, we can issue a GET against the cache key:

GET /rest/v2/caches/{cacheName}/{cacheKey}

In our case, this is going to be:

curl -u myuser:qwer1234! http://localhost:11222/rest/v2/caches/cacheName/book01

<catalog>
    <book id=book01>
       <author>Gambardella, Matthew</author>
       <title>XML Developer's Guide</title>
       <genre>Computer</genre>
       <price>44.95</price>
       <publish_date>2000-10-01</publish_date>
       <description>An in-depth look at creating applications 
      with XML.</description>
    </book>
 </catalog>

In order to update a Cache entry, we will use a PUT command:

PUT /rest/v2/caches/{cacheName}/{cacheKey}

In our case, this is going to be:

curl -X PUT -i  -u "myuser:qwer1234!" -H "Content-type:application/xml" -d "<catalog>
    <book id="book01">
       <author>John Smith</author>
       <title>XML Developer's Guide</title>
       <genre>Computer</genre>
       <price>44.95</price>
       <publish_date>2000-10-01</publish_date>
       <description>An in-depth look at creating applications 
       with XML.</description>
    </book>
 </catalog>" http://localhost:11222/rest/v2/caches/cacheName/book01

Finally, to delete a Cache Entry, we will be using a DELETE command

DELETE /rest/v2/caches/{cacheName}/{cacheKey}

In our case, this is going to be:

curl -X DELETE -u myuser:qwer1234! http://localhost:11222/rest/v2/caches/cacheName/book01

For more details, check the latest release of Infinispan REST documentation: https://infinispan.org/docs/stable/titles/rest/rest.html

Infinispan REST Interface for releases 8 and 9

The second part of the tutorial covers using REST API with older Infinispan releases (8 and 9)

If you are using an older infinispan release (8 or 9) the REST service is also available as part of the server distribution, so you don’t need any more to download an external Web application for it:

15:51:49,815 INFO  [org.infinispan.server.endpoint] (MSC service thread 1-2) DGENDPT10000: REST starting
15:51:50,332 INFO  [org.infinispan.rest.embedded.netty4.NettyRestServer] (MSC service thread 1-2) ISPN012003: REST server starting, listening on 127.0.0.1:8080
15:51:50,333 INFO  [org.infinispan.server.endpoint] (MSC service thread 1-2) DGENDPT10002: REST mapped to rest/rest
15:51:50,452 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management
15:51:50,453 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
15:51:50,453 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: Infinispan Server 9.0.0.Final (WildFly Core 2.2.0.Final) started in 3918ms - Started 150 of 161 services (47 services are lazy, passive or on-demand)

The supported HTTP methods in the REST API for entities are as follows:

  • HEAD : This retrieves a cache entry, but you’ll receive exactly the same content that you have stored, which means that if you have stored a binary object, you will need to deserialize the content yourself
  • GET: This retrieves a cache entry or a list of keys
  • POST: This creates a new cache entry
  • DELETE: This deletes an entry
  • PUT: This updates a cache entry

HTTP GET and HEAD are similar methods and can be used to read data from the cache. The HTTP GET method is used to retrieve a cache entry. If the cache entry exists, the GET will return a representation of the data that was stored in the cache and a response code of 200 ( OK ). Otherwise, if the requested cache key does not exist, it will return a 404 ( NOT FOUND ) or 400 ( BAD REQUEST ) response code.

Accessing Infinispan REST Service

If your goal is just to test the REST services, the simplest option is to use existing tools, such as the cURL library, which is a well known command line tool developed for transferring files using a URL syntax, and it supports protocols such as HTTP, HTTPS, FTP, and FTPS, among others.

So let’s say you want to store in Infinispan the following XML under the key 156211:

<catalog>
   <book id="book01">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
</catalog>

First of all, we’ll need to create an application user so that we are allowed to access the rest application:

$ ./add-user.sh -a -u myuser -p "qwer1234!" -ro supervisor,reader,writer

Next, let’s use cURL to insert an entry with a POST:

$ curl -X POST -i  -u "myuser:qwer1234!" -H "Content-type:application/xml" -d "<catalog>
>    <book id="book01">
>       <author>Gambardella, Matthew</author>
>       <title>XML Developer's Guide</title>
>       <genre>Computer</genre>
>       <price>44.95</price>
>       <publish_date>2000-10-01</publish_date>
>       <description>An in-depth look at creating applications 
>       with XML.</description>
>    </book>
> </catalog>" http://localhost:8080/rest/default/156211

Now you can use an HTTP GET to retrieve the XML document for the key 156211:

$ curl -i -u "myuser:qwer1234!" -H "Accept:application/xml" http://localhost:8080/rest/default/156211

Since the release of Infinispan 5.3, you can obtain additional information for a given cache key by appending the extended parameter on the query string, as follows:

GET /cacheName/cacheKey?extended

For example:

$ curl -i -u "myuser:qwer1234!" -H "Accept:application/xml" http://localhost:8080/rest/default/156211?extended

Here’s the information which will be returned along with the XML file:

HTTP/1.1 200 OK
Connection: keep-alive
Cluster-Node-Name: localhost
ETag: "application/xml-1890166279"
Cluster-Server-Address: [127.0.0.1:55200]
Cluster-Primary-Owner: localhost
Last-Modified: Thu, 1 Jan 1970 01:00:00 +0100
Content-Type: application/xml
transfer-encoding: chunked

As you can see, the GET operation returns the following custom headers:

  • Cluster-Primary-Owner: This is the name of the node that is the primary owner of the key
  • Cluster-Node-Name: This is the name of the node that handled the request in JGroups
  • Cluster-Physical-Address: This is the physical address of the JGroups cluster that handled the request

If you want to delete an item from the Cache, you can use the HTTP DELETE method as follows:

$ curl -i -u "myuser:qwer1234!" -X DELETE http://localhost:8080/rest/default/156211

Finally, if you want to update an existing key, you can use the PUT HTTP method as follows:

$ curl -i  -u "myuser:qwer1234!" -X PUT -H "Content-type:application/xml" "<catalog>
>    <book id="book01">
>       <author>Gambardella, Matthew</author>
>       <title>XML Developer's Guide</title>
>       <genre>Computer</genre>
>       <price>48.95</price>
>       <publish_date>2000-10-01</publish_date>
>       <description>An in-depth look at creating applications 
>       with XML.</description>
>    </book>
> </catalog>" http://localhost:8080/rest/default/156211

Other useful options you can append to your query string

  • performAsync: If defined, performAsync will perform asynchronous requests and the operation will return without waiting for data replication.
  • timeToLiveSeconds is the number of seconds before this entry is automatically deleted. If omitted, Infinispan assumes -1 as the default value and the cache entry will not expire.
  • maxIdleTimeSeconds is the number of seconds between the last usage of this entry and the time it will automatically be deleted. If omitted, Infinispan assumes -1 as the default value and the cache entry will not expire as a result of idle time.

Infinispan REST Interface for releases 4 and 5

If you need to access the Infinispan 4,5 data grid from any kind of client (which is able to consume a RESTful service), then Infinispan REST server is what you need !

Download Infinispan server-rest application from here

This tutorial has been tested with the release 4.2.1 of Infinispan and JBoss AS 6

Now unzip the file infinispan-4.2.1.FINAL-server-rest.zip and extract the file infinispan.war which will be deployed on JBoss AS 6.

Please note, due to issues with the VFS of JBoss AS 6, some libraries (namely scala-library-2.8.1.jar) cause the application deployment to fail. In order to solve this issue you can add to your WEB-INF folder of infinispan.war a JBoss AS metadata file (jboss-scanning.xml) which excludes scala library from the deployment.

Here’s jboss-scanning.xml

<scanning xmlns="urn:jboss:scanning:1.0">

   <path name="WEB-INF/lib/scala-library-2.8.1.jar">
     <exclude name="scala" recurse="true"/>
   </path>

</scanning>

This is how your infinispan.war archive will look like:

infinispan.war

–WEB-INF

   lib

   classes

   — web.xml

   — jboss-scanning.xml

Ok. Now launch JBoss AS 6 and point your browser to the infinispan REST server:

http://localhost:8080/inifinispan

You will hit a welcome page which describes how basically the REST server works:

In order to insert elements in the cache, you can issue an HTTP PUT and POST request.

PUT /{cacheName}/{cacheKey}

POST /{cacheName}/{cacheKey}

In order to retrieve elements from the cache you can use HTTP GET request:

GET /{cacheName}/{cacheKey}

In order to delete elements from the cache you can issue an HTTP DELETE request:

DELETE /{cacheName}/{cacheKey}

By default infinispan has just the “___defaultcache” registered, so if you want to try out of the box the REST server, just create a REST client.

Following here is a jQuery client which puts an entry in the cache:

<html>
<head>

<title>jQuery RESTful client performing PUT</title>
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
 
 <script type="text/javascript">

$.ajax({
type: "PUT",
url: '/infinispan/rest/___defaultcache/bucket1',
data: "Data in cache",
success: function(response){
$("#mydiv").html(response);
}
});

 
 </script>
 
</head>
<body>
<div id="mydiv"><p> </p></div>
 </body>
</html>

And here’s the corresponding page which performs a get on the bucket1 key:

<html>
<head>
 <title>jQuery RESTful client performing GET</title>
 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>
 
 <script type="text/javascript">

$.ajax({
type: "GET",
url: '/infinispan/rest/___defaultcache/bucket1',
data: "",
success: function(response){
 $("#mydiv").html(response);

}
});

 
 </script>
 
</head>
<body>
<div id="mydiv"><p> </p></div>
 </body>
</html>

Do I need to download jQuery to run this sample ? no at all. jQuery is accessed on the cloud via <script src=”http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js”></script>. If you cannot reach the web from there, of course download jquery.js and place it on the server’s path.

Getting started with Infinispan Command Line Interface

The version 10 of Infinispan features a brand new server replacing the WildFly-based server with a smaller, leaner implementation. In this tutorial we will check how to use its Command Line Interface to connect to an Infinispan cluster

First off, let’s check some of the highlights of Infinispan 10:

  • Reduced disk (50MB vs 170MB) and memory footprint (18MB vs 40MB at boot)
  • Simpler to configure, since it shares the configuration schema with embedded with server-specific extensions
  • Single-port design: the Hot Rod, REST and management endpoint are now served through a single port (11222)
  • New REST-based API for administration
  • Security enhancements using WildFly Elytron
  • New CLI with data manipulation operations

Let’s cover the last point in this tutorial. Start by grabbing the latest stable version of infinispan from https://infinispan.org/download/

Now unzip the server in two different location of your drive, so we can make up a Cluster of servers:

$ mkdir $HOME/node1
$ mkdir $HOME/node2

$ unzip infinispan-server-10.1.3.Final.zip -d $HOME/node1
$ unzip infinispan-server-10.1.3.Final.zip -d $HOME/node2

Now let’s check the Infinispan configuration which is located in the INFINISPAN_HOME/server/conf/infinispan.xml

For the purpose of learning, let’s add some more caches to the default configuration.

We will therefore change the cache-container element from this:

   <cache-container name="default" statistics="true">
      <transport cluster="${infinispan.cluster.name}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>
      <metrics gauges="true" histograms="true"/>
   </cache-container>

To this:

  <cache-container default-cache="local">
      <transport cluster="${infinispan.cluster.name}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>
      <local-cache name="local"/>
      <invalidation-cache name="invalidation" mode="SYNC"/>
      <replicated-cache name="repl-sync" mode="SYNC"/>
      <distributed-cache name="dist-sync" mode="SYNC"/>
      <metrics gauges="true" histograms="true"/>
   </cache-container>

Cache containers declare one or more local or clustered caches that a Cache Manager controls. In our case we have added an invalidation cache, a replicated cache and a distributed cache.

Now let’s start both servers using the default port for the first server and an offset of 100 in the second one:

$ cd node1 
$ ./server.sh

$ cd node2 
$ ./server.sh -Dinfinispan.socket.binding.port-offset=100

You should be able to see in the Console that a new Cluster view has been created:

14:27:21,345 INFO  (main) [org.infinispan.CLUSTER] ISPN000078: Starting JGroups channel cluster
14:27:21,512 INFO  (main) [org.infinispan.CLUSTER] ISPN000094: Received new cluster view for channel cluster: [localhost-15456|1] (2) [localhost-15456, localhost-15666]

Now let’s launch the Infinispan CLI as follows:

$ ./cli.sh 

Now let’ connect to the first server:

[disconnected]> connect 127.0.0.1:11222

Once logged, we can check the caches which are available. For example the “repl-sync”:

[localhost-15456@cluster//containers/DefaultCacheManager]> describe caches/repl-sync
{
  "replicated-cache" : {
    "mode" : "SYNC",
    "remote-timeout" : 17500,
    "state-transfer" : {
      "timeout" : 60000
    },
    "transaction" : {
      "mode" : "NONE"
    },
    "locking" : {
      "concurrency-level" : 1000,
      "acquire-timeout" : 15000,
      "striping" : false
    },
    "statistics" : true
  }
}

Now let’s navigate into the “caches/repl-sync”. Then, we will add an entry in it:

[localhost-15456@cluster//containers/DefaultCacheManager]> cd caches/repl-sync
[localhost-15456@cluster//containers/DefaultCacheManager/caches/repl-sync]> put key1 value1
[localhost-15456@cluster//containers/DefaultCacheManager/caches/repl-sync]> get key1
value1

As you can see, we have added a key named “key1” with a value of “value1”. To verify that our replication cache did its job, let’s connect to the other node of the Cluster, then we will retrieve the “key1” as we already did for the first server:

[localhost-15456@cluster//containers/DefaultCacheManager/caches/repl-sync]> connect 127.0.0.1:11322

[localhost-15666@cluster//containers/DefaultCacheManager]> cd caches/repl-sync

[localhost-15666@cluster//containers/DefaultCacheManager/caches/repl-sync]> get key1

value1

You can remove single cache entries using the remove command:

[localhost-56512@cluster//containers/DefaultCacheManager/caches/repl-sync]> remove --cache=repl-sync key1
[localhost-56512@cluster//containers/DefaultCacheManager/caches/repl-sync]> get key1
Not Found 

On the other hand, if you want to clear all entries in a cache, then you can use the clearcache command:

[localhost-56512@cluster//containers/DefaultCacheManager/caches/repl-sync]> clearcache repl-sync

You can also create caches from scratch using the CLI. You can do it either using a Cache template, such as the org.infinispan.DIST_SYNC:

[localhost-56512@cluster//containers/DefaultCacheManager/caches/repl-sync]> create cache --template=org.infinispan.DIST_SYNC demo-cache
[localhost-56512@cluster//containers/DefaultCacheManager/caches/repl-sync]> cd ..
[localhost-56512@cluster//containers/DefaultCacheManager/caches]> cd demo-cache
[localhost-56512@cluster//containers/DefaultCacheManager/caches/demo-cache]> describe

{
  "distributed-cache" : {
    "mode" : "SYNC",
    "remote-timeout" : 17500,
    "state-transfer" : {
      "timeout" : 60000
    },
    "transaction" : {
      "mode" : "NONE"
    },
    "locking" : {
      "concurrency-level" : 1000,
      "acquire-timeout" : 15000,
      "striping" : false
    },
    "statistics" : true
  }
}

The other option is to use a configuration file (XML or JSON) for your cache as in this example:

[//containers/default]> create cache --file=demo_cache.xml demo-cache

To terminate the CLI session, just issue the quit command:

[localhost-56512@cluster//containers/DefaultCacheManager/caches/demo-cache]> quit
[ bin]$

On the other hand, if you want to shutdown the server or the whole cluster, you can use the following commands:

  • Use the shutdown server command to stop individual servers, for example:
[//containers/default]> shutdown server server_hostname
  • Use the shutdown cluster command to stop all servers joined to the cluster, for example:
[//containers/default]> shutdown cluster mycluster

Getting Started with Infinispan data grid -the right way

This tutorial has been updated to work with the Infinispan 12.1.7 release.

Infinispan is a distributed in-memory key/value data grid. An in-memory data grid is a form of middleware that stores sets of data for use in one or more applications, primarily in memory. The data is typically distributed across grid nodes in a network. The data stored in the grid can be unstructured.

You can use Infinispan as an embedded Java library and as a language-independent Remote Service that can be accessed over a variety of protocols (Hot Rod, REST, Memcached and WebSockets). It offers advanced functionality too. For example: transactions, events, querying and distributed processing as well as numerous integrations with frameworks such as the JCache API standard, CDI, Hibernate, WildFly, Spring Cache, Spring Session, Lucene, Spark and Hadoop.

How does Infinispan work

Infinispan main API is the org.infinispan.Cache which extends java.util.concurrent.ConcurrentMap and closely resembles javax.cache.Cache from JSR 107. This is the most performant API to use, and should be used for all new projects.

A Cache provides a highly concurrent, optionally distributed data structure with additional features such as:

  • Eviction and expiration support to prevent OutOfMemoryErrors
  • JTA transaction compatibility
  • Event notification via the Listeners
  • Persistence of entries to a CacheStore, to maintain copies that would withstand server failure or restarts

There are several use cases for Infinispan:

  • As a local cache: In this case, Infinispan is used as a fast in-memory cache of frequently accessed data. Using Infinispan is better than using a simple ConcurrentHashMap, since it has additional useful features such as expiration and eviction.
  • As a clustered cache: If your cache doesn’t fit in a single node, or you want to invalidate entries across multiple instances of your application, Infinispan can scale horizontally to several hundred nodes.
  • As a clustering building block for your applications: for example WildFly uses Infinispan to make your applications cluster-aware and get access to features like topology change notifications, cluster communication and clustered execution.
  • As a remote cache: If you want to keep your caching layer separate from your application or you need to make your data available to different applications, possibly even using different languages / platforms, you can use Infinispan Server and its various clients.
  • As a data grid: you can use Infinispan as your primary store and use its powerful features such as transactions, notifications, queries, distributed execution, etc.

How to configure infinispan cache

Cache instances can be retrieved using an appropriate CacheManager, which represents a collection of caches. The CacheManager has many purposes:

  • It acts as a container for caches and controls their lifecycle
  • It manages global configuration and common data structures and resources (e.g. thread pools)
  • It manages clustering
EmbeddedCacheManager cacheManager = new DefaultCacheManager();

This will give us a default local (i.e. non-clustered) CacheManager. Because a CacheManager holds on to some resources which require proper disposal, you also need to make sure you stop it when you don’t need it anymore. This is done with the stop() method, as follows:

cacheManager.stop();

Once a CacheManager has been stopped it, and all resources obtained from it, it cannot be used anymore.

There are two approaches you can use to configure Infinispan, An Infinispan cache may be configured declaratively using an XML configuration file, or programmatically via the fluent configuration API.

The XML approach is the most common approach, as it relies on an XML file, which is parsed when Infinispan starts. It is easy if you have your entire configuration in one place.

Let’s see with a concrete example how to configure a Cache programmatically and using an XML Configuration.

Infinispan Hello World

In this basic example, we will use a Local Cache which is based on a single JVM process and data is stored on the local node only, regardless of whether a cluster has formed:

In order to get started, a simple Java project will do, so let’s use the simplest Maven archetype for this purpose:

$ mvn archetype:generate -DgroupId=com.mastertheboss -DartifactId=infinispan-demo -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

The first thing we need to do is adding Infinispan dependency into the project:

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mastertheboss</groupId>
	<artifactId>infinispan-demo</artifactId>
	<version>1.0-SNAPSHOT</version>

	<name>infinispan-demo</name>
	<url>http://www.mastertheboss.com</url>

	<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>12.1.7.Final</infinispan.version>
	</properties>

	<dependencies>

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

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

        <!-- build configuration here -->
</project>

Now let’s change the App.java class to boostrap a local Infinispan cache:

package com.mastertheboss;

import org.infinispan.Cache;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;

public class App 
{
 	
    public static void main( String[] args )
    {
    	  DefaultCacheManager cacheManager = new DefaultCacheManager();
          // Define local cache configuration
          cacheManager.defineConfiguration("local", new ConfigurationBuilder().build());
          // Obtain the local cache
          Cache<String, String> cache = cacheManager.getCache("local");
          // Store a key
          cache.put("name", "john");
          // Retrieve the value for they key and print it out
          System.out.printf("name = %s\n", cache.get("name"));
          // Stop the cache manager and release all resources
          cacheManager.stop();
      	  
    }
}

You can run the example as follows:

$ mvn clean compile exec:java -Dexec.mainClass=com.mastertheboss.App

Using a declarative Infinispan configuration

In this example, we are defining a named cache “local” using a default configuration, which means a basic local cache. Caches in Infinispan are “named” which means they are identified by a unique name.

Now let’s see another example of a Cache which uses the configuration from an external XML file container in the src/main/resources folder of your Maven project:

package com.mastertheboss;

import org.infinispan.Cache;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;

public class App2 
{
 	
    public static void main( String[] args ) throws Exception
    {
    	  DefaultCacheManager cacheManager = new DefaultCacheManager("config.xml");
     	  Cache cache = cacheManager.getCache();
          cache.put("key", "value");
          System.out.printf("key = %s\n", cache.get("key"));
          cacheManager.stop();
      	  
    }
}

The following example (config.xml) shows the simplest possible configuration for each of the cache types supported by Infinispan:

<infinispan>
   <cache-container default-cache="local">
      <transport cluster="mycluster"/>
      <local-cache name="local"/>
      <invalidation-cache name="invalidation" mode="SYNC"/>
      <replicated-cache name="repl-sync" mode="SYNC"/>
      <distributed-cache name="dist-sync" mode="SYNC"/>
   </cache-container>
</infinispan>

You can find the source code for the above example here: https://github.com/fmarchioni/mastertheboss/tree/master/infinispan/infinispan-demo

Using Listeners to capture events

Infinispan provides a listener API, where clients can register and get notified when cache level events and cache manager level events happen. Events trigger a notification which is dispatched to listeners. Listeners are simple POJO s annotated with @Listener and registered using the methods defined in the Listenable interface. Let’s see how we can register as listener the same Application class:

package com.mastertheboss;

import org.infinispan.Cache;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
public class AppListener {


	   public static void main(String[] args) {

	      DefaultCacheManager cacheManager = new DefaultCacheManager();
	      cacheManager.defineConfiguration("local", new ConfigurationBuilder().build());
	      Cache<String, String> cache = cacheManager.getCache("local");

	      cache.addListener(new MyListener());
	      // Store some values
	      cache.put("key1", "value1");
	      cache.put("key2", "value2");
	      cache.put("key1", "newValue");
	      // Stop the cache manager and release all resources
	      cacheManager.stop();
	   }
	 
	   @Listener
	   public static class MyListener {
	 
	      @CacheEntryCreated
	      public void entryCreated(CacheEntryCreatedEvent<String, String> event) {
	         if (!event.isPre())
	            System.out.printf("Added entry: %s\n", event.getKey());
	      }
	 
	      @CacheEntryModified
	      public void entryModified(CacheEntryModifiedEvent<String, String> event) {
	         if (event.isPre())
	            System.out.printf("Modified entry: %s\n", event.getKey());
	      }
	   }
	}

Configuring Cache expiration

Data stored in an in-memory data grid is usually the result of an expensive computation or has been retrieved from a data-source. If such data can vary with time, it makes sense to set an expiration time, thus making the cache entries mortal. In Infinispan entry expiration can happen in two ways:

  • After some time after the data was inserted into the cache (i.e. lifespan)
  • After some time since it was last accessed (i.e. maximum idle time)

The Cache interface offers overloaded versions of the put() method that allow specifying either or both expiration properties. The following example shows how to insert an entry which will expire after 1 minute:

cache.put(location, weather, 1, TimeUnit.MINUTES);

You can also set a default expiration lifespan for all entries in a cache via the configuration:

configurationBuilder.expiration().lifespan(1, TimeUnit.MINUTES);

or in XML:

<distributed-cache name="mortaldata">
  <expiration lifespan="60000" /> 
</distributed-cache>

Eviction of Cache Data

Out of the box, caches are unbounded, that is they grow indefinitely and it is up to the application to remove unneeded data. In Infinispan you can also set a maximum size for a cache: when this limit is reached, entries accessed least recently will be evicted. For example, here is how to set a maximum of 1000 entries in the cache:

configurationBuilder.eviction().strategy(EvictionStrategy.LRU).type(EvictionType.COUNT).size(1000);

or in XML:

<distributed-cache name="boundedcache">
  <eviction strategy="LIRS" type="COUNT" size="1000" />
</distributed-cache>

You can also set a maximum memory occupation. For example to set an upper bound of 1MB:

configurationBuilder.eviction().strategy(EvictionStrategy.LIRS).type(EvictionType.MEMORY).size(1000000);

And the equivalent in XML:

<distributed-cache name="boundedcache">
  <eviction strategy="LIRS" type="MEMORY" size="1000000" />
</distributed-cache>

How to clear Infinispan cache

There is no single API to remove all data from the in-memory data grid. If the application uses WildFly’s Infinispan cache (which is however not a recommended option), one could undeploy a particular application, which results in clearing the cache. Apart from that, you can use the following method to clear all entries of a Cache:

cache.entrySet().removeIf(entry -> entry.getKey().indexOf("key-") > 0);

Yet another choice is the cache stream API which may be a little more cumbersome to use, but will provide you the best performance of all of the options:

cache.entrySet().parallelStream()  
   .filter(e -> e.getKey().indexOf("key-") > 0)
   .forEach((cache, e) -> cache.remove(e.getKey()));

Generate Sequences with Infinispan Clustered Counters

In this tutorial we will learn how to use Clustered Counters with Infinispan 9.1.0. Cluster Counters are an useful feature of Infinispan which lets you generate unique identifiers in a cluster.

Typically reliable counters are generated through a Database’s Sequence schema but this requires a connection to a Database limiting how the application scales for high throughputs. An Infinispan counter can be a light and reliable alternative. First of all let’s start from the configuration.

Configuring a Counter in Infinispan

In this example, we will show an application which runs Infinispan in Library mode, that is, ships the Infinispan libraries as part of the application and configures Infinispan programmatically. Check the documentation (http://infinispan.org/docs/stable/user_guide/user_guide.html#clustered_counters) for an example of configuring Counters into the server’s configuration file.

Here is the relevant part of our example application:

public void getCacheContainer() {

	GlobalConfigurationBuilder global = new GlobalConfigurationBuilder().clusteredDefault();

	CounterManagerConfigurationBuilder counterBuilder = global.addModule(CounterManagerConfigurationBuilder.class);
	counterBuilder.numOwner(2).reliability(Reliability.AVAILABLE);

	Configuration loc = new ConfigurationBuilder().jmxStatistics().enable().clustering().cacheMode(CacheMode.LOCAL)
			.locking().isolationLevel(IsolationLevel.REPEATABLE_READ).persistence().passivation(false)
			.addSingleFileStore().purgeOnStartup(true)

			.build(); // Builds the Configuration object

	manager = new DefaultCacheManager(global.build(), loc, true);
	System.out.println("Created DefaultCacheManager using library mode");
	cm = EmbeddedCounterManagerFactory.asCounterManager(manager);
	cm.defineCounter("my-counter", CounterConfiguration.builder(CounterType.BOUNDED_STRONG).initialValue(0)
			.lowerBound(0).upperBound(100).storage(Storage.VOLATILE).build());

}

As you can see, in order to bootstrap our counters we need to add to our Clustered configuration an instance of CounterManagerConfigurationBuilder. Through the CounterManagerConfigurationBuilder you will be able to configure:

  • The number of owners which sets the number of counter’s copies to keep cluster-wide.
  • The Reliability which sets the counter’s update behavior in a network partition. Default value is AVAILABLE which means all partitions are able to read and update the counter’s value. The other option is CONSISTENT which means that only the primary partition will be able to Read/Write the counter’s value.

Strong Counters: which performs update locks on the key therefore it lets retrieve the counter’s value after each update. Example:

StrongCounter aCounter = cm.getStrongCounter("strong-counter");
aCounter.incrementAndGet();

Weak Counters: don’t performs update locks on the key therefore it can be used when the result of the update operation is not needed. Example:

WeakCounter aCounter = cm.getWeakCounter("weak-counter");
aCounter.increment().get();

Finally, depending on where the counter is stored, you can have two possible values for Storage:

  • VOLATILE: the counter’s value is only available in memory. The value will be lost when a cluster is shutdown.
  • PERSISTENT: the counter’s value is stored in a private and local persistent store.

Here is the full example of a Servlet, which when invoked generates 10 Strong Counters from a Cache:

package com.mastertheboss.infinispan;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.infinispan.commons.api.BasicCacheContainer;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.counter.EmbeddedCounterManagerFactory;
import org.infinispan.counter.api.CounterConfiguration;
import org.infinispan.counter.api.CounterManager;
import org.infinispan.counter.api.CounterType;
import org.infinispan.counter.api.Storage;
import org.infinispan.counter.api.StrongCounter;
import org.infinispan.counter.configuration.CounterManagerConfigurationBuilder;
import org.infinispan.counter.configuration.Reliability;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.util.concurrent.IsolationLevel;

@WebServlet("/CounterServlet")
public class CounterServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	CounterManager cm;


	public CounterServlet() {
	}

	public void init() throws ServletException {
		getCacheContainer();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		StrongCounter aCounter = cm.getStrongCounter("my-counter");
		try {
			for (int ii = 0; ii < 10; ii++)
				response.getWriter().append("Counter is " + aCounter.incrementAndGet().get());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

	private DefaultCacheManager manager;

	public void getCacheContainer() {

		GlobalConfigurationBuilder global = new GlobalConfigurationBuilder().clusteredDefault();

		CounterManagerConfigurationBuilder counterBuilder = global.addModule(CounterManagerConfigurationBuilder.class);
		counterBuilder.numOwner(2).reliability(Reliability.AVAILABLE);

		Configuration loc = new ConfigurationBuilder().jmxStatistics().enable().clustering().cacheMode(CacheMode.LOCAL)
				.locking().isolationLevel(IsolationLevel.REPEATABLE_READ).persistence().passivation(false)
				.addSingleFileStore().purgeOnStartup(true)

				.build(); // Builds the Configuration object

		manager = new DefaultCacheManager(global.build(), loc, true);
		System.out.println("Created DefaultCacheManager using library mode");
		cm = EmbeddedCounterManagerFactory.asCounterManager(manager);
		cm.defineCounter("my-counter", CounterConfiguration.builder(CounterType.BOUNDED_STRONG).initialValue(0)
				.lowerBound(0).upperBound(100).storage(Storage.VOLATILE).build());

	}
}

Finally, in order to be able to use Counter, you will need to add, besides the Infinispan core dependencies, also the infinispan-clustered-counter in your project:

<dependency>
   <groupId>org.infinispan</groupId>
   <artifactId>infinispan-clustered-counter</artifactId>
   <version>9.1.0.Final</version>
</dependency>

That’s all! enjoy using Clustered counters with Infinispan!

How to iterate over an Infinispan cache

The simplest way to iterate over an Infinispan Cache is by means of iterating over its Set of entries, as in the following example, which uses JDK 1.8 (Lambda expression) style:

cache.entrySet().forEach(entry -> System.out.printf("%s = %s\n", entry.getKey(), entry.getValue()));

 The above example will print cache statistics for the whole cluster. On the other hand, if you want to display Cache entries just for the current node, then you could use the Flag.SKIP_REMOTE_LOOKUP on the Cache object:

cache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP)
.entrySet().forEach(entry -> System.out.printf("%s = %s\n", entry.getKey(), entry.getValue()));

 See the full example: https://github.com/infinispan/infinispan-simple-tutorials/blob/master/distributed/src/main/java/org/infinispan/tutorial/simple/distributed/InfinispanDistributed.java

Monitoring remote Infinispan caches with Listeners

Event listeners can be used with Infinispan caches to notify remote clients of events happening on the Cache, such as CacheEntryCreated, CacheEntryModified or CacheEntryRemoved. In this tutorial we’ve learnt how to monitor a local embedded Infinispan cache: Monitoring WildFly’s Infinispan caches.

The following example shows how a remote client, using the Hot Rod protocol, is able to listen and print events happening on the remote cache:

import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.annotation.ClientCacheEntryCreated;
import org.infinispan.client.hotrod.annotation.ClientCacheEntryModified;
import org.infinispan.client.hotrod.annotation.ClientListener;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.event.ClientCacheEntryCreatedEvent;
import org.infinispan.client.hotrod.event.ClientCacheEntryModifiedEvent;
import org.infinispan.client.hotrod.impl.ConfigurationProperties;
 
public class RemoteListenerDemo {
 
   public static void main(String[] args) throws InterruptedException {
       
      ConfigurationBuilder builder = new ConfigurationBuilder();
      builder.addServer().host("192.168.10.1").port(ConfigurationProperties.DEFAULT_HOTROD_PORT);
       
      RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());
       
      RemoteCache<String, String> cache = cacheManager.getCache();
       
      BasicListener listener = new BasicListener();
      cache.addClientListener(listener);
       
      cache.put("entry1", "value1");
      cache.put("entry2", "value2");
      cache.put("entry3", "value3");
      
      Thread.sleep(1000);
       
      cache.removeClientListener(listener);
       
      cacheManager.stop();
   }
 
   @ClientListener
   public static class BasicListener {
 
      @ClientCacheEntryCreated
      public void entryCreated(ClientCacheEntryCreatedEvent<String> event) {
         System.out.printf("Created %s%n", event.getKey());
      }
 
      @ClientCacheEntryModified
      public void entryModified(ClientCacheEntryModifiedEvent<String> event) {
         System.out.printf("Going to modify %s%n", event.getKey());
      }
 
   }
 
}

The expected output will be:

ClientCacheEntryCreatedEvent(key=entry1,dataVersion=1)
ClientCacheEntryCreatedEvent(key=entry2,dataVersion=1)
ClientCacheEntryCreatedEvent(key=entry3,dataVersion=1)

Please note that the actual value is not sent back to the client for performance reasons. As a matter of fact, receiving remote events has a performance impact, which increases as the cache size grows. In order to avoid bottlenecks to Hot Rod remote clients, it is recommended to filter remote events on the server side.

Running a cluster of Infinispan servers in a Docker container

In this quick tutorial we will learn how to use Docker to run a cluster of Infinispan servers in no time!

First of all, we need to find out the Docker image to pull:

$ docker search infinispan
INDEX       NAME                                                        DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
docker.io   docker.io/jboss/infinispan-server                                                                           2                    [OK]

The list is quite large, however we will rely on the first one (jboss/infinispan-server) which is the official one. You can run it with just one command:

$ docker run -it jboss/infinispan-server

The first time you will execute this command, the image will be pulled from the Docker Hub repository so it will take some time. Once done, it will display the console as you have started it in interactive mode.

From another shell, execute the following command that will retrieve the IP Address of the last started container:

$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' $(docker ps -q)
172.17.0.2

 Great! Now let’s start another Infinispan server re-using the same Docker image:

$ docker run -it jboss/infinispan-server

This time the server will start instantly. As you can see from the server logs, the server has received a new cluster view which includes two servers:

14:56:47,304 INFO  [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-5) ISPN000078: Starting JGroups channel clustered
14:56:47,316 INFO  [org.infinispan.remoting.transport.jgroups.JGroupsTransport] (MSC service thread 1-5) ISPN000094: Received new cluster view for channel clustered: [f4cdbdfc1e53|1] (2) [f4cdbdfc1e53, b643a6cb879b]

Let’s pickup as well the second server’s IP Address:

$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' $(docker ps -q)
172.17.0.3

We can confirm that the Cluster is up and running by executing the ispn-cli.sh shell into one of the containers:

$ docker exec -it $(docker ps -q -l) /opt/jboss/infinispan-server/bin/ispn-cli.sh -c "/subsystem=datagrid-infinispan/cache-container=clustered:read-attribute(name=members)"

{
    "outcome" => "success",
    "result" => "[f4cdbdfc1e53, b643a6cb879b]"
}

Great! We have a cluster of infinispan up and running. Our final test will be connecting to the Cluster. For this purpose we can code a simple Hot Rod Client which contains the cluster view (For more details about Infinispan and Hot Rod see this tutorial: Infinispan 8 tutorial )

Here is our HotRod client class:

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 Main {
 
    private RemoteCacheManager cacheManager;
    private RemoteCache<String, Object> cache;
 
    public Main() {
 
        ConfigurationBuilder builder = new ConfigurationBuilder();
        builder.addServer()
                .host("172.17.0.2")
                .port(Integer.parseInt("11222"));
        
        builder.addServer()
                .host("172.17.0.3")
                .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) {
 
        Main manager = new Main();
 
    }
 
}

As you can see from the Client logs, the Hot Rod library correctly received the Cluster view and updated the cache “default” with a random String:

INFO: ISPN004006: /172.17.0.2:11222 sent new topology view (id=3) containing 2 addresses: [/172.17.0.3:11222, /172.17.0.2:11222]
Dec 29, 2016 5:18:08 PM org.infinispan.client.hotrod.RemoteCacheManager start
INFO: ISPN004021: Infinispan version: 8.0.1.Final
Dec 29, 2016 5:18:08 PM org.infinispan.client.hotrod.impl.protocol.Codec20 readNewTopologyAndHash
INFO: ISPN004006: /172.17.0.2:11222 sent new topology view (id=3) containing 2 addresses: [/172.17.0.3:11222, /172.17.0.2:11222]
Dumping cache Data
==========================
281542e5-85de-44b4-942d-f6bff573a7b2
1752fd2a-21ed-4424-8426-863ab69a4bce
currentNumberOfEntries : 2
hits : 0
removeHits : 0
totalBytesRead : 15
timeSinceStart : 7
removeMisses : 0
totalNumberOfEntries : 1
stores : 1
misses : 0
retrievals : 0
totalBytesWritten : 0

That’s all! You can find the complete list of Jboss.org Docker images available at: http://www.jboss.org/docker/

Developing applications with Infinispan in Library Mode

In this tutorial we want to give some guidelines on developing applications with Infinispan or the supported version named JBoss Data Grid.

First of all, make a bookmark on the following page which contains the demo quickstart applications, configured to run on JBoss Data Grid: https://github.com/jboss-developer/jboss-jdg-quickstarts

The main difference, if you want to run the above examples on the upstream project (Infinispan) is that you need replacing JDG dependencies with Infinispan core libraries.

Before beginning let’s make a distinction between the two different usage modes:

Infinispan modes

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 DataGrid 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).

 

Let’s see of coding an example of Library mode. (If you want an example of a Client-Server application take a look at the following tutorial: Infinispan 8 tutorial )

Developing an applicaiton in Library Mode 

The core part of it is the LocalCacheContainerProvider which defines the CacheContainer configuration programmatically:

 
package com.demo.infinispan.cache;
 
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import org.infinispan.commons.api.BasicCacheContainer;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.global.GlobalConfiguration;
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.util.concurrent.IsolationLevel;
 
 
@ApplicationScoped
public class LocalCacheContainerProvider   {
    

    private BasicCacheContainer manager;

    public BasicCacheContainer getCacheContainer() {
        if (manager == null) {
            GlobalConfiguration glob = new GlobalConfigurationBuilder()
                .nonClusteredDefault()  
                .globalJmxStatistics().enable()  
                .build();  
            Configuration loc = new ConfigurationBuilder()
                .jmxStatistics().enable()  
                .clustering().cacheMode(CacheMode.LOCAL)  
                .locking().isolationLevel(IsolationLevel.REPEATABLE_READ)  
                .persistence().passivation(false).addSingleFileStore().purgeOnStartup(true)  
                .build(); //Builds the Configuration object
            manager = new DefaultCacheManager(glob, loc, true);
           System.out.println("Created DefaultCacheManager using library mode");
        }
        return manager;
    }

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

This CDI class is used to produce a BasicCacheContainer based on a Local Cache mode. For Clustered Cache Containers you can use as cacheMode one of the available modes (Replication/Distribution):

Configuration config = new ConfigurationBuilder()
.clustering().cacheMode(CacheMode.REPL_SYNC)
.build();

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

package com.demo.infinispan.manager;

import org.infinispan.commons.api.BasicCache;

import com.demo.infinispan.cache.LocalCacheContainerProvider;
import com.demo.infinispan.model.Item;

import javax.enterprise.inject.Model;
import javax.inject.Inject;

import java.util.ArrayList;

import java.util.List;

@Model
public class Manager {
	public static final String CACHE_NAME = "itemcache";

	@Inject
	private LocalCacheContainerProvider cacheProvider;

	private BasicCache<String, Item> cache;

	private String key;
	private String value;

	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 Manager() {
	}

	public String save() {
		cache = cacheProvider.getCacheContainer().getCache(CACHE_NAME);
		Item item = new Item();
		item.setKey(key);
		item.setValue(value);
		cache.put(item.getKey(), item);
		return "home";
	}

	public List getCacheList() {
		List<Item> dataList = new ArrayList<Item>();
		if (cache != null)
			dataList.addAll(cache.values());
		return dataList;

	}

	public void removeItem(String key) {
		cache = cacheProvider.getCacheContainer().getCache(CACHE_NAME);
		cache.remove(key);
	}

	public void clear() {
		cache = cacheProvider.getCacheContainer().getCache(CACHE_NAME);
		cache.clear();
	}

}

Completing the application is 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>DataGrid 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>

And here is the application in action:

You can find the full project here: https://github.com/fmarchioni/mastertheboss/tree/master/infinispan-demo

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-8.2" services="export"/>
      </dependencies>
   </deployment>
</jboss-deployment-structure>

(Please notice that for JDG, the slot name is “jdg-6.3”)