diff --git a/libraries/pom.xml b/libraries/pom.xml
index fa1839010c..64cc9c6a84 100644
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -649,6 +649,11 @@
google-http-client-gson
${googleclient.version}
+
+ org.infinispan
+ infinispan-core
+ ${infinispan.version}
+
@@ -811,5 +816,6 @@
3.0.14
8.5.24
2.2.0
+ 9.1.5.Final
-
\ No newline at end of file
+
diff --git a/libraries/src/main/java/com/baeldung/infinispan/CacheConfiguration.java b/libraries/src/main/java/com/baeldung/infinispan/CacheConfiguration.java
new file mode 100644
index 0000000000..bf214458f3
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/infinispan/CacheConfiguration.java
@@ -0,0 +1,85 @@
+package com.baeldung.infinispan;
+
+import com.baeldung.infinispan.listener.CacheListener;
+import org.infinispan.Cache;
+import org.infinispan.configuration.cache.Configuration;
+import org.infinispan.configuration.cache.ConfigurationBuilder;
+import org.infinispan.eviction.EvictionType;
+import org.infinispan.manager.DefaultCacheManager;
+import org.infinispan.transaction.LockingMode;
+import org.infinispan.transaction.TransactionMode;
+
+import java.util.concurrent.TimeUnit;
+
+public class CacheConfiguration {
+
+ public static final String SIMPLE_HELLO_WORLD_CACHE = "simple-hello-world-cache";
+ public static final String EXPIRING_HELLO_WORLD_CACHE = "expiring-hello-world-cache";
+ public static final String EVICTING_HELLO_WORLD_CACHE = "evicting-hello-world-cache";
+ public static final String PASSIVATING_HELLO_WORLD_CACHE = "passivating-hello-world-cache";
+ public static final String TRANSACTIONAL_CACHE = "transactional-cache";
+
+ public DefaultCacheManager cacheManager() {
+ DefaultCacheManager cacheManager = new DefaultCacheManager();
+ return cacheManager;
+ }
+
+ public Cache transactionalCache(DefaultCacheManager cacheManager, CacheListener listener) {
+ return this.buildCache(TRANSACTIONAL_CACHE, cacheManager, listener, transactionalConfiguration());
+ }
+
+ public Cache simpleHelloWorldCache(DefaultCacheManager cacheManager, CacheListener listener) {
+ return this.buildCache(SIMPLE_HELLO_WORLD_CACHE, cacheManager, listener, new ConfigurationBuilder().build());
+ }
+
+ public Cache expiringHelloWorldCache(DefaultCacheManager cacheManager, CacheListener listener) {
+ return this.buildCache(EXPIRING_HELLO_WORLD_CACHE, cacheManager, listener, expiringConfiguration());
+ }
+
+ public Cache evictingHelloWorldCache(DefaultCacheManager cacheManager, CacheListener listener) {
+ return this.buildCache(EVICTING_HELLO_WORLD_CACHE, cacheManager, listener, evictingConfiguration());
+ }
+
+ public Cache passivatingHelloWorldCache(DefaultCacheManager cacheManager, CacheListener listener) {
+ return this.buildCache(PASSIVATING_HELLO_WORLD_CACHE, cacheManager, listener, passivatingConfiguration());
+ }
+
+ private Cache buildCache(String cacheName, DefaultCacheManager cacheManager,
+ CacheListener listener, Configuration configuration) {
+
+ cacheManager.defineConfiguration(cacheName, configuration);
+ Cache cache = cacheManager.getCache(cacheName);
+ cache.addListener(listener);
+ return cache;
+ }
+
+ private Configuration expiringConfiguration() {
+ return new ConfigurationBuilder().expiration().lifespan(1, TimeUnit.SECONDS)
+ .build();
+ }
+
+ private Configuration evictingConfiguration() {
+ return new ConfigurationBuilder()
+ .memory().evictionType(EvictionType.COUNT).size(1)
+ .build();
+ }
+
+ private Configuration passivatingConfiguration() {
+ return new ConfigurationBuilder()
+ .memory().evictionType(EvictionType.COUNT).size(1)
+ .persistence()
+ .passivation(true)
+ .addSingleFileStore()
+ .purgeOnStartup(true)
+ .location(System.getProperty("java.io.tmpdir"))
+ .build();
+ }
+
+ private Configuration transactionalConfiguration() {
+ return new ConfigurationBuilder()
+ .transaction().transactionMode(TransactionMode.TRANSACTIONAL)
+ .lockingMode(LockingMode.PESSIMISTIC)
+ .build();
+ }
+
+}
diff --git a/libraries/src/main/java/com/baeldung/infinispan/listener/CacheListener.java b/libraries/src/main/java/com/baeldung/infinispan/listener/CacheListener.java
new file mode 100644
index 0000000000..2f6536ad87
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/infinispan/listener/CacheListener.java
@@ -0,0 +1,55 @@
+package com.baeldung.infinispan.listener;
+
+import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.*;
+import org.infinispan.notifications.cachelistener.event.*;
+
+@Listener
+public class CacheListener {
+
+ @CacheEntryCreated
+ public void entryCreated(CacheEntryCreatedEvent event) {
+ this.printLog("Adding key '" + event.getKey() + "' to cache", event);
+ }
+
+ @CacheEntryExpired
+ public void entryExpired(CacheEntryExpiredEvent event) {
+ this.printLog("Expiring key '" + event.getKey() + "' from cache", event);
+ }
+
+ @CacheEntryVisited
+ public void entryVisited(CacheEntryVisitedEvent event) {
+ this.printLog("Key '" + event.getKey() + "' was visited", event);
+ }
+
+ @CacheEntryActivated
+ public void entryActivated(CacheEntryActivatedEvent event) {
+ this.printLog("Activating key '" + event.getKey() + "' on cache", event);
+ }
+
+ @CacheEntryPassivated
+ public void entryPassivated(CacheEntryPassivatedEvent event) {
+ this.printLog("Passivating key '" + event.getKey() + "' from cache", event);
+ }
+
+ @CacheEntryLoaded
+ public void entryLoaded(CacheEntryLoadedEvent event) {
+ this.printLog("Loading key '" + event.getKey() + "' to cache", event);
+ }
+
+ @CacheEntriesEvicted
+ public void entriesEvicted(CacheEntriesEvictedEvent event) {
+ final StringBuilder builder = new StringBuilder();
+ event.getEntries().entrySet().forEach((e) ->
+ builder.append(e.getKey() + ", ")
+ );
+ System.out.println("Evicting following entries from cache: " + builder.toString());
+ }
+
+ private void printLog(String log, CacheEntryEvent event) {
+ if (!event.isPre()) {
+ System.out.println(log);
+ }
+ }
+
+}
diff --git a/libraries/src/main/java/com/baeldung/infinispan/repository/HelloWorldRepository.java b/libraries/src/main/java/com/baeldung/infinispan/repository/HelloWorldRepository.java
new file mode 100644
index 0000000000..85c0d539a3
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/infinispan/repository/HelloWorldRepository.java
@@ -0,0 +1,15 @@
+package com.baeldung.infinispan.repository;
+
+public class HelloWorldRepository {
+
+ public String getHelloWorld() {
+ try {
+ System.out.println("Executing some heavy query");
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return "Hello World!";
+ }
+
+}
diff --git a/libraries/src/main/java/com/baeldung/infinispan/service/HelloWorldService.java b/libraries/src/main/java/com/baeldung/infinispan/service/HelloWorldService.java
new file mode 100644
index 0000000000..0d1ffb4168
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/infinispan/service/HelloWorldService.java
@@ -0,0 +1,90 @@
+package com.baeldung.infinispan.service;
+
+import com.baeldung.infinispan.listener.CacheListener;
+import com.baeldung.infinispan.repository.HelloWorldRepository;
+import org.infinispan.Cache;
+
+import java.util.concurrent.TimeUnit;
+
+public class HelloWorldService {
+
+ private final HelloWorldRepository repository;
+
+ private final Cache simpleHelloWorldCache;
+ private final Cache expiringHelloWorldCache;
+ private final Cache evictingHelloWorldCache;
+ private final Cache passivatingHelloWorldCache;
+
+ public HelloWorldService(HelloWorldRepository repository, CacheListener listener,
+ Cache simpleHelloWorldCache,
+ Cache expiringHelloWorldCache,
+ Cache evictingHelloWorldCache,
+ Cache passivatingHelloWorldCache) {
+
+ this.repository = repository;
+
+ this.simpleHelloWorldCache = simpleHelloWorldCache;
+ this.expiringHelloWorldCache = expiringHelloWorldCache;
+ this.evictingHelloWorldCache = evictingHelloWorldCache;
+ this.passivatingHelloWorldCache = passivatingHelloWorldCache;
+ }
+
+ public String findSimpleHelloWorld() {
+ String cacheKey = "simple-hello";
+ String helloWorld = simpleHelloWorldCache.get(cacheKey);
+ if (helloWorld == null) {
+ helloWorld = repository.getHelloWorld();
+ simpleHelloWorldCache.put(cacheKey, helloWorld);
+ }
+ return helloWorld;
+ }
+
+ public String findExpiringHelloWorld() {
+ String cacheKey = "expiring-hello";
+ String helloWorld = simpleHelloWorldCache.get(cacheKey);
+ if (helloWorld == null) {
+ helloWorld = repository.getHelloWorld();
+ simpleHelloWorldCache.put(cacheKey, helloWorld, 1, TimeUnit.SECONDS);
+ }
+ return helloWorld;
+ }
+
+ public String findIdleHelloWorld() {
+ String cacheKey = "idle-hello";
+ String helloWorld = simpleHelloWorldCache.get(cacheKey);
+ if (helloWorld == null) {
+ helloWorld = repository.getHelloWorld();
+ simpleHelloWorldCache.put(cacheKey, helloWorld, -1, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);
+ }
+ return helloWorld;
+ }
+
+ public String findSimpleHelloWorldInExpiringCache() {
+ String cacheKey = "simple-hello";
+ String helloWorld = expiringHelloWorldCache.get(cacheKey);
+ if (helloWorld == null) {
+ helloWorld = repository.getHelloWorld();
+ expiringHelloWorldCache.put(cacheKey, helloWorld);
+ }
+ return helloWorld;
+ }
+
+ public String findEvictingHelloWorld(String key) {
+ String value = evictingHelloWorldCache.get(key);
+ if(value == null) {
+ value = repository.getHelloWorld();
+ evictingHelloWorldCache.put(key, value);
+ }
+ return value;
+ }
+
+ public String findPassivatingHelloWorld(String key) {
+ String value = passivatingHelloWorldCache.get(key);
+ if(value == null) {
+ value = repository.getHelloWorld();
+ passivatingHelloWorldCache.put(key, value);
+ }
+ return value;
+ }
+
+}
diff --git a/libraries/src/main/java/com/baeldung/infinispan/service/TransactionalService.java b/libraries/src/main/java/com/baeldung/infinispan/service/TransactionalService.java
new file mode 100644
index 0000000000..b0dbf5475f
--- /dev/null
+++ b/libraries/src/main/java/com/baeldung/infinispan/service/TransactionalService.java
@@ -0,0 +1,57 @@
+package com.baeldung.infinispan.service;
+
+import org.infinispan.Cache;
+import org.springframework.util.StopWatch;
+
+import javax.transaction.TransactionManager;
+
+public class TransactionalService {
+
+ private final Cache transactionalCache;
+
+ private static final String KEY = "key";
+
+ public TransactionalService(Cache transactionalCache) {
+ this.transactionalCache = transactionalCache;
+
+ transactionalCache.put(KEY, 0);
+ }
+
+ public Integer getQuickHowManyVisits() {
+ try {
+ TransactionManager tm = transactionalCache.getAdvancedCache().getTransactionManager();
+ tm.begin();
+ Integer howManyVisits = transactionalCache.get(KEY);
+ howManyVisits++;
+ System.out.println("Ill try to set HowManyVisits to " + howManyVisits);
+ StopWatch watch = new StopWatch();
+ watch.start();
+ transactionalCache.put(KEY, howManyVisits);
+ watch.stop();
+ System.out.println("I was able to set HowManyVisits to " + howManyVisits +
+ " after waiting " + watch.getTotalTimeSeconds() + " seconds");
+
+ tm.commit();
+ return howManyVisits;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ public void startBackgroundBatch() {
+ try {
+ TransactionManager tm = transactionalCache.getAdvancedCache().getTransactionManager();
+ tm.begin();
+ transactionalCache.put(KEY, 1000);
+ System.out.println("HowManyVisits should now be 1000, " +
+ "but we are holding the transaction");
+ Thread.sleep(1000L);
+ tm.rollback();
+ System.out.println("The slow batch suffered a rollback");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/libraries/src/test/java/com/baeldung/infinispan/ConfigurationTest.java b/libraries/src/test/java/com/baeldung/infinispan/ConfigurationTest.java
new file mode 100644
index 0000000000..b05314491b
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/infinispan/ConfigurationTest.java
@@ -0,0 +1,56 @@
+package com.baeldung.infinispan;
+
+import com.baeldung.infinispan.listener.CacheListener;
+import com.baeldung.infinispan.repository.HelloWorldRepository;
+import com.baeldung.infinispan.service.HelloWorldService;
+import com.baeldung.infinispan.service.TransactionalService;
+import org.infinispan.Cache;
+import org.infinispan.manager.DefaultCacheManager;
+import org.junit.After;
+import org.junit.Before;
+
+public class ConfigurationTest {
+
+ private DefaultCacheManager cacheManager;
+
+ private HelloWorldRepository repository = new HelloWorldRepository();
+
+ protected HelloWorldService helloWorldService;
+ protected TransactionalService transactionalService;
+
+ @Before
+ public void setup() {
+ CacheConfiguration configuration = new CacheConfiguration();
+ CacheListener listener = new CacheListener();
+
+ cacheManager = configuration.cacheManager();
+
+ Cache transactionalCache =
+ configuration.transactionalCache(cacheManager, listener);
+
+ Cache simpleHelloWorldCache =
+ configuration.simpleHelloWorldCache(cacheManager, listener);
+
+ Cache expiringHelloWorldCache =
+ configuration.expiringHelloWorldCache(cacheManager, listener);
+
+ Cache evictingHelloWorldCache =
+ configuration.evictingHelloWorldCache(cacheManager, listener);
+
+ Cache passivatingHelloWorldCache =
+ configuration.passivatingHelloWorldCache(cacheManager, listener);
+
+ this.helloWorldService = new HelloWorldService(repository,
+ listener, simpleHelloWorldCache, expiringHelloWorldCache, evictingHelloWorldCache,
+ passivatingHelloWorldCache);
+
+ this.transactionalService = new TransactionalService(transactionalCache);
+
+ }
+
+ @After
+ public void tearDown() {
+ cacheManager.stop();
+ }
+
+}
diff --git a/libraries/src/test/java/com/baeldung/infinispan/service/HelloWorldServiceUnitTest.java b/libraries/src/test/java/com/baeldung/infinispan/service/HelloWorldServiceUnitTest.java
new file mode 100644
index 0000000000..bda2e6886b
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/infinispan/service/HelloWorldServiceUnitTest.java
@@ -0,0 +1,105 @@
+package com.baeldung.infinispan.service;
+
+import com.baeldung.infinispan.ConfigurationTest;
+import org.junit.Test;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+public class HelloWorldServiceUnitTest extends ConfigurationTest {
+
+ @Test
+ public void whenGetIsCalledTwoTimes_thenTheSecondShouldHitTheCache() {
+ long milis = System.currentTimeMillis();
+ helloWorldService.findSimpleHelloWorld();
+ long executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findSimpleHelloWorld();
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isLessThan(100);
+ }
+
+ @Test
+ public void whenGetIsCalledTwoTimesQuickly_thenTheSecondShouldHitTheCache() {
+ long milis = System.currentTimeMillis();
+ helloWorldService.findExpiringHelloWorld();
+ long executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findExpiringHelloWorld();
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isLessThan(100);
+ }
+
+ @Test
+ public void whenGetIsCalledTwoTimesSparsely_thenNeitherShouldHitTheCache()
+ throws InterruptedException {
+
+ long milis = System.currentTimeMillis();
+ helloWorldService.findSimpleHelloWorldInExpiringCache();
+ long executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ Thread.sleep(1100);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findSimpleHelloWorldInExpiringCache();
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+ }
+
+ @Test
+ public void givenOneEntryIsConfigured_whenTwoAreAdded_thenFirstShouldntBeAvailable()
+ throws InterruptedException {
+
+ long milis = System.currentTimeMillis();
+ helloWorldService.findEvictingHelloWorld("key 1");
+ long executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findEvictingHelloWorld("key 2");
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findEvictingHelloWorld("key 1");
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+ }
+
+ @Test
+ public void givenOneEntryIsConfigured_whenTwoAreAdded_thenTheFirstShouldBeAvailable()
+ throws InterruptedException {
+
+ long milis = System.currentTimeMillis();
+ helloWorldService.findPassivatingHelloWorld("key 1");
+ long executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findPassivatingHelloWorld("key 2");
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThanOrEqualTo(1000);
+
+ milis = System.currentTimeMillis();
+ helloWorldService.findPassivatingHelloWorld("key 1");
+ executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isLessThan(100);
+ }
+
+}
diff --git a/libraries/src/test/java/com/baeldung/infinispan/service/TransactionalServiceUnitTest.java b/libraries/src/test/java/com/baeldung/infinispan/service/TransactionalServiceUnitTest.java
new file mode 100644
index 0000000000..f529222079
--- /dev/null
+++ b/libraries/src/test/java/com/baeldung/infinispan/service/TransactionalServiceUnitTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.infinispan.service;
+
+import com.baeldung.infinispan.ConfigurationTest;
+import org.junit.Test;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+
+public class TransactionalServiceUnitTest extends ConfigurationTest {
+
+ @Test
+ public void whenLockingAnEntry_thenItShouldBeInaccessible() throws InterruptedException {
+ Runnable backGroundJob = () -> transactionalService.startBackgroundBatch();
+ Thread backgroundThread = new Thread(backGroundJob);
+ transactionalService.getQuickHowManyVisits();
+ backgroundThread.start();
+ Thread.sleep(100); //lets wait our thread warm up
+ long milis = System.currentTimeMillis();
+ transactionalService.getQuickHowManyVisits();
+ long executionTime = System.currentTimeMillis() - milis;
+
+ assertThat(executionTime).isGreaterThan(500).isLessThan(1000);
+ }
+
+}