/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * 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.infinispan.loaders; import org.infinispan.Cache; import org.infinispan.config.CacheLoaderManagerConfig; import org.infinispan.config.Configuration; import org.infinispan.container.entries.InternalCacheEntry; import org.infinispan.test.fwk.TestInternalCacheEntryFactory; import org.infinispan.context.Flag; import org.infinispan.loaders.decorators.ChainingCacheStore; import org.infinispan.loaders.dummy.DummyInMemoryCacheStore; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.test.SingleCacheManagerTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.testng.annotations.Test; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collections; import java.util.Set; import static org.testng.Assert.assertEquals; /** * A test to ensure stuff from a cache store is not loaded unnecessarily if it already exists in memory, * or if the Flag.SKIP_CACHE_STORE is applied. * * @author Manik Surtani * @author Sanne Grinovero * @version 4.1 */ @Test(testName = "loaders.UnnnecessaryLoadingTest", groups = "functional", sequential = true) public class UnnnecessaryLoadingTest extends SingleCacheManagerTest { CacheStore store; @Override protected EmbeddedCacheManager createCacheManager() throws Exception { Configuration cfg = getDefaultStandaloneConfig(true); cfg.setInvocationBatchingEnabled(true); CacheLoaderManagerConfig clmc = new CacheLoaderManagerConfig(); clmc.addCacheLoaderConfig(new CountingCacheStoreConfig()); clmc.addCacheLoaderConfig(new DummyInMemoryCacheStore.Cfg()); cfg.setCacheLoaderManagerConfig(clmc); EmbeddedCacheManager cm = TestCacheManagerFactory.createCacheManager(cfg); cache = cm.getCache(); store = TestingUtil.extractComponent(cache, CacheLoaderManager.class).getCacheStore(); return cm; } public void testRepeatedLoads() throws CacheLoaderException { CountingCacheStore countingCS = getCountingCacheStore(); store.store(TestInternalCacheEntryFactory.create("k1", "v1")); assert countingCS.numLoads == 0; assert countingCS.numContains == 0; assert "v1".equals(cache.get("k1")); assert countingCS.numLoads == 1 : "Expected 1, was " + countingCS.numLoads; assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; assert "v1".equals(cache.get("k1")); assert countingCS.numLoads == 1 : "Expected 1, was " + countingCS.numLoads; assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; } public void testSkipCacheFlagUsage() throws CacheLoaderException { CountingCacheStore countingCS = getCountingCacheStore(); store.store(TestInternalCacheEntryFactory.create("k1", "v1")); assert countingCS.numLoads == 0; assert countingCS.numContains == 0; //load using SKIP_CACHE_STORE should not find the object in the store assert cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE).get("k1") == null; assert countingCS.numLoads == 0; assert countingCS.numContains == 0; // counter-verify that the object was actually in the store: assert "v1".equals(cache.get("k1")); assert countingCS.numLoads == 1 : "Expected 1, was " + countingCS.numLoads; assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; // now check that put won't return the stored value store.store(TestInternalCacheEntryFactory.create("k2", "v2")); Object putReturn = cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE).put("k2", "v2-second"); assert putReturn == null; assert countingCS.numLoads == 1 : "Expected 1, was " + countingCS.numLoads; assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; // but it inserted it in the cache: assert "v2-second".equals(cache.get("k2")); // perform the put in the cache & store, using same value: putReturn = cache.put("k2", "v2-second"); //returned value from the cache: assert "v2-second".equals(putReturn); //and verify that the put operation updated the store too: assert "v2-second".equals(store.load("k2").getValue()); assert countingCS.numLoads == 2 : "Expected 2, was " + countingCS.numLoads; assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; cache.containsKey("k1"); assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; assert false == cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE).containsKey("k3"); assert countingCS.numContains == 0 : "Expected 0, was " + countingCS.numContains; assert countingCS.numLoads == 2 : "Expected 2, was " + countingCS.numLoads; //now with batching: boolean batchStarted = cache.getAdvancedCache().startBatch(); assert batchStarted; assert null == cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_STORE).get("k1batch"); assert countingCS.numLoads == 2 : "Expected 2, was " + countingCS.numLoads; assert null == cache.getAdvancedCache().get("k2batch"); assert countingCS.numLoads == 3 : "Expected 3, was " + countingCS.numLoads; cache.endBatch(true); } private CountingCacheStore getCountingCacheStore() { CacheLoaderManager clm = TestingUtil.extractComponent(cache, CacheLoaderManager.class); ChainingCacheStore ccs = (ChainingCacheStore) clm.getCacheLoader(); CountingCacheStore countingCS = (CountingCacheStore) ccs.getStores().keySet().iterator().next(); reset(cache, countingCS); return countingCS; } public void testSkipCacheLoadFlagUsage() throws CacheLoaderException { CountingCacheStore countingCS = getCountingCacheStore(); store.store(TestInternalCacheEntryFactory.create("home", "Vermezzo")); store.store(TestInternalCacheEntryFactory.create("home-second", "Newcastle Upon Tyne")); assert countingCS.numLoads == 0; //load using SKIP_CACHE_LOAD should not find the object in the store assert cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD).get("home") == null; assert countingCS.numLoads == 0; assert cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD).put("home", "Newcastle") == null; assert countingCS.numLoads == 0; final Object put = cache.getAdvancedCache().put("home-second", "Newcastle Upon Tyne, second"); assertEquals(put, "Newcastle Upon Tyne"); assert countingCS.numLoads == 1; } private void reset(Cache cache, CountingCacheStore countingCS) { cache.clear(); countingCS.numLoads = 0; countingCS.numContains = 0; } public static class CountingCacheStore extends AbstractCacheStore { public int numLoads, numContains; @Override public void store(InternalCacheEntry entry) throws CacheLoaderException { } @Override public void fromStream(ObjectInput inputStream) throws CacheLoaderException { } @Override public void toStream(ObjectOutput outputStream) throws CacheLoaderException { } @Override public void clear() throws CacheLoaderException { } @Override public boolean remove(Object key) throws CacheLoaderException { return false; } @Override protected void purgeInternal() throws CacheLoaderException { } @Override public InternalCacheEntry load(Object key) throws CacheLoaderException { incrementLoads(); return null; } @Override public Set<InternalCacheEntry> loadAll() throws CacheLoaderException { return Collections.emptySet(); } @Override public Set<InternalCacheEntry> load(int numEntries) throws CacheLoaderException { return Collections.emptySet(); } @Override public Set<Object> loadAllKeys(Set<Object> keysToExclude) throws CacheLoaderException { return Collections.emptySet(); } @Override public boolean containsKey(Object key) throws CacheLoaderException { numContains++; return false; } @Override public Class<? extends CacheLoaderConfig> getConfigurationClass() { return CountingCacheStoreConfig.class; } private void incrementLoads() { numLoads++; } } public static class CountingCacheStoreConfig extends AbstractCacheStoreConfig { public CountingCacheStoreConfig() { setCacheLoaderClassName(CountingCacheStore.class.getName()); } } }