This tutorial has been updated to work with the lastes Infinispan 9 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 such as 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>9.4.5.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 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>

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()));
0
0
0
s2smodern

Related articles available on mastertheboss.com

Infinispan tutorial part 2

In the first tutorial we have covered some basic concepts about I

Infinispan RESTful interface

This is an update for Infinispan REST tutorial which is based on

Develop a clustered application with Infinispan Data Grid

In this tutorial we will use Infinispan Data Grid Platform to sto

Using Infinispan with WildFly

The Infinispan subsystem provides caching support for HA services

Using Infinispan Query API

Applications using a NoSQL storage often need to query data using

Monitoring WildFly's Infinispan caches

In this tutorial we will learn how to monitor the Embedded Infini