/* * Copyright (C) 2010 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.cache.impl.infinispan; import junit.framework.TestCase; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.container.PortalContainer; import org.exoplatform.container.configuration.ConfigurationManager; import org.exoplatform.services.cache.CacheListener; import org.exoplatform.services.cache.CacheListenerContext; import org.exoplatform.services.cache.CacheService; import org.exoplatform.services.cache.CachedObjectSelector; import org.exoplatform.services.cache.ExoCache; import org.exoplatform.services.cache.ExoCacheConfig; import org.exoplatform.services.cache.ExoCacheFactory; import org.exoplatform.services.cache.ExoCacheInitException; import org.exoplatform.services.cache.ObjectCacheInfo; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * @author <a href="mailto:nicolas.filotto@exoplatform.com">Nicolas Filotto</a> * @version $Id$ * */ public class TestAbstractExoCache extends TestCase { CacheService service; AbstractExoCache<Serializable, Object> cache; public TestAbstractExoCache(String name) { super(name); } public void setUp() throws Exception { this.service = (CacheService)PortalContainer.getInstance().getComponentInstanceOfType(CacheService.class); this.cache = (AbstractExoCache<Serializable, Object>)service.getCacheInstance("myCache"); } public void testGet() throws Exception { cache.put(new MyKey("a"), "a"); assertEquals("a", cache.get(new MyKey("a"))); cache.put(new MyKey("a"), "c"); assertEquals("c", cache.get(new MyKey("a"))); cache.remove(new MyKey("a")); assertEquals(null, cache.get(new MyKey("a"))); assertEquals(null, cache.get(new MyKey("x"))); cache.clearCache(); } public void testRemove() throws Exception { cache.put(new MyKey("a"), 1); cache.put(new MyKey("b"), 2); cache.put(new MyKey("c"), 3); assertEquals(3, cache.getCacheSize()); assertEquals(1, cache.remove(new MyKey("a"))); assertEquals(2, cache.getCacheSize()); assertEquals(2, cache.remove(new MyKey("b"))); assertEquals(1, cache.getCacheSize()); assertEquals(null, cache.remove(new MyKey("x"))); assertEquals(1, cache.getCacheSize()); cache.clearCache(); } public void testPutMap() throws Exception { Map<Serializable, Object> values = new HashMap<Serializable, Object>(); values.put(new MyKey("a"), "a"); values.put(new MyKey("b"), "b"); assertEquals(0, cache.getCacheSize()); cache.putMap(values); assertEquals(2, cache.getCacheSize()); values = new HashMap<Serializable, Object>() { private static final long serialVersionUID = 1L; public Set<Entry<Serializable, Object>> entrySet() { Set<Entry<Serializable, Object>> set = new LinkedHashSet<Entry<Serializable, Object>>(super.entrySet()); set.add(new Entry<Serializable, Object>() { public Object setValue(Object paramV) { return null; } public Object getValue() { throw new RuntimeException("An exception"); } public Serializable getKey() { return "c"; } }); return set; } }; values.put(new MyKey("e"), "e"); values.put(new MyKey("d"), "d"); cache.putMap(values); assertEquals(2, cache.getCacheSize()); cache.clearCache(); } public void testGetCachedObjects() throws Exception { cache.put(new MyKey("a"), "a"); cache.put(new MyKey("b"), "b"); cache.put(new MyKey("c"), "c"); cache.put(new MyKey("d"), null); assertEquals(3, cache.getCacheSize()); List<Object> values = cache.getCachedObjects(); assertEquals(3, values.size()); assertTrue(values.contains("a")); assertTrue(values.contains("b")); assertTrue(values.contains("c")); cache.clearCache(); } public void testRemoveCachedObjects() throws Exception { cache.put(new MyKey("a"), "a"); cache.put(new MyKey("b"), "b"); cache.put(new MyKey("c"), "c"); cache.put(new MyKey("d"), null); assertEquals(3, cache.getCacheSize()); List<Object> values = cache.removeCachedObjects(); assertEquals(3, values.size()); assertTrue(values.contains("a")); assertTrue(values.contains("b")); assertTrue(values.contains("c")); assertEquals(0, cache.getCacheSize()); cache.clearCache(); } public void testSelect() throws Exception { cache.put(new MyKey("a"), 1); cache.put(new MyKey("b"), 2); cache.put(new MyKey("c"), 3); final AtomicInteger count = new AtomicInteger(); CachedObjectSelector<Serializable, Object> selector = new CachedObjectSelector<Serializable, Object>() { public void onSelect(ExoCache<? extends Serializable, ? extends Object> cache, Serializable key, ObjectCacheInfo<? extends Object> ocinfo) throws Exception { assertTrue(key.equals(new MyKey("a")) || key.equals(new MyKey("b")) || key.equals(new MyKey("c"))); assertTrue(ocinfo.get().equals(1) || ocinfo.get().equals(2) || ocinfo.get().equals(3)); count.incrementAndGet(); } public boolean select(Serializable key, ObjectCacheInfo<? extends Object> ocinfo) { return true; } }; cache.select(selector); assertEquals(3, count.intValue()); cache.clearCache(); } public void testGetHitsNMisses() throws Exception { int hits = cache.getCacheHit(); int misses = cache.getCacheMiss(); cache.put(new MyKey("a"), "a"); cache.get(new MyKey("a")); cache.remove(new MyKey("a")); cache.get(new MyKey("a")); cache.get(new MyKey("z")); assertEquals(1, cache.getCacheHit() - hits); assertEquals(2, cache.getCacheMiss() - misses); cache.clearCache(); } private ExoCacheFactory getExoCacheFactoryInstance() throws ExoCacheInitException { PortalContainer pc = PortalContainer.getInstance(); return new ExoCacheFactoryImpl((ExoContainerContext)pc.getComponentInstanceOfType(ExoContainerContext.class), "jar:/conf/portal/cache-configuration-template.xml", (ConfigurationManager)pc .getComponentInstanceOfType(ConfigurationManager.class)); } public void testMultiThreading() throws Exception { final ExoCache<Serializable, Object> cache = service.getCacheInstance("test-multi-threading"); final int totalElement = 100; final int totalTimes = 20; int reader = 20; int writer = 10; int remover = 5; int cleaner = 1; final CountDownLatch startSignalWriter = new CountDownLatch(1); final CountDownLatch startSignalOthers = new CountDownLatch(1); final CountDownLatch doneSignal = new CountDownLatch(reader + writer + remover); final List<Exception> errors = Collections.synchronizedList(new ArrayList<Exception>()); for (int i = 0; i < writer; i++) { final int index = i; Thread thread = new Thread() { public void run() { try { startSignalWriter.await(); for (int j = 0; j < totalTimes; j++) { for (int i = 0; i < totalElement; i++) { cache.put(new MyKey("key" + i), "value" + i); } if (index == 0 && j == 0) { // The cache is full, we can launch the others startSignalOthers.countDown(); } sleep(50); } } catch (Exception e) { errors.add(e); } finally { doneSignal.countDown(); } } }; thread.start(); } startSignalWriter.countDown(); for (int i = 0; i < reader; i++) { Thread thread = new Thread() { public void run() { try { startSignalOthers.await(); for (int j = 0; j < totalTimes; j++) { for (int i = 0; i < totalElement; i++) { cache.get(new MyKey("key" + i)); } sleep(50); } } catch (Exception e) { errors.add(e); } finally { doneSignal.countDown(); } } }; thread.start(); } for (int i = 0; i < remover; i++) { Thread thread = new Thread() { public void run() { try { startSignalOthers.await(); for (int j = 0; j < totalTimes; j++) { for (int i = 0; i < totalElement; i++) { cache.remove(new MyKey("key" + i)); } sleep(50); } } catch (Exception e) { errors.add(e); } finally { doneSignal.countDown(); } } }; thread.start(); } doneSignal.await(); for (int i = 0; i < totalElement; i++) { cache.put(new MyKey("key" + i), "value" + i); } assertEquals(totalElement, cache.getCacheSize()); final CountDownLatch startSignal = new CountDownLatch(1); final CountDownLatch doneSignal2 = new CountDownLatch(writer + cleaner); for (int i = 0; i < writer; i++) { Thread thread = new Thread() { public void run() { try { startSignal.await(); for (int j = 0; j < totalTimes; j++) { for (int i = 0; i < totalElement; i++) { cache.put(new MyKey("key" + i), "value" + i); } sleep(50); } } catch (Exception e) { errors.add(e); } finally { doneSignal2.countDown(); } } }; thread.start(); } for (int i = 0; i < cleaner; i++) { Thread thread = new Thread() { public void run() { try { startSignal.await(); for (int j = 0; j < totalTimes; j++) { sleep(150); cache.clearCache(); } } catch (Exception e) { errors.add(e); } finally { doneSignal2.countDown(); } } }; thread.start(); } cache.clearCache(); assertEquals(0, cache.getCacheSize()); if (!errors.isEmpty()) { for (Exception e : errors) { e.printStackTrace(); } throw errors.get(0); } cache.clearCache(); } public static class MyCacheListener implements CacheListener<Serializable, Object> { public int clearCache; public int expire; public int get; public int put; public int remove; public void onClearCache(ExoCache<Serializable, Object> cache) throws Exception { clearCache++; } public void onExpire(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception { expire++; } public void onGet(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception { get++; } public void onPut(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception { put++; } public void onRemove(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception { remove++; } public void onClearCache(CacheListenerContext context) throws Exception { clearCache++; } public void onExpire(CacheListenerContext context, Serializable key, Object obj) throws Exception { expire++; } public void onGet(CacheListenerContext context, Serializable key, Object obj) throws Exception { get++; } public void onPut(CacheListenerContext context, Serializable key, Object obj) throws Exception { put++; } public void onRemove(CacheListenerContext context, Serializable key, Object obj) throws Exception { remove++; } } public static class MyKey implements Serializable { private static final long serialVersionUID = 1L; public String value; public MyKey(String value) { this.value = value; } @Override public boolean equals(Object paramObject) { return paramObject instanceof MyKey && ((MyKey)paramObject).value.equals(value); } @Override public int hashCode() { return value.hashCode(); } @Override public String toString() { return value; } } /** * WARNING: For Linux distributions the following JVM parameter must be set to true: java.net.preferIPv4Stack */ @SuppressWarnings("unchecked") public void testDistributedCache() throws Exception { // If the cache is still alive this test fails due to a TimeoutException. // cache.cache.getCacheManager().stop(); ExoCacheConfig config = new ExoCacheConfig(); config.setName("MyCacheDistributed"); config.setMaxSize(8); config.setLiveTime(1); config.setImplementation("LRU"); config.setReplicated(true); ExoCacheConfig config2 = new ExoCacheConfig(); config2.setName("MyCacheDistributed2"); config2.setMaxSize(8); config2.setLiveTime(1); config2.setImplementation("LRU"); config2.setReplicated(true); AbstractExoCache<Serializable, Object> cache1 = (AbstractExoCache<Serializable, Object>)getExoCacheFactoryInstance().createCache(config); MyCacheListener listener1 = new MyCacheListener(); cache1.addCacheListener(listener1); AbstractExoCache<Serializable, Object> cache2 = (AbstractExoCache<Serializable, Object>)getExoCacheFactoryInstance().createCache(config); MyCacheListener listener2 = new MyCacheListener(); cache2.addCacheListener(listener2); // Cache 3 creates a conflict with cache 1 and 2 // AbstractExoCache<Serializable, Object> cache3 = // (AbstractExoCache<Serializable, Object>)getExoCacheFactoryInstance().createCache(config2); // MyCacheListener listener3 = new MyCacheListener(); // cache3.addCacheListener(listener3); try { cache1.put(new MyKey("a"), "b"); assertEquals(1, cache1.getCacheSize()); assertEquals("b", cache2.get(new MyKey("a"))); assertEquals(1, cache2.getCacheSize()); // assertEquals(0, cache3.getCacheSize()); assertEquals(1, listener1.put); assertEquals(1, listener2.put); // assertEquals(0, listener3.put); assertEquals(0, listener1.get); assertEquals(1, listener2.get); // assertEquals(0, listener3.get); cache2.put(new MyKey("b"), "c"); assertEquals(2, cache1.getCacheSize()); assertEquals(2, cache2.getCacheSize()); assertEquals("c", cache1.get(new MyKey("b"))); // assertEquals(0, cache3.getCacheSize()); assertEquals(2, listener1.put); assertEquals(2, listener2.put); // assertEquals(0, listener3.put); assertEquals(1, listener1.get); assertEquals(1, listener2.get); // assertEquals(0, listener3.get); // cache3.put(new MyKey("c"), "d"); assertEquals(2, cache1.getCacheSize()); assertEquals(2, cache2.getCacheSize()); // assertEquals(1, cache3.getCacheSize()); // assertEquals("d", cache3.get(new MyKey("c"))); assertEquals(2, listener1.put); assertEquals(2, listener2.put); // assertEquals(1, listener3.put); assertEquals(1, listener1.get); assertEquals(1, listener2.get); // assertEquals(1, listener3.get); cache2.put(new MyKey("a"), "a"); assertEquals(2, cache1.getCacheSize()); assertEquals(2, cache2.getCacheSize()); assertEquals("a", cache1.get(new MyKey("a"))); assertEquals(3, listener1.put); assertEquals(3, listener2.put); // assertEquals(1, listener3.put); assertEquals(2, listener1.get); assertEquals(1, listener2.get); // assertEquals(1, listener3.get); cache2.remove(new MyKey("a")); assertEquals(1, cache1.getCacheSize()); assertEquals(1, cache2.getCacheSize()); assertEquals(3, listener1.put); assertEquals(3, listener2.put); // assertEquals(1, listener3.put); assertEquals(2, listener1.get); assertEquals(1, listener2.get); // assertEquals(1, listener3.get); assertEquals(1, listener1.remove); assertEquals(1, listener2.remove); // assertEquals(0, listener3.remove); cache1.put(new MyKey("c"), "c"); cache1.clearCache(); assertEquals(0, cache1.getCacheSize()); assertEquals(null, cache1.get(new MyKey("b"))); assertEquals("c", cache2.get(new MyKey("b"))); assertEquals("c", cache2.get(new MyKey("c"))); assertEquals(2, cache2.getCacheSize()); assertEquals(4, listener1.put); assertEquals(4, listener2.put); // assertEquals(1, listener3.put); assertEquals(3, listener1.get); assertEquals(3, listener2.get); // assertEquals(1, listener3.get); assertEquals(1, listener1.remove); assertEquals(1, listener2.remove); // assertEquals(0, listener3.remove); assertEquals(1, listener1.clearCache); assertEquals(0, listener2.clearCache); // assertEquals(0, listener3.clearCache); Map<Serializable, Object> values = new HashMap<Serializable, Object>(); values.put(new MyKey("a"), "a"); values.put(new MyKey("b"), "b"); cache1.putMap(values); assertEquals(2, cache1.getCacheSize()); Thread.sleep(40); assertEquals("a", cache2.get(new MyKey("a"))); assertEquals("b", cache2.get(new MyKey("b"))); assertEquals(3, cache2.getCacheSize()); assertEquals(6, listener1.put); assertEquals(6, listener2.put); // assertEquals(1, listener3.put); assertEquals(3, listener1.get); assertEquals(5, listener2.get); // assertEquals(1, listener3.get); assertEquals(1, listener1.remove); assertEquals(1, listener2.remove); // assertEquals(0, listener3.remove); assertEquals(1, listener1.clearCache); assertEquals(0, listener2.clearCache); // assertEquals(0, listener3.clearCache); values = new HashMap<Serializable, Object>() { private static final long serialVersionUID = 1L; public Set<Entry<Serializable, Object>> entrySet() { Set<Entry<Serializable, Object>> set = new LinkedHashSet<Entry<Serializable, Object>>(super.entrySet()); set.add(new Entry<Serializable, Object>() { public Object setValue(Object paramV) { return null; } public Object getValue() { throw new RuntimeException("An exception"); } public Serializable getKey() { return "c"; } }); return set; } }; values.put(new MyKey("e"), "e"); values.put(new MyKey("d"), "d"); cache1.putMap(values); assertEquals(2, cache1.getCacheSize()); assertEquals(3, cache2.getCacheSize()); // assertEquals(1, cache3.getCacheSize()); assertEquals(6, listener1.put); assertEquals(6, listener2.put); // assertEquals(1, listener3.put); assertEquals(3, listener1.get); assertEquals(5, listener2.get); // assertEquals(1, listener3.get); assertEquals(1, listener1.remove); assertEquals(1, listener2.remove); // assertEquals(0, listener3.remove); assertEquals(1, listener1.clearCache); assertEquals(0, listener2.clearCache); // assertEquals(0, listener3.clearCache); assertEquals(0, listener1.expire); assertEquals(0, listener2.expire); // assertEquals(0, listener3.expire); Thread.sleep(5600); // The values are evicted lazily when we call it cache1.get(new MyKey("a")); cache1.get(new MyKey("b")); cache2.get(new MyKey("a")); cache2.get(new MyKey("b")); cache2.get(new MyKey("c")); assertEquals(0, cache1.getCacheSize()); assertEquals(0, cache2.getCacheSize()); // assertEquals(0, cache3.getCacheSize()); assertEquals(6, listener1.put); assertEquals(6, listener2.put); // assertEquals(1, listener3.put); assertEquals(5, listener1.get); assertEquals(8, listener2.get); // assertEquals(1, listener3.get); assertEquals(1, listener1.remove); assertEquals(1, listener2.remove); // assertEquals(0, listener3.remove); assertEquals(1, listener1.clearCache); assertEquals(0, listener2.clearCache); // assertEquals(0, listener3.clearCache); // Expiration events are not triggered in infinispan // assertEquals(2, listener1.expire); // assertEquals(3, listener2.expire); // assertEquals(1, listener3.expire); } finally { cache1.cache.getCacheManager().stop(); cache2.cache.getCacheManager().stop(); // cache3.cache.getCacheManager().stop(); } } public void testPut() throws Exception { cache.put(new MyKey("a"), "a"); cache.put(new MyKey("b"), "b"); cache.put(new MyKey("c"), "c"); assertEquals(3, cache.getCacheSize()); cache.put(new MyKey("a"), "c"); assertEquals(3, cache.getCacheSize()); cache.put(new MyKey("d"), "c"); assertEquals(4, cache.getCacheSize()); cache.clearCache(); } public void testClearCache() throws Exception { cache.put(new MyKey("a"), "a"); cache.put(new MyKey("b"), "b"); cache.put(new MyKey("c"), "c"); assertTrue(cache.getCacheSize() > 0); cache.clearCache(); assertTrue(cache.getCacheSize() == 0); cache.clearCache(); } }