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); + } + +}