/*
* 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.docs.transactions.xa;
import bitronix.tm.BitronixTransactionManager;
import bitronix.tm.TransactionManagerServices;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.Configuration;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.impl.config.loaderwriter.DefaultCacheLoaderWriterConfiguration;
import org.ehcache.impl.config.persistence.CacheManagerPersistenceConfiguration;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.transactions.xa.txmgr.btm.BitronixTransactionManagerLookup;
import org.ehcache.transactions.xa.txmgr.provider.LookupTransactionManagerProviderConfiguration;
import org.ehcache.xml.XmlConfiguration;
import org.ehcache.spi.loaderwriter.BulkCacheWritingException;
import org.ehcache.spi.loaderwriter.CacheLoaderWriter;
import org.ehcache.transactions.xa.XACacheException;
import org.ehcache.transactions.xa.configuration.XAStoreConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static java.util.Collections.singletonMap;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* @author Ludovic Orban
*/
public class XAGettingStarted {
@Before
public void setUp() throws Exception {
TransactionManagerServices.getConfiguration().setJournal("null").setServerId(getClass().getSimpleName());
}
@After
public void tearDown() throws Exception {
if (TransactionManagerServices.isTransactionManagerRunning()) {
TransactionManagerServices.getTransactionManager().shutdown();
}
}
@Test
public void testSimpleXACache() throws Exception {
// tag::testSimpleXACache[]
BitronixTransactionManager transactionManager =
TransactionManagerServices.getTransactionManager(); // <1>
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2>
.withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <3>
ResourcePoolsBuilder.heap(10)) // <4>
.add(new XAStoreConfiguration("xaCache")) // <5>
.build()
)
.build(true);
final Cache<Long, String> xaCache = cacheManager.getCache("xaCache", Long.class, String.class);
transactionManager.begin(); // <6>
{
xaCache.put(1L, "one"); // <7>
}
transactionManager.commit(); // <8>
cacheManager.close();
transactionManager.shutdown();
// end::testSimpleXACache[]
}
@Test
public void testNonTransactionalAccess() throws Exception {
// tag::testNonTransactionalAccess[]
BitronixTransactionManager transactionManager =
TransactionManagerServices.getTransactionManager(); // <1>
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2>
.withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <3>
ResourcePoolsBuilder.heap(10)) // <4>
.add(new XAStoreConfiguration("xaCache")) // <5>
.build()
)
.build(true);
final Cache<Long, String> xaCache = cacheManager.getCache("xaCache", Long.class, String.class);
try {
xaCache.get(1L); // <6>
fail("expected XACacheException");
} catch (XACacheException e) {
// expected
}
cacheManager.close();
transactionManager.shutdown();
// end::testNonTransactionalAccess[]
}
@Test
@SuppressWarnings("unchecked")
public void testXACacheWithWriteThrough() throws Exception {
// tag::testXACacheWithWriteThrough[]
BitronixTransactionManager transactionManager =
TransactionManagerServices.getTransactionManager(); // <1>
Class<CacheLoaderWriter<?, ?>> klazz = (Class<CacheLoaderWriter<?, ?>>) (Class) (SampleLoaderWriter.class);
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2>
.withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <3>
ResourcePoolsBuilder.heap(10)) // <4>
.add(new XAStoreConfiguration("xaCache")) // <5>
.add(new DefaultCacheLoaderWriterConfiguration(klazz, singletonMap(1L, "eins"))) // <6>
.build()
)
.build(true);
final Cache<Long, String> xaCache = cacheManager.getCache("xaCache", Long.class, String.class);
transactionManager.begin(); // <7>
{
assertThat(xaCache.get(1L), equalTo("eins")); // <8>
xaCache.put(1L, "one"); // <9>
}
transactionManager.commit(); // <10>
cacheManager.close();
transactionManager.shutdown();
// end::testXACacheWithWriteThrough[]
}
@Test
public void testXACacheWithThreeTiers() throws Exception {
// tag::testXACacheWithThreeTiers[]
BitronixTransactionManager transactionManager =
TransactionManagerServices.getTransactionManager(); // <1>
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.using(new LookupTransactionManagerProviderConfiguration(BitronixTransactionManagerLookup.class)) // <2>
.with(new CacheManagerPersistenceConfiguration(new File(getStoragePath(), "testXACacheWithThreeTiers"))) // <3>
.withCache("xaCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, // <4>
ResourcePoolsBuilder.newResourcePoolsBuilder() // <5>
.heap(10, EntryUnit.ENTRIES)
.offheap(10, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true)
)
.add(new XAStoreConfiguration("xaCache")) // <6>
.build()
)
.build(true);
final Cache<Long, String> xaCache = persistentCacheManager.getCache("xaCache", Long.class, String.class);
transactionManager.begin(); // <7>
{
xaCache.put(1L, "one"); // <8>
}
transactionManager.commit(); // <9>
persistentCacheManager.close();
transactionManager.shutdown();
// end::testXACacheWithThreeTiers[]
}
@Test
public void testXACacheWithXMLConfig() throws Exception {
// tag::testXACacheWithXMLConfig[]
BitronixTransactionManager transactionManager =
TransactionManagerServices.getTransactionManager(); // <1>
URL myUrl = this.getClass().getResource("/docs/configs/xa-getting-started.xml"); // <2>
Configuration xmlConfig = new XmlConfiguration(myUrl); // <3>
CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig); // <4>
myCacheManager.init();
myCacheManager.close();
transactionManager.shutdown();
// end::testXACacheWithXMLConfig[]
}
private String getStoragePath() throws URISyntaxException {
return getClass().getClassLoader().getResource(".").toURI().getPath();
}
public static class SampleLoaderWriter<K, V> implements CacheLoaderWriter<K, V> {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleLoaderWriter.class);
private final Map<K, V> data = new HashMap<K, V>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public SampleLoaderWriter(Map<K, V> initialData) {
data.putAll(initialData);
}
@Override
public V load(K key) throws Exception {
lock.readLock().lock();
try {
V v = data.get(key);
LOGGER.info("Key - '{}', Value - '{}' successfully loaded", key, v);
return v;
} finally {
lock.readLock().unlock();
}
}
@Override
public Map<K, V> loadAll(Iterable<? extends K> keys) throws Exception {
throw new UnsupportedOperationException("Implement me!");
}
@Override
public void write(K key, V value) throws Exception {
lock.writeLock().lock();
try {
data.put(key, value);
LOGGER.info("Key - '{}', Value - '{}' successfully written", key, value);
} finally {
lock.writeLock().unlock();
}
}
@Override
public void writeAll(Iterable<? extends Map.Entry<? extends K, ? extends V>> entries) throws BulkCacheWritingException, Exception {
lock.writeLock().lock();
try {
for (Map.Entry<? extends K, ? extends V> entry : entries) {
data.put(entry.getKey(), entry.getValue());
LOGGER.info("Key - '{}', Value - '{}' successfully written in batch", entry.getKey(), entry.getValue());
}
} finally {
lock.writeLock().unlock();
}
}
@Override
public void delete(K key) throws Exception {
lock.writeLock().lock();
try {
data.remove(key);
LOGGER.info("Key - '{}' successfully deleted", key);
} finally {
lock.writeLock().unlock();
}
}
@Override
public void deleteAll(Iterable<? extends K> keys) throws BulkCacheWritingException, Exception {
lock.writeLock().lock();
try {
for (K key : keys) {
data.remove(key);
LOGGER.info("Key - '{}' successfully deleted in batch", key);
}
} finally {
lock.writeLock().unlock();
}
}
}
}