/* * Copyright Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ehcache.transactions.xa.internal; import org.ehcache.Cache; import org.ehcache.ValueSupplier; import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePool; import org.ehcache.config.ResourceType; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.events.StoreEventDispatcher; import org.ehcache.core.internal.service.ServiceLocator; import org.ehcache.core.spi.service.DiskResourceService; import org.ehcache.core.spi.store.Store; import org.ehcache.expiry.Duration; import org.ehcache.expiry.Expirations; import org.ehcache.expiry.Expiry; import org.ehcache.core.spi.function.BiFunction; import org.ehcache.core.spi.function.Function; import org.ehcache.core.spi.function.NullaryFunction; import org.ehcache.impl.config.copy.DefaultCopyProviderConfiguration; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; import org.ehcache.impl.internal.spi.copy.DefaultCopyProvider; import org.ehcache.impl.internal.store.heap.OnHeapStore; import org.ehcache.impl.internal.store.offheap.MemorySizeParser; import org.ehcache.impl.internal.store.offheap.OffHeapStore; import org.ehcache.impl.internal.store.offheap.OffHeapStoreLifecycleHelper; import org.ehcache.impl.internal.store.tiering.TieredStore; import org.ehcache.internal.TestTimeSource; import org.ehcache.spi.service.ServiceProvider; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.copy.CopyProvider; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.service.Service; import org.ehcache.spi.service.ServiceConfiguration; import org.ehcache.transactions.xa.XACacheException; import org.ehcache.transactions.xa.configuration.XAStoreConfiguration; import org.ehcache.transactions.xa.internal.journal.Journal; import org.ehcache.transactions.xa.internal.journal.TransientJournal; import org.ehcache.transactions.xa.internal.txmgr.NullXAResourceRegistry; import org.ehcache.transactions.xa.txmgr.TransactionManagerWrapper; import org.ehcache.transactions.xa.txmgr.provider.TransactionManagerProvider; import org.ehcache.transactions.xa.utils.JavaSerializer; import org.ehcache.transactions.xa.utils.TestXid; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.InvalidTransactionException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import static java.util.Collections.emptySet; import static org.ehcache.core.internal.service.ServiceLocator.dependencySet; import static org.ehcache.expiry.Duration.of; import static org.ehcache.expiry.Expirations.timeToLiveExpiration; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; /** * Tests for {@link XAStore} and {@link org.ehcache.transactions.xa.internal.XAStore.Provider XAStore.Provider}. */ public class XAStoreTest { @Rule public TestName testName = new TestName(); @SuppressWarnings("unchecked") private final Class<SoftLock<String>> valueClass = (Class) SoftLock.class; private final TestTransactionManager testTransactionManager = new TestTransactionManager(); private TransactionManagerWrapper transactionManagerWrapper; private OnHeapStore<Long, SoftLock<String>> onHeapStore; private Journal<Long> journal; private TestTimeSource testTimeSource; private ClassLoader classLoader; private Serializer<Long> keySerializer; private Serializer<SoftLock<String>> valueSerializer; private StoreEventDispatcher<Long, SoftLock<String>> eventDispatcher; private final Expiry<Object, Object> expiry = timeToLiveExpiration(of(1, TimeUnit.SECONDS)); private Copier<Long> keyCopier; private Copier<SoftLock<String>> valueCopier; @Before public void setUp() { transactionManagerWrapper = new TransactionManagerWrapper(testTransactionManager, new NullXAResourceRegistry()); classLoader = ClassLoader.getSystemClassLoader(); keySerializer = new JavaSerializer<Long>(classLoader); valueSerializer = new JavaSerializer<SoftLock<String>>(classLoader); CopyProvider copyProvider = new DefaultCopyProvider(new DefaultCopyProviderConfiguration()); keyCopier = copyProvider.createKeyCopier(Long.class, keySerializer); valueCopier = copyProvider.createValueCopier(valueClass, valueSerializer); Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, Expirations.noExpiration(), ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); testTimeSource = new TestTimeSource(); eventDispatcher = NullStoreEventDispatcher.nullStoreEventDispatcher(); onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); journal = new TransientJournal<Long>(); } @Test public void testXAStoreProviderFailsToRankWhenNoTMProviderConfigured() throws Exception { XAStore.Provider provider = new XAStore.Provider(); provider.start(new ServiceProvider<Service>() { @Override public <U extends Service> U getService(Class<U> serviceType) { return null; } @Override public <U extends Service> Collection<U> getServicesOfType(Class<U> serviceType) { return emptySet(); } }); try { Set<ResourceType<?>> resources = emptySet(); provider.rank(resources, Collections.<ServiceConfiguration<?>>singleton(mock(XAStoreConfiguration.class))); fail("Expected exception"); } catch (IllegalStateException e) { assertThat(e.getMessage(), containsString("TransactionManagerProvider")); } } @Test public void testSimpleGetPutRemove() throws Exception { XAStore<Long, String> xaStore = getXAStore(onHeapStore); testTransactionManager.begin(); { assertThat(xaStore.remove(1L), equalTo(false)); assertThat(xaStore.get(1L), is(nullValue())); assertThat(xaStore.put(1L, "1"), equalTo(Store.PutStatus.PUT)); assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.UPDATE)); assertThat(xaStore.get(1L).value(), equalTo("one")); } testTransactionManager.rollback(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { assertThat(xaStore.get(1L), is(nullValue())); assertThat(xaStore.put(1L, "1"), equalTo(Store.PutStatus.PUT)); assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.UPDATE)); assertThat(xaStore.get(1L).value(), equalTo("one")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { assertThat(xaStore.remove(1L), equalTo(true)); assertThat(xaStore.remove(1L), equalTo(false)); assertThat(xaStore.get(1L), is(nullValue())); assertThat(xaStore.put(1L, "1"), equalTo(Store.PutStatus.PUT)); } testTransactionManager.rollback(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "un"), equalTo(Store.PutStatus.UPDATE)); assertThat(xaStore.remove(1L), equalTo(true)); assertThat(xaStore.remove(1L), equalTo(false)); assertThat(xaStore.get(1L), is(nullValue())); assertThat(xaStore.put(1L, "un"), equalTo(Store.PutStatus.PUT)); assertThat(xaStore.get(1L).value(), equalTo("un")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "un"); } @Test public void testConflictingGetPutRemove() throws Exception { final XAStore<Long, String> xaStore = getXAStore(onHeapStore); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); testTransactionManager.begin(); { xaStore.put(1L, "one"); } testTransactionManager.commit(); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "un"), equalTo(Store.PutStatus.UPDATE)); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.put(1L, "uno"), equalTo(Store.PutStatus.NOOP)); testTransactionManager.commit(); return null; } }); assertThat(xaStore.put(1L, "eins"), equalTo(Store.PutStatus.UPDATE)); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { xaStore.put(1L, "one"); } testTransactionManager.commit(); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "un"), equalTo(Store.PutStatus.UPDATE)); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.remove(1L), is(false)); testTransactionManager.commit(); return null; } }); assertThat(xaStore.put(1L, "een"), equalTo(Store.PutStatus.UPDATE)); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { xaStore.put(1L, "one"); } testTransactionManager.commit(); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "un"), equalTo(Store.PutStatus.UPDATE)); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.get(1L), is(nullValue())); testTransactionManager.commit(); return null; } }); assertThat(xaStore.put(1L, "yksi"), equalTo(Store.PutStatus.UPDATE)); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); } private void executeWhileIn2PC(final AtomicReference<Throwable> exception, final Callable callable) { testTransactionManager.getCurrentTransaction().registerTwoPcListener(new TwoPcListener() { @Override public void inMiddleOf2PC() { try { Thread t = new Thread() { @Override public void run() { try { // this runs while the committing TX is in-doubt callable.call(); } catch (Throwable t) { exception.set(t); } } }; t.start(); t.join(); } catch (Throwable e) { exception.set(e); } } }); } @Test public void testIterate() throws Exception { XAStore<Long, String> xaStore = getXAStore(onHeapStore); testTransactionManager.begin(); { xaStore.put(1L, "one"); xaStore.put(2L, "two"); xaStore.put(3L, "three"); } testTransactionManager.commit(); testTransactionManager.begin(); { xaStore.put(0L, "zero"); xaStore.put(1L, "un"); xaStore.put(2L, "two"); xaStore.remove(3L); Map<Long, String> iterated = new HashMap<Long, String>(); Store.Iterator<Cache.Entry<Long, Store.ValueHolder<String>>> iterator = xaStore.iterator(); while (iterator.hasNext()) { Cache.Entry<Long, Store.ValueHolder<String>> next = iterator.next(); iterated.put(next.getKey(), next.getValue().value()); } assertThat(iterated.size(), is(3)); assertThat(iterated.get(0L), equalTo("zero")); assertThat(iterated.get(1L), equalTo("un")); assertThat(iterated.get(2L), equalTo("two")); } testTransactionManager.commit(); testTransactionManager.begin(); { Map<Long, String> iterated = new HashMap<Long, String>(); Store.Iterator<Cache.Entry<Long, Store.ValueHolder<String>>> iterator = xaStore.iterator(); while (iterator.hasNext()) { Cache.Entry<Long, Store.ValueHolder<String>> next = iterator.next(); iterated.put(next.getKey(), next.getValue().value()); } assertThat(iterated.size(), is(3)); assertThat(iterated.get(0L), equalTo("zero")); assertThat(iterated.get(1L), equalTo("un")); assertThat(iterated.get(2L), equalTo("two")); } testTransactionManager.commit(); Store.Iterator<Cache.Entry<Long, Store.ValueHolder<String>>> iterator; testTransactionManager.begin(); { iterator = xaStore.iterator(); iterator.next(); } testTransactionManager.commit(); // cannot use iterator outside of tx context try { iterator.hasNext(); fail(); } catch (XACacheException e) { // expected } try { iterator.next(); fail(); } catch (XACacheException e) { // expected } // cannot use iterator outside of original tx context testTransactionManager.begin(); { try { iterator.hasNext(); fail(); } catch (IllegalStateException e) { // expected } try { iterator.next(); fail(); } catch (IllegalStateException e) { // expected } } testTransactionManager.commit(); } @Test public void testPutIfAbsent() throws Exception { final XAStore<Long, String> xaStore = getXAStore(onHeapStore); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); testTransactionManager.begin(); { assertThat(xaStore.putIfAbsent(1L, "one"), is(nullValue())); assertThat(xaStore.get(1L).value(), equalTo("one")); assertThat(xaStore.putIfAbsent(1L, "un").value(), equalTo("one")); assertThat(xaStore.get(1L).value(), equalTo("one")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { assertThat(xaStore.putIfAbsent(1L, "un").value(), equalTo("one")); assertThat(xaStore.get(1L).value(), equalTo("one")); assertThat(xaStore.remove(1L), equalTo(true)); assertThat(xaStore.putIfAbsent(1L, "uno"), is(nullValue())); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "uno"); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.putIfAbsent(1L, "un"), is(nullValue())); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); } @Test public void testRemove2Args() throws Exception { final XAStore<Long, String> xaStore = getXAStore(onHeapStore); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); testTransactionManager.begin(); { assertThat(xaStore.remove(1L, "one"), equalTo(Store.RemoveStatus.KEY_MISSING)); assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.PUT)); assertThat(xaStore.remove(1L, "un"), equalTo(Store.RemoveStatus.KEY_PRESENT)); assertThat(xaStore.remove(1L, "one"), equalTo(Store.RemoveStatus.REMOVED)); assertThat(xaStore.remove(1L, "eins"), equalTo(Store.RemoveStatus.KEY_MISSING)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.PUT)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { assertThat(xaStore.remove(1L, "een"), equalTo(Store.RemoveStatus.KEY_PRESENT)); assertThat(xaStore.remove(1L, "one"), equalTo(Store.RemoveStatus.REMOVED)); assertThat(xaStore.remove(1L, "eins"), equalTo(Store.RemoveStatus.KEY_MISSING)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.remove(1L, "un"), equalTo(Store.RemoveStatus.KEY_MISSING)); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.PUT)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.remove(1L, "un"), equalTo(Store.RemoveStatus.KEY_MISSING)); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); } @Test public void testReplace2Args() throws Exception { final XAStore<Long, String> xaStore = getXAStore(onHeapStore); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); testTransactionManager.begin(); { assertThat(xaStore.replace(1L, "one"), is(nullValue())); assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.PUT)); assertThat(xaStore.replace(1L, "un").value(), equalTo("one")); assertThat(xaStore.replace(1L, "uno").value(), equalTo("un")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "uno"); testTransactionManager.begin(); { assertThat(xaStore.replace(1L, "een").value(), equalTo("uno")); assertThat(xaStore.replace(1L, "eins").value(), equalTo("een")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "eins"); testTransactionManager.begin(); { assertThat(xaStore.remove(1L), is(true)); assertThat(xaStore.replace(1L, "yksi"), is(nullValue())); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.replace(1L, "un"), is(nullValue())); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "one"), is(Store.PutStatus.PUT)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.replace(1L, "un"), is(nullValue())); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); } @Test public void testReplace3Args() throws Exception { final XAStore<Long, String> xaStore = getXAStore(onHeapStore); final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); testTransactionManager.begin(); { assertThat(xaStore.replace(1L, "one", "un"), equalTo(Store.ReplaceStatus.MISS_NOT_PRESENT)); assertThat(xaStore.put(1L, "one"), equalTo(Store.PutStatus.PUT)); assertThat(xaStore.replace(1L, "eins", "un"), equalTo(Store.ReplaceStatus.MISS_PRESENT)); assertThat(xaStore.replace(1L, "one", "un"), equalTo(Store.ReplaceStatus.HIT)); assertThat(xaStore.get(1L).value(), equalTo("un")); assertThat(xaStore.replace(1L, "eins", "een"), equalTo(Store.ReplaceStatus.MISS_PRESENT)); assertThat(xaStore.replace(1L, "un", "uno"), equalTo(Store.ReplaceStatus.HIT)); assertThat(xaStore.get(1L).value(), equalTo("uno")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "uno"); testTransactionManager.begin(); { assertThat(xaStore.replace(1L, "one", "uno"), equalTo(Store.ReplaceStatus.MISS_PRESENT)); assertThat(xaStore.replace(1L, "uno", "un"), equalTo(Store.ReplaceStatus.HIT)); assertThat(xaStore.get(1L).value(), equalTo("un")); assertThat(xaStore.remove(1L), equalTo(true)); assertThat(xaStore.replace(1L, "un", "eins"), equalTo(Store.ReplaceStatus.MISS_NOT_PRESENT)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.replace(1L, "eins", "one"), is(Store.ReplaceStatus.MISS_NOT_PRESENT)); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { assertThat(xaStore.put(1L, "one"), is(Store.PutStatus.PUT)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { xaStore.put(1L, "eins"); executeWhileIn2PC(exception, new Callable() { @Override public Object call() throws Exception { testTransactionManager.begin(); assertThat(xaStore.replace(1L, "one", "un"), is(Store.ReplaceStatus.MISS_NOT_PRESENT)); testTransactionManager.commit(); return null; } }); } testTransactionManager.commit(); assertThat(exception.get(), is(nullValue())); assertMapping(xaStore, 1L, null); } @Test public void testCompute() throws Exception { Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, Expirations.noExpiration(), ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, is(nullValue())); return "one"; } }); assertThat(computed1.value(), equalTo("one")); Store.ValueHolder<String> computed2 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("one")); return "un"; } }); assertThat(computed2.value(), equalTo("un")); Store.ValueHolder<String> computed3 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("un")); return null; } }); assertThat(computed3, is(nullValue())); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, is(nullValue())); return "one"; } }, new NullaryFunction<Boolean>() { @Override public Boolean apply() { return Boolean.FALSE; } }); assertThat(computed1.value(), equalTo("one")); Store.ValueHolder<String> computed2 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("one")); return null; } }, new NullaryFunction<Boolean>() { @Override public Boolean apply() { return Boolean.FALSE; } }); assertThat(computed2, is(nullValue())); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, is(nullValue())); return "one"; } }); assertThat(computed1.value(), equalTo("one")); Store.ValueHolder<String> computed2 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("one")); return null; } }); assertThat(computed2, is(nullValue())); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, is(nullValue())); return "one"; } }); assertThat(computed1.value(), equalTo("one")); Store.ValueHolder<String> computed2 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("one")); return "un"; } }); assertThat(computed2.value(), equalTo("un")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "un"); testTransactionManager.begin(); { Store.ValueHolder<String> computed = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("un")); return "eins"; } }); assertThat(computed.value(), equalTo("eins")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "eins"); testTransactionManager.begin(); { Store.ValueHolder<String> computed = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("eins")); return null; } }); assertThat(computed, is(nullValue())); } testTransactionManager.rollback(); assertMapping(xaStore, 1L, "eins"); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, equalTo("eins")); return null; } }); assertThat(computed1, is(nullValue())); Store.ValueHolder<String> computed2 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, is(nullValue())); return null; } }); assertThat(computed2, is(nullValue())); Store.ValueHolder<String> computed3 = xaStore.compute(1L, new BiFunction<Long, String, String>() { @Override public String apply(Long aLong, String s) { assertThat(aLong, is(1L)); assertThat(s, is(nullValue())); return "uno"; } }); assertThat(computed3.value(), equalTo("uno")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "uno"); testTransactionManager.begin(); { xaStore.remove(1L); } testTransactionManager.commit(); testTransactionManager.begin(); { assertThat(xaStore.containsKey(1L), is(false)); xaStore.put(1L, "uno"); assertThat(xaStore.containsKey(1L), is(true)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "uno"); testTransactionManager.begin(); { assertThat(xaStore.containsKey(1L), is(true)); xaStore.remove(1L); assertThat(xaStore.containsKey(1L), is(false)); } testTransactionManager.commit(); assertMapping(xaStore, 1L, null); OffHeapStoreLifecycleHelper.close(offHeapStore); } @Test public void testComputeIfAbsent() throws Exception { Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, Expirations.noExpiration(), ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.computeIfAbsent(1L, new Function<Long, String>() { @Override public String apply(Long aLong) { assertThat(aLong, is(1L)); return "one"; } }); assertThat(computed1.value(), equalTo("one")); Store.ValueHolder<String> computed2 = xaStore.computeIfAbsent(1L, new Function<Long, String>() { @Override public String apply(Long aLong) { fail("should not be absent"); throw new AssertionError(); } }); assertThat(computed2.value(), equalTo("one")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTransactionManager.begin(); { Store.ValueHolder<String> computed1 = xaStore.computeIfAbsent(1L, new Function<Long, String>() { @Override public String apply(Long aLong) { fail("should not be absent"); throw new AssertionError(); } }); assertThat(computed1.value(), equalTo("one")); xaStore.remove(1L); Store.ValueHolder<String> computed2 = xaStore.computeIfAbsent(1L, new Function<Long, String>() { @Override public String apply(Long aLong) { assertThat(aLong, is(1L)); return "un"; } }); assertThat(computed2.value(), equalTo("un")); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "un"); OffHeapStoreLifecycleHelper.close(offHeapStore); } @Test public void testExpiry() throws Exception { Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); { xaStore.put(1L, "one"); } testTransactionManager.commit(); assertMapping(xaStore, 1L, "one"); testTimeSource.advanceTime(2000); assertMapping(xaStore, 1L, null); OffHeapStoreLifecycleHelper.close(offHeapStore); } @Test public void testExpiryCreateException() throws Exception { Expiry<Object, Object> expiry = new Expiry<Object, Object>() { @Override public Duration getExpiryForCreation(Object key, Object value) { throw new RuntimeException(); } @Override public Duration getExpiryForAccess(Object key, ValueSupplier<? extends Object> value) { throw new AssertionError(); } @Override public Duration getExpiryForUpdate(Object key, ValueSupplier<? extends Object> oldValue, Object newValue) { throw new AssertionError(); } }; Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); OnHeapStore<Long, SoftLock<String>> onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); xaStore.put(1L, "one"); testTransactionManager.commit(); assertMapping(xaStore, 1L, null); } @Test public void testExpiryAccessException() throws Exception { String uniqueXAResourceId = "testExpiryAccessException"; Expiry<Object, Object> expiry = new Expiry<Object, Object>() { @Override public Duration getExpiryForCreation(Object key, Object value) { return Duration.INFINITE; } @Override public Duration getExpiryForAccess(Object key, ValueSupplier<? extends Object> value) { if (testTimeSource.getTimeMillis() > 0) { throw new RuntimeException(); } return Duration.INFINITE; } @Override public Duration getExpiryForUpdate(Object key, ValueSupplier<? extends Object> oldValue, Object newValue) { return Duration.INFINITE; } }; Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); OnHeapStore<Long, SoftLock<String>> onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); xaStore.put(1L, "one"); testTransactionManager.commit(); testTimeSource.advanceTime(1000); testTransactionManager.begin(); assertThat(xaStore.get(1L).value(), is("one")); testTransactionManager.commit(); testTransactionManager.begin(); assertThat(xaStore.get(1L), nullValue()); testTransactionManager.commit(); } @Test public void testExpiryUpdateException() throws Exception{ Expiry<Object, Object> expiry = new Expiry<Object, Object>() { @Override public Duration getExpiryForCreation(Object key, Object value) { return Duration.INFINITE; } @Override public Duration getExpiryForAccess(Object key, ValueSupplier<? extends Object> value) { return Duration.INFINITE; } @Override public Duration getExpiryForUpdate(Object key, ValueSupplier<? extends Object> oldValue, Object newValue) { if (testTimeSource.getTimeMillis() > 0) { throw new RuntimeException(); } return Duration.INFINITE; } }; Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); OnHeapStore<Long, SoftLock<String>> onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); xaStore.put(1L, "one"); xaStore.get(1L); testTransactionManager.commit(); testTimeSource.advanceTime(1000); testTransactionManager.begin(); xaStore.put(1L, "two"); testTransactionManager.commit(); assertMapping(xaStore, 1L, null); } @Test public void testBulkCompute() throws Exception { String uniqueXAResourceId = "testBulkCompute"; Expiry<Object, Object> expiry = Expirations.timeToLiveExpiration(new Duration(1, TimeUnit.SECONDS)); Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); OnHeapStore<Long, SoftLock<String>> onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); { Map<Long, Store.ValueHolder<String>> computedMap = xaStore.bulkCompute(asSet(1L, 2L, 3L), new Function<Iterable<? extends Map.Entry<? extends Long, ? extends String>>, Iterable<? extends Map.Entry<? extends Long, ? extends String>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends String>> apply(Iterable<? extends Map.Entry<? extends Long, ? extends String>> entries) { Map<Long, String> result = new HashMap<Long, String>(); for (Map.Entry<? extends Long, ? extends String> entry : entries) { Long key = entry.getKey(); String value = entry.getValue(); assertThat(value, is(nullValue())); result.put(key, "stuff#" + key); } return result.entrySet(); } }); assertThat(computedMap.size(), is(3)); assertThat(computedMap.get(1L).value(), equalTo("stuff#1")); assertThat(computedMap.get(2L).value(), equalTo("stuff#2")); assertThat(computedMap.get(3L).value(), equalTo("stuff#3")); computedMap = xaStore.bulkCompute(asSet(0L, 1L, 3L), new Function<Iterable<? extends Map.Entry<? extends Long, ? extends String>>, Iterable<? extends Map.Entry<? extends Long, ? extends String>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends String>> apply(Iterable<? extends Map.Entry<? extends Long, ? extends String>> entries) { Map<Long, String> result = new HashMap<Long, String>(); for (Map.Entry<? extends Long, ? extends String> entry : entries) { Long key = entry.getKey(); String value = entry.getValue(); switch (key.intValue()) { case 0: assertThat(value, is(nullValue())); break; case 1: case 3: assertThat(value, equalTo("stuff#" + key)); break; } if (key != 3L) { result.put(key, "otherStuff#" + key); } else { result.put(key, null); } } return result.entrySet(); } }); assertThat(computedMap.size(), is(3)); assertThat(computedMap.get(0L).value(), equalTo("otherStuff#0")); assertThat(computedMap.get(1L).value(), equalTo("otherStuff#1")); assertThat(computedMap.get(3L), is(nullValue())); } testTransactionManager.commit(); assertSize(xaStore, 3); assertMapping(xaStore, 0L, "otherStuff#0"); assertMapping(xaStore, 1L, "otherStuff#1"); assertMapping(xaStore, 2L, "stuff#2"); OffHeapStoreLifecycleHelper.close(offHeapStore); } @Test public void testBulkComputeIfAbsent() throws Exception { Expiry<Object, Object> expiry = Expirations.timeToLiveExpiration(new Duration(1, TimeUnit.SECONDS)); Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(10, EntryUnit.ENTRIES).build(), 0, keySerializer, valueSerializer); OnHeapStore<Long, SoftLock<String>> onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); Store.Configuration<Long, SoftLock<String>> offHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, null, classLoader, expiry, ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(10, MemoryUnit.MB).build(), 0, keySerializer, valueSerializer); OffHeapStore<Long, SoftLock<String>> offHeapStore = new OffHeapStore<Long, SoftLock<String>>(offHeapConfig, testTimeSource, eventDispatcher, MemorySizeParser.parse("10M")); OffHeapStoreLifecycleHelper.init(offHeapStore); TieredStore<Long, SoftLock<String>> tieredStore = new TieredStore<Long, SoftLock<String>>(onHeapStore, offHeapStore); XAStore<Long, String> xaStore = getXAStore(tieredStore); testTransactionManager.begin(); { Map<Long, Store.ValueHolder<String>> computedMap = xaStore.bulkComputeIfAbsent(asSet(1L, 2L, 3L), new Function<Iterable<? extends Long>, Iterable<? extends Map.Entry<? extends Long, ? extends String>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends String>> apply(Iterable<? extends Long> keys) { Map<Long, String> result = new HashMap<Long, String>(); for (Long key : keys) { result.put(key, "stuff#" + key); } return result.entrySet(); } }); assertThat(computedMap.size(), is(3)); assertThat(computedMap.get(1L).value(), equalTo("stuff#1")); assertThat(computedMap.get(2L).value(), equalTo("stuff#2")); assertThat(computedMap.get(3L).value(), equalTo("stuff#3")); computedMap = xaStore.bulkComputeIfAbsent(asSet(0L, 1L, 3L), new Function<Iterable<? extends Long>, Iterable<? extends Map.Entry<? extends Long, ? extends String>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends String>> apply(Iterable<? extends Long> keys) { Map<Long, String> result = new HashMap<Long, String>(); for (Long key : keys) { switch (key.intValue()) { case 0: result.put(key, "otherStuff#" + key); break; case 1: case 3: fail("key " + key + " should not be absent"); break; } } return result.entrySet(); } }); assertThat(computedMap.size(), is(3)); assertThat(computedMap.get(0L).value(), equalTo("otherStuff#0")); assertThat(computedMap.get(1L).value(), equalTo("stuff#1")); assertThat(computedMap.get(3L).value(), equalTo("stuff#3")); } testTransactionManager.commit(); assertSize(xaStore, 4); assertMapping(xaStore, 0L, "otherStuff#0"); assertMapping(xaStore, 1L, "stuff#1"); assertMapping(xaStore, 2L, "stuff#2"); assertMapping(xaStore, 3L, "stuff#3"); OffHeapStoreLifecycleHelper.close(offHeapStore); } @Test public void testCustomEvictionAdvisor() throws Exception { final AtomicBoolean invoked = new AtomicBoolean(); EvictionAdvisor<Long, SoftLock> evictionAdvisor = new EvictionAdvisor<Long, SoftLock>() { @Override public boolean adviseAgainstEviction(Long key, SoftLock value) { invoked.set(true); return false; } }; Store.Configuration<Long, SoftLock<String>> onHeapConfig = new StoreConfigurationImpl<Long, SoftLock<String>>(Long.class, valueClass, evictionAdvisor, classLoader, Expirations.noExpiration(), ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(10, EntryUnit.ENTRIES) .build(), 0, keySerializer, valueSerializer); OnHeapStore<Long, SoftLock<String>> onHeapStore = new OnHeapStore<Long, SoftLock<String>>(onHeapConfig, testTimeSource, keyCopier, valueCopier, new NoopSizeOfEngine(), eventDispatcher); final XAStore<Long, String> xaStore = getXAStore(onHeapStore); testTransactionManager.begin(); { xaStore.put(1L, "1"); } testTransactionManager.rollback(); assertThat(invoked.get(), is(false)); testTransactionManager.begin(); { xaStore.put(1L, "1"); } testTransactionManager.commit(); assertThat(invoked.get(), is(true)); } @Test public void testRank() throws Exception { XAStore.Provider provider = new XAStore.Provider(); XAStoreConfiguration configuration = new XAStoreConfiguration("testXAResourceId"); ServiceLocator serviceLocator = dependencySet() .with(provider) .with(Store.Provider.class) .with(mock(DiskResourceService.class)) .with(mock(TransactionManagerProvider.class)).build(); serviceLocator.startAllServices(); final Set<ServiceConfiguration<?>> xaStoreConfigs = Collections.<ServiceConfiguration<?>>singleton(configuration); assertRank(provider, 1001, xaStoreConfigs, ResourceType.Core.HEAP); assertRank(provider, 1001, xaStoreConfigs, ResourceType.Core.OFFHEAP); assertRank(provider, 1001, xaStoreConfigs, ResourceType.Core.DISK); assertRank(provider, 1002, xaStoreConfigs, ResourceType.Core.OFFHEAP, ResourceType.Core.HEAP); assertRank(provider, -1, xaStoreConfigs, ResourceType.Core.DISK, ResourceType.Core.OFFHEAP); assertRank(provider, 1002, xaStoreConfigs, ResourceType.Core.DISK, ResourceType.Core.HEAP); assertRank(provider, 1003, xaStoreConfigs, ResourceType.Core.DISK, ResourceType.Core.OFFHEAP, ResourceType.Core.HEAP); final Set<ServiceConfiguration<?>> emptyConfigs = emptySet(); assertRank(provider, 0, emptyConfigs, ResourceType.Core.DISK, ResourceType.Core.OFFHEAP, ResourceType.Core.HEAP); final ResourceType<ResourcePool> unmatchedResourceType = new ResourceType<ResourcePool>() { @Override public Class<ResourcePool> getResourcePoolClass() { return ResourcePool.class; } @Override public boolean isPersistable() { return true; } @Override public boolean requiresSerialization() { return true; } @Override public int getTierHeight() { return 10; } }; assertRank(provider, -1, xaStoreConfigs, unmatchedResourceType); assertRank(provider, -1, xaStoreConfigs, ResourceType.Core.DISK, ResourceType.Core.OFFHEAP, ResourceType.Core.HEAP, unmatchedResourceType); } private void assertRank(final Store.Provider provider, final int expectedRank, final Collection<ServiceConfiguration<?>> serviceConfigs, final ResourceType<?>... resources) { if (expectedRank == -1) { try { provider.rank(new HashSet<ResourceType<?>>(Arrays.asList(resources)), serviceConfigs); fail(); } catch (IllegalStateException e) { // Expected assertThat(e.getMessage(), startsWith("No Store.Provider ")); } } else { assertThat(provider.rank(new HashSet<ResourceType<?>>(Arrays.asList(resources)), serviceConfigs), is(expectedRank)); } } private Set<Long> asSet(Long... longs) { return new HashSet<Long>(Arrays.asList(longs)); } private void assertMapping(XAStore<Long, String> xaStore, long key, String value) throws Exception { testTransactionManager.begin(); Store.ValueHolder<String> valueHolder = xaStore.get(key); if (value != null) { assertThat(valueHolder.value(), equalTo(value)); } else { assertThat(valueHolder, is(nullValue())); } testTransactionManager.commit(); } private void assertSize(XAStore<Long, String> xaStore, int expectedSize) throws Exception { testTransactionManager.begin(); int counter = 0; Store.Iterator<Cache.Entry<Long, Store.ValueHolder<String>>> iterator = xaStore.iterator(); while (iterator.hasNext()) { Cache.Entry<Long, Store.ValueHolder<String>> next = iterator.next(); counter++; } assertThat(counter, is(expectedSize)); testTransactionManager.commit(); } private XAStore<Long, String> getXAStore(Store<Long, SoftLock<String>> store) { return new XAStore<Long, String>(Long.class, String.class, store, transactionManagerWrapper, testTimeSource, journal, testName.getMethodName()); } static class TestTransactionManager implements TransactionManager { volatile TestTransaction currentTransaction; final AtomicLong gtridGenerator = new AtomicLong(); public TestTransaction getCurrentTransaction() { return currentTransaction; } @Override public void begin() throws NotSupportedException, SystemException { currentTransaction = new TestTransaction(gtridGenerator.incrementAndGet()); } @Override public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { currentTransaction.commit(); currentTransaction = null; } @Override public int getStatus() throws SystemException { return 0; } @Override public Transaction getTransaction() throws SystemException { return currentTransaction; } @Override public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { } @Override public void rollback() throws IllegalStateException, SecurityException, SystemException { currentTransaction.rollback(); currentTransaction = null; } @Override public void setRollbackOnly() throws IllegalStateException, SystemException { } @Override public void setTransactionTimeout(int seconds) throws SystemException { } @Override public Transaction suspend() throws SystemException { return null; } } static class TestTransaction implements Transaction { final long gtrid; final Map<XAResource, TestXid> xids = new IdentityHashMap<XAResource, TestXid>(); final AtomicLong bqualGenerator = new AtomicLong(); final List<Synchronization> synchronizations = new CopyOnWriteArrayList<Synchronization>(); final List<TwoPcListener> twoPcListeners = new CopyOnWriteArrayList<TwoPcListener>(); public TestTransaction(long gtrid) { this.gtrid = gtrid; } @Override public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { try { Set<Map.Entry<XAResource, TestXid>> entries = xids.entrySet(); // delist for (Map.Entry<XAResource, TestXid> entry : entries) { try { entry.getKey().end(entry.getValue(), XAResource.TMSUCCESS); } catch (XAException e) { throw (SystemException) new SystemException(XAException.XAER_RMERR).initCause(e); } } fireBeforeCompletion(); Set<XAResource> preparedResources = new HashSet<XAResource>(); // prepare for (Map.Entry<XAResource, TestXid> entry : entries) { try { int prepareStatus = entry.getKey().prepare(entry.getValue()); if (prepareStatus != XAResource.XA_RDONLY) { preparedResources.add(entry.getKey()); } } catch (XAException e) { throw (SystemException) new SystemException(XAException.XAER_RMERR).initCause(e); } } fireInMiddleOf2PC(); // commit for (Map.Entry<XAResource, TestXid> entry : entries) { try { if (preparedResources.contains(entry.getKey())) { entry.getKey().commit(entry.getValue(), false); } } catch (XAException e) { throw (SystemException) new SystemException(XAException.XAER_RMERR).initCause(e); } } } finally { fireAfterCompletion(Status.STATUS_COMMITTED); } } @Override public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException { return true; } @Override public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException { TestXid testXid = xids.get(xaRes); if (testXid == null) { testXid = new TestXid(gtrid, bqualGenerator.incrementAndGet()); xids.put(xaRes, testXid); } try { xaRes.start(testXid, XAResource.TMNOFLAGS); } catch (XAException e) { throw (SystemException) new SystemException(XAException.XAER_RMERR).initCause(e); } return true; } @Override public int getStatus() throws SystemException { return 0; } public void registerTwoPcListener(TwoPcListener listener) { twoPcListeners.add(listener); } @Override public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException { synchronizations.add(sync); } @Override public void rollback() throws IllegalStateException, SystemException { try { Set<Map.Entry<XAResource, TestXid>> entries = xids.entrySet(); // delist for (Map.Entry<XAResource, TestXid> entry : entries) { try { entry.getKey().end(entry.getValue(), XAResource.TMSUCCESS); } catch (XAException e) { throw (SystemException) new SystemException(XAException.XAER_RMERR).initCause(e); } } // rollback for (Map.Entry<XAResource, TestXid> entry : entries) { try { entry.getKey().rollback(entry.getValue()); } catch (XAException e) { throw (SystemException) new SystemException(XAException.XAER_RMERR).initCause(e); } } } finally { fireAfterCompletion(Status.STATUS_ROLLEDBACK); } } private void fireBeforeCompletion() { for (Synchronization synchronization : synchronizations) { synchronization.beforeCompletion(); } } private void fireAfterCompletion(int status) { for (Synchronization synchronization : synchronizations) { synchronization.afterCompletion(status); } } private void fireInMiddleOf2PC() { for (TwoPcListener twoPcListener : twoPcListeners) { twoPcListener.inMiddleOf2PC(); } } @Override public void setRollbackOnly() throws IllegalStateException, SystemException { } } interface TwoPcListener { void inMiddleOf2PC(); } }