/* * JBoss, Home of Professional Open Source * Copyright 2009 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.bdbje; import com.sleepycat.bind.serial.StoredClassCatalog; import com.sleepycat.collections.CurrentTransaction; import com.sleepycat.collections.StoredMap; import com.sleepycat.collections.StoredSortedMap; import com.sleepycat.collections.TransactionWorker; import com.sleepycat.je.Cursor; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.Environment; import com.sleepycat.util.RuntimeExceptionWrapper; import org.infinispan.Cache; import org.infinispan.container.entries.InternalCacheEntry; import org.infinispan.test.fwk.TestInternalCacheEntryFactory; import org.infinispan.loaders.CacheLoaderException; import org.infinispan.loaders.modifications.Store; import org.infinispan.marshall.StreamingMarshaller; import org.infinispan.marshall.TestObjectStreamMarshaller; import org.infinispan.transaction.xa.GlobalTransaction; import org.infinispan.transaction.xa.TransactionFactory; import org.infinispan.util.ReflectionUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; import java.io.IOException; import java.io.ObjectInput; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.Properties; import static org.mockito.Mockito.*; /** * @author Adrian Cole * @since 4.0 */ @Test(groups = "unit", testName = "loaders.bdbje.BdbjeCacheStoreTest") public class BdbjeCacheStoreTest { private BdbjeCacheStore cs; private BdbjeCacheStoreConfig cfg; private BdbjeResourceFactory factory; private Cache cache; private Environment env; private Database cacheDb; private Database catalogDb; private Database expiryDb; private StoredClassCatalog catalog; private StoredMap cacheMap; private StoredSortedMap expiryMap; private PreparableTransactionRunner runner; private CurrentTransaction currentTransaction; private TransactionFactory gtf; private class MockBdbjeResourceFactory extends BdbjeResourceFactory { @Override public PreparableTransactionRunner createPreparableTransactionRunner(Environment env) { return runner; } @Override public CurrentTransaction createCurrentTransaction(Environment env) { return currentTransaction; } @Override public Environment createEnvironment(File envLocation, Properties environmentProperties) throws DatabaseException { return env; } @Override public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException { return catalog; } @Override public Database createDatabase(Environment env, String name) throws DatabaseException { if (name.equals(cfg.getCacheDbName())) return cacheDb; else if (name.equals(cfg.getCatalogDbName())) return catalogDb; else if (name.equals(cfg.getExpiryDbName())) return expiryDb; else throw new IllegalStateException("Unknown name:" + name); } @Override public StoredMap createStoredMapViewOfDatabase(Database database, StoredClassCatalog classCatalog, StreamingMarshaller m) throws DatabaseException { return cacheMap; } @Override public StoredSortedMap<Long, Object> createStoredSortedMapForKeyExpiry(Database database, StoredClassCatalog classCatalog, StreamingMarshaller marshaller) throws DatabaseException { return expiryMap; } public MockBdbjeResourceFactory(BdbjeCacheStoreConfig config) { super(config); } } @BeforeMethod public void setUp() throws Exception { cfg = new BdbjeCacheStoreConfig(); factory = new MockBdbjeResourceFactory(cfg); cache = mock(Cache.class); cs = new BdbjeCacheStore(); env = mock(Environment.class); cacheDb = mock(Database.class); catalogDb = mock(Database.class); expiryDb = mock(Database.class); catalog = mock(StoredClassCatalog.class); cacheMap = mock(StoredMap.class); expiryMap = mock(StoredSortedMap.class); currentTransaction = mock(CurrentTransaction.class); gtf = new TransactionFactory(); gtf.init(false, false, true); WeakReference<Environment> envRef = new WeakReference<Environment>(env); ReflectionUtil.setValue(currentTransaction, "envRef", envRef); ThreadLocal localTrans = new ThreadLocal(); ReflectionUtil.setValue(currentTransaction, "localTrans", localTrans); runner = mock(PreparableTransactionRunner.class); } @AfterMethod public void tearDown() throws CacheLoaderException { runner = null; currentTransaction = null; cacheMap = null; catalogDb = null; expiryDb = null; cacheDb = null; env = null; factory = null; cache = null; cfg = null; cs = null; gtf = null; } void start() throws DatabaseException, CacheLoaderException { cs.init(cfg, factory, cache, new TestObjectStreamMarshaller()); when(cache.getName()).thenReturn("cache"); when(cache.getConfiguration()).thenReturn(null); } public void testGetConfigurationClass() throws Exception { assert cs.getConfigurationClass().equals(BdbjeCacheStoreConfig.class); } public void testInitNoMock() throws Exception { cs.init(cfg, cache, null); assert cfg.equals(ReflectionUtil.getValue(cs, "cfg")); assert cache.equals(ReflectionUtil.getValue(cs, "cache")); assert ReflectionUtil.getValue(cs, "factory") instanceof BdbjeResourceFactory; } public void testExceptionClosingCacheDatabaseDoesNotPreventEnvironmentFromClosing() throws Exception { start(); doThrow(new DatabaseException("Dummy") {}).when(expiryDb).close(); cs.start(); cs.stop(); } public void testExceptionClosingCatalogDoesNotPreventEnvironmentFromClosing() throws Exception { start(); doThrow(new DatabaseException("Dummy") {}).when(catalog).close(); cs.start(); cs.stop(); } @Test(expectedExceptions = CacheLoaderException.class) public void testExceptionClosingEnvironment() throws Exception { start(); doThrow(new DatabaseException("Dummy") {}).when(env).close(); cs.start(); cs.stop(); } @Test(expectedExceptions = CacheLoaderException.class) public void testThrowsCorrectExceptionOnStartForDatabaseException() throws Exception { factory = new MockBdbjeResourceFactory(cfg) { @Override public StoredClassCatalog createStoredClassCatalog(Database catalogDb) throws DatabaseException { throw new DatabaseException("Dummy"){}; } }; start(); cs.start(); } @Test(expectedExceptions = CacheLoaderException.class) public void testEnvironmentDirectoryExistsButNotAFile() throws Exception { File file = mock(File.class); when(file.exists()).thenReturn(true); when(file.isDirectory()).thenReturn(false); cs.verifyOrCreateEnvironmentDirectory(file); } @Test(expectedExceptions = CacheLoaderException.class) public void testCantCreateEnvironmentDirectory() throws Exception { File file = mock(File.class); when(file.exists()).thenReturn(false); when(file.mkdirs()).thenReturn(false); cs.verifyOrCreateEnvironmentDirectory(file); } public void testCanCreateEnvironmentDirectory() throws Exception { File file = mock(File.class); when(file.exists()).thenReturn(false); when(file.mkdirs()).thenReturn(true); when(file.isDirectory()).thenReturn(true); assert file.equals(cs.verifyOrCreateEnvironmentDirectory(file)); } public void testNoExceptionOnRollback() throws Exception { start(); GlobalTransaction tx = gtf.newGlobalTransaction(null, false); cs.start(); cs.rollback(tx); } public void testApplyModificationsThrowsOriginalDatabaseException() throws Exception { start(); DatabaseException ex = new DatabaseException("Dummy"){}; doThrow(new RuntimeExceptionWrapper(ex)).when(runner).run(isA(TransactionWorker.class)); cs.start(); try { cs.applyModifications(Collections.singletonList(new Store(TestInternalCacheEntryFactory.create("k", "v")))); assert false : "should have gotten an exception"; } catch (CacheLoaderException e) { assert ex.equals(e.getCause()); return; } assert false : "should have returned"; } public void testCommitThrowsOriginalDatabaseException() throws Exception { start(); DatabaseException ex = new DatabaseException("Dummy"){}; com.sleepycat.je.Transaction txn = mock(com.sleepycat.je.Transaction.class); when(currentTransaction.beginTransaction(null)).thenReturn(txn); runner.prepare(isA(TransactionWorker.class)); doThrow(new RuntimeExceptionWrapper(ex)).when(txn).commit(); cs.start(); try { txn = currentTransaction.beginTransaction(null); GlobalTransaction t = gtf.newGlobalTransaction(null, false); cs.prepare(Collections.singletonList(new Store(TestInternalCacheEntryFactory.create("k", "v"))), t, false); cs.commit(t); assert false : "should have gotten an exception"; } catch (CacheLoaderException e) { assert ex.equals(e.getCause()); return; } assert false : "should have returned"; } public void testPrepareThrowsOriginalDatabaseException() throws Exception { start(); DatabaseException ex = new DatabaseException("Dummy"){}; doThrow(new RuntimeExceptionWrapper(ex)).when(runner).prepare(isA(TransactionWorker.class)); cs.start(); try { GlobalTransaction tx = gtf.newGlobalTransaction(null, false); cs.prepare(Collections.singletonList(new Store(TestInternalCacheEntryFactory.create("k", "v"))), tx, false); assert false : "should have gotten an exception"; } catch (CacheLoaderException e) { assert ex.equals(e.getCause()); return; } assert false : "should have returned"; } public void testClearOnAbortFromStream() throws Exception { start(); InternalCacheEntry entry = TestInternalCacheEntryFactory.create("key", "value"); when(cacheMap.put(entry.getKey(), entry)).thenReturn(null); ObjectInput ois = mock(ObjectInput.class); when(ois.readLong()).thenReturn((long) 1); com.sleepycat.je.Transaction txn = mock(com.sleepycat.je.Transaction.class); when(currentTransaction.beginTransaction(null)).thenReturn(txn); Cursor cursor = mock(Cursor.class); when(cacheDb.openCursor(txn, null)).thenReturn(cursor); IOException ex = new IOException(); when(ois.readObject()).thenThrow(ex); cacheMap.clear(); expiryMap.clear(); cs.start(); try { cs.store(entry); cs.fromStream(ois); assert false : "should have gotten an exception"; } catch (CacheLoaderException e) { assert ex.equals(e.getCause()); return; } assert false : "should have returned"; } }