diff --git a/btm/src/main/java/bitronix/tm/ServicesFactory.java b/btm/src/main/java/bitronix/tm/ServicesFactory.java new file mode 100644 index 00000000..b0ef7f5e --- /dev/null +++ b/btm/src/main/java/bitronix/tm/ServicesFactory.java @@ -0,0 +1,104 @@ +package bitronix.tm; + +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.BitronixTransactionSynchronizationRegistry; +import bitronix.tm.Configuration; +import bitronix.tm.TransactionManagerServices; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import bitronix.tm.journal.DiskJournal; +import bitronix.tm.journal.Journal; +import bitronix.tm.journal.NullJournal; +import bitronix.tm.recovery.Recoverer; +import bitronix.tm.resource.ResourceLoader; +import bitronix.tm.timer.TaskScheduler; +import bitronix.tm.twopc.executor.AsyncExecutor; +import bitronix.tm.twopc.executor.Executor; +import bitronix.tm.twopc.executor.SyncExecutor; +import bitronix.tm.utils.ClassLoaderUtils; +import bitronix.tm.utils.DefaultExceptionAnalyzer; +import bitronix.tm.utils.ExceptionAnalyzer; +import bitronix.tm.utils.InitializationException; + +/** + * Factory of services. + */ +public class ServicesFactory { + private final static Logger log = LoggerFactory.getLogger(TransactionManagerServices.class); + + public static BitronixTransactionManager crateTransactionManager() { + return new BitronixTransactionManager(); + } + + public static BitronixTransactionSynchronizationRegistry createTransactionSynchronizationRegistry() { + return new BitronixTransactionSynchronizationRegistry(); + } + + public static Configuration createConfiguration() { + return new Configuration(); + } + + public static Journal createJournal(Journal journal, String configuredJournal) throws InitializationException { + if ("null".equals(configuredJournal) || null == configuredJournal) { + journal = new NullJournal(); + } else if ("disk".equals(configuredJournal)) { + journal = new DiskJournal(); + } else { + try { + Class clazz = ClassLoaderUtils.loadClass(configuredJournal); + journal = (Journal) clazz.newInstance(); + } catch (Exception ex) { + throw new InitializationException("invalid journal implementation '" + configuredJournal + "'", ex); + } + } + if (log.isDebugEnabled()) { + log.debug("using journal " + configuredJournal); + } + return journal; + } + + public static TaskScheduler createTaskScheduler() { + return new TaskScheduler(); + } + + public static ResourceLoader createResourceLoader() { + return new ResourceLoader(); + } + + public static Recoverer createRecoverer() { + return new Recoverer(); + } + + public static Executor createExecutor(boolean isAsynchronouse2Pc) { + Executor executor; + if (isAsynchronouse2Pc) { + if (log.isDebugEnabled()) { + log.debug("using AsyncExecutor"); + } + executor = new AsyncExecutor(); + } else { + if (log.isDebugEnabled()) { + log.debug("using SyncExecutor"); + } + executor = new SyncExecutor(); + } + return executor; + } + + public static ExceptionAnalyzer createExceptionAnalyser(String exceptionAnalyzerName) { + ExceptionAnalyzer analyzer; + analyzer = new DefaultExceptionAnalyzer(); + if (exceptionAnalyzerName != null) { + try { + analyzer = (ExceptionAnalyzer) ClassLoaderUtils.loadClass(exceptionAnalyzerName).newInstance(); + } catch (Exception ex) { + log.warn("failed to initialize custom exception analyzer, using default one instead", ex); + } + } + return analyzer; + } + + +} diff --git a/btm/src/main/java/bitronix/tm/TransactionManagerServices.java b/btm/src/main/java/bitronix/tm/TransactionManagerServices.java index 7b5ac543..3611f1fd 100644 --- a/btm/src/main/java/bitronix/tm/TransactionManagerServices.java +++ b/btm/src/main/java/bitronix/tm/TransactionManagerServices.java @@ -15,25 +15,18 @@ */ package bitronix.tm; -import bitronix.tm.journal.DiskJournal; +import java.util.Iterator; +import java.util.ServiceLoader; + import bitronix.tm.journal.Journal; -import bitronix.tm.journal.NullJournal; import bitronix.tm.recovery.Recoverer; import bitronix.tm.resource.ResourceLoader; +import bitronix.tm.spi.BitronixContext; +import bitronix.tm.spi.DefaultBitronixContext; import bitronix.tm.timer.TaskScheduler; -import bitronix.tm.twopc.executor.AsyncExecutor; import bitronix.tm.twopc.executor.Executor; -import bitronix.tm.twopc.executor.SyncExecutor; -import bitronix.tm.utils.ClassLoaderUtils; -import bitronix.tm.utils.DefaultExceptionAnalyzer; import bitronix.tm.utils.ExceptionAnalyzer; -import bitronix.tm.utils.InitializationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * Container for all BTM services. @@ -45,34 +38,23 @@ */ public class TransactionManagerServices { - private final static Logger log = LoggerFactory.getLogger(TransactionManagerServices.class); - - private static final Lock transactionManagerLock = new ReentrantLock(); - private static volatile BitronixTransactionManager transactionManager; + private final static BitronixContext context; - private static final AtomicReference transactionSynchronizationRegistryRef = new AtomicReference(); - private static final AtomicReference configurationRef = new AtomicReference(); - private static final AtomicReference journalRef = new AtomicReference(); - private static final AtomicReference taskSchedulerRef = new AtomicReference(); - private static final AtomicReference resourceLoaderRef = new AtomicReference(); - private static final AtomicReference recovererRef = new AtomicReference(); - private static final AtomicReference executorRef = new AtomicReference(); - private static final AtomicReference exceptionAnalyzerRef = new AtomicReference(); + static { + Iterator iterator = ServiceLoader.load(BitronixContext.class).iterator(); + if (iterator.hasNext()) { + context = iterator.next(); + } else { + context = new DefaultBitronixContext(); + } + } /** * Create an initialized transaction manager. * @return the transaction manager. */ public static BitronixTransactionManager getTransactionManager() { - transactionManagerLock.lock(); - try { - if (transactionManager == null) { - transactionManager = new BitronixTransactionManager(); - } - return transactionManager; - } finally { - transactionManagerLock.unlock(); - } + return context.getTransactionManager(); } /** @@ -80,14 +62,7 @@ public static BitronixTransactionManager getTransactionManager() { * @return the TransactionSynchronizationRegistry. */ public static BitronixTransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { - BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = transactionSynchronizationRegistryRef.get(); - if (transactionSynchronizationRegistry == null) { - transactionSynchronizationRegistry = new BitronixTransactionSynchronizationRegistry(); - if (!transactionSynchronizationRegistryRef.compareAndSet(null, transactionSynchronizationRegistry)) { - transactionSynchronizationRegistry = transactionSynchronizationRegistryRef.get(); - } - } - return transactionSynchronizationRegistry; + return context.getTransactionSynchronizationRegistry(); } /** @@ -95,14 +70,7 @@ public static BitronixTransactionSynchronizationRegistry getTransactionSynchroni * @return the global configuration. */ public static Configuration getConfiguration() { - Configuration configuration = configurationRef.get(); - if (configuration == null) { - configuration = new Configuration(); - if (!configurationRef.compareAndSet(null, configuration)) { - configuration = configurationRef.get(); - } - } - return configuration; + return context.getConfiguration(); } /** @@ -110,28 +78,7 @@ public static Configuration getConfiguration() { * @return the transactions journal. */ public static Journal getJournal() { - Journal journal = journalRef.get(); - if (journal == null) { - String configuredJournal = getConfiguration().getJournal(); - if ("null".equals(configuredJournal) || null == configuredJournal) { - journal = new NullJournal(); - } else if ("disk".equals(configuredJournal)) { - journal = new DiskJournal(); - } else { - try { - Class clazz = ClassLoaderUtils.loadClass(configuredJournal); - journal = (Journal) clazz.newInstance(); - } catch (Exception ex) { - throw new InitializationException("invalid journal implementation '" + configuredJournal + "'", ex); - } - } - if (log.isDebugEnabled()) { log.debug("using journal " + configuredJournal); } - - if (!journalRef.compareAndSet(null, journal)) { - journal = journalRef.get(); - } - } - return journal; + return context.getJournal(); } /** @@ -139,16 +86,7 @@ public static Journal getJournal() { * @return the task scheduler. */ public static TaskScheduler getTaskScheduler() { - TaskScheduler taskScheduler = taskSchedulerRef.get(); - if (taskScheduler == null) { - taskScheduler = new TaskScheduler(); - if (!taskSchedulerRef.compareAndSet(null, taskScheduler)) { - taskScheduler = taskSchedulerRef.get(); - } else { - taskScheduler.start(); - } - } - return taskScheduler; + return context.getTaskScheduler(); } /** @@ -156,14 +94,7 @@ public static TaskScheduler getTaskScheduler() { * @return the resource loader. */ public static ResourceLoader getResourceLoader() { - ResourceLoader resourceLoader = resourceLoaderRef.get(); - if (resourceLoader == null) { - resourceLoader = new ResourceLoader(); - if (!resourceLoaderRef.compareAndSet(null, resourceLoader)) { - resourceLoader = resourceLoaderRef.get(); - } - } - return resourceLoader; + return context.getResourceLoader(); } /** @@ -171,14 +102,7 @@ public static ResourceLoader getResourceLoader() { * @return the transaction recoverer. */ public static Recoverer getRecoverer() { - Recoverer recoverer = recovererRef.get(); - if (recoverer == null) { - recoverer = new Recoverer(); - if (!recovererRef.compareAndSet(null, recoverer)) { - recoverer = recovererRef.get(); - } - } - return recoverer; + return context.getRecoverer(); } /** @@ -186,45 +110,15 @@ public static Recoverer getRecoverer() { * @return the 2PC executor. */ public static Executor getExecutor() { - Executor executor = executorRef.get(); - if (executor == null) { - if (getConfiguration().isAsynchronous2Pc()) { - if (log.isDebugEnabled()) { log.debug("using AsyncExecutor"); } - executor = new AsyncExecutor(); - } else { - if (log.isDebugEnabled()) { log.debug("using SyncExecutor"); } - executor = new SyncExecutor(); - } - if (!executorRef.compareAndSet(null, executor)) { - executor.shutdown(); - executor = executorRef.get(); - } - } - return executor; + return context.getExecutor(); } /** * Create the exception analyzer. * @return the exception analyzer. */ - public static ExceptionAnalyzer getExceptionAnalyzer() { - ExceptionAnalyzer analyzer = exceptionAnalyzerRef.get(); - if (analyzer == null) { - String exceptionAnalyzerName = getConfiguration().getExceptionAnalyzer(); - analyzer = new DefaultExceptionAnalyzer(); - if (exceptionAnalyzerName != null) { - try { - analyzer = (ExceptionAnalyzer) ClassLoaderUtils.loadClass(exceptionAnalyzerName).newInstance(); - } catch (Exception ex) { - log.warn("failed to initialize custom exception analyzer, using default one instead", ex); - } - } - if (!exceptionAnalyzerRef.compareAndSet(null, analyzer)) { - analyzer.shutdown(); - analyzer = exceptionAnalyzerRef.get(); - } - } - return analyzer; + public static ExceptionAnalyzer getExceptionAnalyzer() { + return context.getExceptionAnalyzer(); } /** @@ -232,7 +126,7 @@ public static ExceptionAnalyzer getExceptionAnalyzer() { * @return true if the transaction manager has started. */ public static boolean isTransactionManagerRunning() { - return transactionManager != null; + return context.isTransactionManagerRunning(); } /** @@ -240,23 +134,14 @@ public static boolean isTransactionManagerRunning() { * @return true if the task scheduler has started. */ public static boolean isTaskSchedulerRunning() { - return taskSchedulerRef.get() != null; + return context.isTaskSchedulerRunning(); } /** * Clear services references. Called at the end of the shutdown procedure. */ protected static synchronized void clear() { - transactionManager = null; - - transactionSynchronizationRegistryRef.set(null); - configurationRef.set(null); - journalRef.set(null); - taskSchedulerRef.set(null); - resourceLoaderRef.set(null); - recovererRef.set(null); - executorRef.set(null); - exceptionAnalyzerRef.set(null); + context.clear(); } } diff --git a/btm/src/main/java/bitronix/tm/spi/BitronixContext.java b/btm/src/main/java/bitronix/tm/spi/BitronixContext.java new file mode 100644 index 00000000..0120b2da --- /dev/null +++ b/btm/src/main/java/bitronix/tm/spi/BitronixContext.java @@ -0,0 +1,87 @@ +package bitronix.tm.spi; + +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.BitronixTransactionSynchronizationRegistry; +import bitronix.tm.Configuration; +import bitronix.tm.journal.Journal; +import bitronix.tm.recovery.Recoverer; +import bitronix.tm.resource.ResourceLoader; +import bitronix.tm.timer.TaskScheduler; +import bitronix.tm.twopc.executor.Executor; +import bitronix.tm.utils.ExceptionAnalyzer; + +/** + * Keeps services instances. + */ +public interface BitronixContext { + /** + * Create an initialized transaction manager. + * @return the transaction manager. + */ + BitronixTransactionManager getTransactionManager(); + + /** + * Create the JTA 1.1 TransactionSynchronizationRegistry. + * @return the TransactionSynchronizationRegistry. + */ + BitronixTransactionSynchronizationRegistry getTransactionSynchronizationRegistry(); + + /** + * Create the configuration of all the components of the transaction manager. + * @return the global configuration. + */ + Configuration getConfiguration(); + + /** + * Create the transactions journal. + * @return the transactions journal. + */ + Journal getJournal(); + + /** + * Create the task scheduler. + * @return the task scheduler. + */ + TaskScheduler getTaskScheduler(); + + /** + * Create the resource loader. + * @return the resource loader. + */ + ResourceLoader getResourceLoader(); + + /** + * Create the transaction recoverer. + * @return the transaction recoverer. + */ + Recoverer getRecoverer(); + + /** + * Create the 2PC executor. + * @return the 2PC executor. + */ + Executor getExecutor(); + + /** + * Create the exception analyzer. + * @return the exception analyzer. + */ + ExceptionAnalyzer getExceptionAnalyzer(); + + /** + * Check if the transaction manager has started. + * @return true if the transaction manager has started. + */ + boolean isTransactionManagerRunning(); + + /** + * Check if the task scheduler has started. + * @return true if the task scheduler has started. + */ + boolean isTaskSchedulerRunning(); + + /** + * Clear services references. Called at the end of the shutdown procedure. + */ + void clear(); +} diff --git a/btm/src/main/java/bitronix/tm/spi/DefaultBitronixContext.java b/btm/src/main/java/bitronix/tm/spi/DefaultBitronixContext.java new file mode 100644 index 00000000..20b43a2e --- /dev/null +++ b/btm/src/main/java/bitronix/tm/spi/DefaultBitronixContext.java @@ -0,0 +1,173 @@ +package bitronix.tm.spi; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.BitronixTransactionSynchronizationRegistry; +import bitronix.tm.Configuration; +import bitronix.tm.ServicesFactory; +import bitronix.tm.journal.Journal; +import bitronix.tm.recovery.Recoverer; +import bitronix.tm.resource.ResourceLoader; +import bitronix.tm.timer.TaskScheduler; +import bitronix.tm.twopc.executor.Executor; +import bitronix.tm.utils.ExceptionAnalyzer; + +/** + * Default context which keeps one instance of each service. + */ +public class DefaultBitronixContext implements BitronixContext { + private final Lock transactionManagerLock = new ReentrantLock(); + private volatile BitronixTransactionManager transactionManager; + + private final AtomicReference transactionSynchronizationRegistryRef = new AtomicReference(); + private final AtomicReference configurationRef = new AtomicReference(); + private final AtomicReference journalRef = new AtomicReference(); + private final AtomicReference taskSchedulerRef = new AtomicReference(); + private final AtomicReference resourceLoaderRef = new AtomicReference(); + private final AtomicReference recovererRef = new AtomicReference(); + private final AtomicReference executorRef = new AtomicReference(); + private final AtomicReference exceptionAnalyzerRef = new AtomicReference(); + + @Override + public BitronixTransactionManager getTransactionManager() { + transactionManagerLock.lock(); + try { + if (transactionManager == null) { + transactionManager = ServicesFactory.crateTransactionManager(); + } + return transactionManager; + } finally { + transactionManagerLock.unlock(); + } + } + + @Override + public BitronixTransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { + BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = transactionSynchronizationRegistryRef.get(); + if (transactionSynchronizationRegistry == null) { + transactionSynchronizationRegistry = ServicesFactory.createTransactionSynchronizationRegistry(); + if (!transactionSynchronizationRegistryRef.compareAndSet(null, transactionSynchronizationRegistry)) { + transactionSynchronizationRegistry = transactionSynchronizationRegistryRef.get(); + } + } + return transactionSynchronizationRegistry; + } + + @Override + public Configuration getConfiguration() { + Configuration configuration = configurationRef.get(); + if (configuration == null) { + configuration = ServicesFactory.createConfiguration(); + if (!configurationRef.compareAndSet(null, configuration)) { + configuration = configurationRef.get(); + } + } + return configuration; + } + + @Override + public Journal getJournal() { + Journal journal = journalRef.get(); + if (journal == null) { + String configuredJournal = getConfiguration().getJournal(); + journal = ServicesFactory.createJournal(journal, configuredJournal); + + if (!journalRef.compareAndSet(null, journal)) { + journal = journalRef.get(); + } + } + return journal; + } + + @Override + public TaskScheduler getTaskScheduler() { + TaskScheduler taskScheduler = taskSchedulerRef.get(); + if (taskScheduler == null) { + taskScheduler = ServicesFactory.createTaskScheduler(); + if (!taskSchedulerRef.compareAndSet(null, taskScheduler)) { + taskScheduler = taskSchedulerRef.get(); + } else { + taskScheduler.start(); + } + } + return taskScheduler; + } + + @Override + public ResourceLoader getResourceLoader() { + ResourceLoader resourceLoader = resourceLoaderRef.get(); + if (resourceLoader == null) { + resourceLoader = ServicesFactory.createResourceLoader(); + if (!resourceLoaderRef.compareAndSet(null, resourceLoader)) { + resourceLoader = resourceLoaderRef.get(); + } + } + return resourceLoader; + } + + @Override + public Recoverer getRecoverer() { + Recoverer recoverer = recovererRef.get(); + if (recoverer == null) { + recoverer = ServicesFactory.createRecoverer(); + if (!recovererRef.compareAndSet(null, recoverer)) { + recoverer = recovererRef.get(); + } + } + return recoverer; + } + + @Override + public Executor getExecutor() { + Executor executor = executorRef.get(); + if (executor == null) { + executor = ServicesFactory.createExecutor(getConfiguration().isAsynchronous2Pc()); + if (!executorRef.compareAndSet(null, executor)) { + executor.shutdown(); + executor = executorRef.get(); + } + } + return executor; + } + + @Override + public ExceptionAnalyzer getExceptionAnalyzer() { + ExceptionAnalyzer analyzer = exceptionAnalyzerRef.get(); + if (analyzer == null) { + String exceptionAnalyzerName = getConfiguration().getExceptionAnalyzer(); + analyzer = ServicesFactory.createExceptionAnalyser(exceptionAnalyzerName); + if (!exceptionAnalyzerRef.compareAndSet(null, analyzer)) { + analyzer.shutdown(); + analyzer = exceptionAnalyzerRef.get(); + } + } + return analyzer; + } + + @Override + public boolean isTransactionManagerRunning() { + return transactionManager != null; + } + + @Override + public boolean isTaskSchedulerRunning() { + return taskSchedulerRef.get() != null; + } + + @Override + public synchronized void clear() { + transactionManager = null; + + transactionSynchronizationRegistryRef.set(null); + configurationRef.set(null); + journalRef.set(null); + taskSchedulerRef.set(null); + resourceLoaderRef.set(null); + recovererRef.set(null); + executorRef.set(null); + exceptionAnalyzerRef.set(null); + } +}