/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Florent Guillaume, jcarsique */ package org.eclipse.ecr.runtime.jtajca; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.resource.ResourceException; import javax.resource.spi.ConnectionManager; import javax.resource.spi.ConnectionRequestInfo; import javax.resource.spi.ManagedConnectionFactory; import javax.security.auth.Subject; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import org.apache.geronimo.connector.outbound.AbstractConnectionManager; import org.apache.geronimo.connector.outbound.GenericConnectionManager; import org.apache.geronimo.connector.outbound.SubjectSource; import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PartitionedPool; import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PoolingSupport; import org.apache.geronimo.connector.outbound.connectionmanagerconfig.TransactionSupport; import org.apache.geronimo.connector.outbound.connectionmanagerconfig.XATransactions; import org.apache.geronimo.connector.outbound.connectiontracking.ConnectionTrackingCoordinator; import org.apache.geronimo.transaction.GeronimoUserTransaction; import org.apache.geronimo.transaction.manager.RecoverableTransactionManager; import org.apache.geronimo.transaction.manager.TransactionManagerImpl; import org.eclipse.ecr.runtime.transaction.TransactionHelper; /** * Internal helper for the Nuxeo-defined transaction manager and connection * manager. * <p> * This code is called by the factories registered through JNDI, or by unit * tests mimicking JNDI bindings. */ public class NuxeoContainer { public static final String JNDI_TRANSACTION_MANAGER = "java:comp/TransactionManager"; public static final String JNDI_USER_TRANSACTION = "java:comp/UserTransaction"; public static final String JNDI_NUXEO_CONNECTION_MANAGER = "java:comp/NuxeoConnectionManager"; private static RecoverableTransactionManager transactionManager; private static UserTransaction userTransaction; private static ConnectionManagerWrapper connectionManager; private static boolean isInstalled = false; private NuxeoContainer() { } /** * Install transaction and connection management "by hand" if the container * didn't do it using file-based configuration. Binds the names in JNDI. */ public static void install() throws NamingException { install(new TransactionManagerConfiguration(), new ConnectionManagerConfiguration()); } /** * Install transaction and connection management "by hand" if the container * didn't do it using file-based configuration. Binds the names in JNDI. * * @param txconfig the transaction manager configuration * @param cmconfig the connection manager configuration * * @since 5.4.2 */ public static synchronized void install(TransactionManagerConfiguration txconfig, ConnectionManagerConfiguration cmconfig) throws NamingException { initTransactionManager(txconfig); initConnectionManager(cmconfig); jndiBind(); isInstalled = true; } public static synchronized boolean isInstalled() { return isInstalled; } public static synchronized void uninstall() { if (!isInstalled) { return; } isInstalled = false; try { InitialContext context = new InitialContext(); context.unbind(JNDI_TRANSACTION_MANAGER); context.unbind(JNDI_USER_TRANSACTION); context.unbind(JNDI_NUXEO_CONNECTION_MANAGER); } catch (Exception e) { // do nothing } finally { transactionManager = null; userTransaction = null; connectionManager = null; } } protected static void jndiBind() throws NamingException { InitialContext context = new InitialContext(); context.rebind(JNDI_TRANSACTION_MANAGER, getTransactionManager()); context.rebind(JNDI_USER_TRANSACTION, getUserTransaction()); context.rebind(JNDI_NUXEO_CONNECTION_MANAGER, getConnectionManager()); } /** @deprecated use {@link #install} instead. */ @Deprecated public static void initTransactionManagement() throws NamingException { install(); } /** * Gets the transaction manager used by the container. * * @return the transaction manager */ public static TransactionManager getTransactionManager() { return transactionManager; } /** * Gets the user transaction used by the container. * * @return the user transaction */ public static UserTransaction getUserTransaction() throws NamingException { if (transactionManager == null) { initTransactionManager(); } return userTransaction; } /** * Gets the Nuxeo connection manager used by the container. * * @return the connection manager */ public static ConnectionManager getConnectionManager() { return connectionManager; } protected static void initTransactionManager() throws NamingException { // doing a lookup will initialize it with its configuration parameters TransactionHelper.lookupTransactionManager(); } public static synchronized void initTransactionManager( TransactionManagerConfiguration config) { if (transactionManager == null) { transactionManager = createTransactionManager(config); userTransaction = createUserTransaction(transactionManager); } } public static synchronized void initConnectionManager( ConnectionManagerConfiguration config) throws NamingException { if (transactionManager == null) { initTransactionManager(); } if (connectionManager == null) { AbstractConnectionManager cm = createConnectionManager( transactionManager, config); connectionManager = new ConnectionManagerWrapper(cm, config); } } public static synchronized void resetConnectionManager() throws Exception { ConnectionManagerWrapper cm = connectionManager; if (cm == null) { return; } cm.reset(); } protected static RecoverableTransactionManager createTransactionManager( TransactionManagerConfiguration config) { try { return new TransactionManagerImpl(config.transactionTimeoutSeconds); } catch (Exception e) { // failed in recovery somewhere throw new RuntimeException(e.toString(), e); } } protected static UserTransaction createUserTransaction( TransactionManager transactionManager) { return new GeronimoUserTransaction(transactionManager); } /** * Creates a Geronimo pooled connection manager using a Geronimo transaction * manager. * <p> * The pool uses the transaction manager for recovery, and when using * XATransactions for cache + enlist/delist. */ protected static AbstractConnectionManager createConnectionManager( RecoverableTransactionManager transactionManager, ConnectionManagerConfiguration config) { TransactionSupport transactionSupport = new XATransactions( config.useTransactionCaching, config.useThreadCaching); // note: XATransactions -> TransactionCachingInterceptor -> // ConnectorTransactionContext casts transaction to Geronimo's // TransactionImpl (from TransactionManagerImpl) PoolingSupport poolingSupport = new PartitionedPool(config.maxPoolSize, config.minPoolSize, config.blockingTimeoutMillis, config.idleTimeoutMinutes, config.matchOne, config.matchAll, config.selectOneNoMatch, config.partitionByConnectionRequestInfo, config.partitionBySubject); final Subject subject = new Subject(); SubjectSource subjectSource = new SubjectSource() { @Override public Subject getSubject() { return subject; } }; ConnectionTrackingCoordinator connectionTracker = new ConnectionTrackingCoordinator(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // NuxeoContainer.class.getClassLoader(); return new GenericConnectionManager(transactionSupport, poolingSupport, subjectSource, connectionTracker, transactionManager, config.name, classLoader); } public static class TransactionManagerConfiguration { public int transactionTimeoutSeconds = 600; public void setTransactionTimeoutSeconds(int transactionTimeoutSeconds) { this.transactionTimeoutSeconds = transactionTimeoutSeconds; } } /** * Wraps a Geronimo ConnectionManager and adds a {@link #reset} method to * flush the pool. */ public static class ConnectionManagerWrapper implements ConnectionManager { private static final long serialVersionUID = 1L; protected AbstractConnectionManager cm; protected final ConnectionManagerConfiguration config; public ConnectionManagerWrapper(AbstractConnectionManager cm, ConnectionManagerConfiguration config) { this.cm = cm; this.config = config; } @Override public Object allocateConnection( ManagedConnectionFactory managedConnectionFactory, ConnectionRequestInfo connectionRequestInfo) throws ResourceException { return cm.allocateConnection(managedConnectionFactory, connectionRequestInfo); } public void reset() throws Exception { cm.doStop(); cm = createConnectionManager(transactionManager, config); } } public static class ConnectionManagerConfiguration { public String name = "NuxeoConnectionManager"; // transaction public boolean useTransactionCaching = true; public boolean useThreadCaching = true; // pool public boolean matchOne = true; // unused by Geronimo? public boolean matchAll = true; public boolean selectOneNoMatch = false; public boolean partitionByConnectionRequestInfo = false; public boolean partitionBySubject = true; public int maxPoolSize = 20; public int minPoolSize = 0; public int blockingTimeoutMillis = 100; public int idleTimeoutMinutes = 0; // no timeout public void setName(String name) { this.name = name; } public void setUseTransactionCaching(boolean useTransactionCaching) { this.useTransactionCaching = useTransactionCaching; } public void setUseThreadCaching(boolean useThreadCaching) { this.useThreadCaching = useThreadCaching; } public void setMatchOne(boolean matchOne) { this.matchOne = matchOne; } public void setMatchAll(boolean matchAll) { this.matchAll = matchAll; } public void setSelectOneNoMatch(boolean selectOneNoMatch) { this.selectOneNoMatch = selectOneNoMatch; } public void setPartitionByConnectionRequestInfo( boolean partitionByConnectionRequestInfo) { this.partitionByConnectionRequestInfo = partitionByConnectionRequestInfo; } public void setPartitionBySubject(boolean partitionBySubject) { this.partitionBySubject = partitionBySubject; } public void setMaxPoolSize(int maxPoolSize) { this.maxPoolSize = maxPoolSize; } public void setMinPoolSize(int minPoolSize) { this.minPoolSize = minPoolSize; } public void setBlockingTimeoutMillis(int blockingTimeoutMillis) { this.blockingTimeoutMillis = blockingTimeoutMillis; } public void setIdleTimeoutMinutes(int idleTimeoutMinutes) { this.idleTimeoutMinutes = idleTimeoutMinutes; } } }