/* * ModeShape (http://www.modeshape.org) * * 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.modeshape.jcr.txn; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Stream; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.TransactionManager; import org.modeshape.common.logging.Logger; import org.modeshape.common.util.Reflection; import org.modeshape.jcr.JcrI18n; import org.modeshape.jcr.api.txn.TransactionManagerLookup; /** * ModeShape's default implementation of a {@link TransactionManagerLookup}. This is used by default when no other explicit * lookup instance is present and looks for a transaction manager in the following order: * <pre> * <ol> * <li>JNDI: it searches JNDI for a number of known bindings of transaction managers, specific most JEE containers</li> * <li>Standalone JBoss JTA: it searches for a local JBoss JTA instance</li> * <li>Atomikos JTA: it searches for a local Atomikos instance</li> * </ol> * </pre> * * If none of the above steps are able to locate a transaction manager, ModeShape will fall back to a {@link LocalTransactionManager} * instance. * * @author Horia Chiorean (hchiorea@redhat.com) * @since 5.0 */ public class DefaultTransactionManagerLookup implements TransactionManagerLookup { /** * A list of known JNDI binding for different transaction manager implementations * * see https://java.net/jira/browse/JAVAEE_SPEC-8 */ private static final List<String> JNDI_BINDINGS = Arrays.asList("java:jboss/TransactionManager", "java:comp/TransactionManager", "java:comp/UserTransaction", "java:/TransactionManager", "java:appserver/TransactionManager", "java:pm/TransactionManager", "javax.transaction.TransactionManager", "osgi:service/javax.transaction.TransactionManager"); private static final Logger LOGGER = Logger.getLogger(DefaultTransactionManagerLookup.class); @Override public TransactionManager getTransactionManager() { return Stream.of(getTransactionManagerSuppliers()) .map(Supplier::get) .filter(Optional::isPresent) .map(Optional::get) .peek((transactionManager) -> LOGGER.debug("Found tx manager '{0}'", transactionManager.getClass().getName())) .findFirst() .orElseGet(() -> { LOGGER.debug(JcrI18n.warnNoTxManagerFound.text()); return new LocalTransactionManager(); }); } @SuppressWarnings("unchecked") protected Supplier<Optional<TransactionManager>>[] getTransactionManagerSuppliers() { return new Supplier[] { (Supplier<Optional<TransactionManager>>) this::lookInJNDI, this::lookForStandaloneJBossJTA, this::lookForAtomikosJTA }; } protected Optional<TransactionManager> lookForAtomikosJTA() { LOGGER.debug("Looking for Atomikos JTA..."); try { Class<?> clazz = getClass().getClassLoader().loadClass("com.atomikos.icatch.jta.UserTransactionManager"); Object result = clazz.newInstance(); return result instanceof TransactionManager ? Optional.of((TransactionManager) result) : Optional.empty(); } catch (ClassNotFoundException e) { return Optional.empty(); } catch (Exception e) { LOGGER.debug(e, "Error while trying to instantiate an Atomikos JTA instance..."); return Optional.empty(); } } protected Optional<TransactionManager> lookForStandaloneJBossJTA() { LOGGER.debug("Looking for JBoss Standalone JTA..."); try { Class<?> clazz = getClass().getClassLoader().loadClass("com.arjuna.ats.jta.TransactionManager"); Method method = Reflection.findMethod(clazz, "transactionManager"); Object result = Reflection.invokeAccessibly(null, method, null); return result instanceof TransactionManager ? Optional.of((TransactionManager) result) : Optional.empty(); } catch (ClassNotFoundException e) { return Optional.empty(); } catch (Exception e) { LOGGER.debug(e, "Error while trying to instantiate a standalone JBoss JTA instance..."); return Optional.empty(); } } private Optional<TransactionManager> lookInJNDI() { return JNDI_BINDINGS.stream().map(this::lookForJNDIName).filter(Objects::nonNull).findFirst(); } private TransactionManager lookForJNDIName(String jndiName) { InitialContext context = null; try { context = new InitialContext(); } catch (NamingException e) { LOGGER.debug(e, "Cannot create initial JNDI context"); return null; } try { LOGGER.debug("Looking up transaction manager at: '{0}'", jndiName); Object obj = context.lookup(jndiName); if (obj instanceof TransactionManager) { return (TransactionManager)obj; } LOGGER.debug("Transaction manager not found at: '{0}'", jndiName); return null; } catch (NamingException e) { LOGGER.debug(e, "Failed to lookup '{0}' in JNDI", jndiName); return null; } finally { try { context.close(); } catch (NamingException e) { LOGGER.debug(e, "Cannot close JNDI context"); } } } }