org.nuxeo.runtime.jtajca.NuxeoContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.runtime.jtajca.NuxeoContainer.java

Source

/*
 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * 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.
 *
 * Contributors:
 *     Florent Guillaume
 *     Julien Carsique
 */
package org.nuxeo.runtime.jtajca;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.spi.NamingManager;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnectionFactory;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;
import javax.transaction.UserTransaction;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.connector.outbound.AbstractConnectionManager;
import org.apache.geronimo.connector.outbound.connectionmanagerconfig.LocalTransactions;
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.transaction.manager.NamedXAResourceFactory;
import org.apache.geronimo.transaction.manager.RecoverableTransactionManager;
import org.apache.geronimo.transaction.manager.TransactionImpl;
import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
import org.apache.xbean.naming.reference.SimpleReference;
import org.nuxeo.common.logging.SequenceTracer;
import org.nuxeo.common.utils.ExceptionUtils;
import org.nuxeo.runtime.jtajca.NuxeoConnectionManager.ActiveMonitor;
import org.nuxeo.runtime.metrics.MetricsService;
import org.nuxeo.runtime.transaction.TransactionHelper;

import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.codahale.metrics.Timer;

/**
 * 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 {

    protected static final Log log = LogFactory.getLog(NuxeoContainer.class);

    protected static RecoverableTransactionManager tmRecoverable;

    protected static TransactionManager tm;

    protected static TransactionSynchronizationRegistry tmSynchRegistry;

    protected static UserTransaction ut;

    protected static Map<String, ConnectionManagerWrapper> connectionManagers = new ConcurrentHashMap<>(8, 0.75f,
            2);

    private static final List<NuxeoContainerListener> listeners = new ArrayList<>();

    private static volatile InstallContext installContext;

    protected static Context rootContext;

    protected static Context parentContext;

    protected static String jndiPrefix = "java:comp/env/";

    // @since 5.7
    protected static final MetricRegistry registry = SharedMetricRegistries
            .getOrCreate(MetricsService.class.getName());

    protected static final Counter rollbackCount = registry
            .counter(MetricRegistry.name("nuxeo", "transactions", "rollbacks"));

    protected static final Counter concurrentCount = registry
            .counter(MetricRegistry.name("nuxeo", "transactions", "concurrents", "count"));

    protected static final Counter concurrentMaxCount = registry
            .counter(MetricRegistry.name("nuxeo", "transactions", "concurrents", "max"));

    protected static final Timer transactionTimer = registry
            .timer(MetricRegistry.name("nuxeo", "transactions", "duration"));

    protected static final ConcurrentHashMap<Transaction, Timer.Context> timers = new ConcurrentHashMap<>();

    private NuxeoContainer() {
    }

    public static class InstallContext extends Throwable {
        private static final long serialVersionUID = 1L;

        public final String threadName;

        InstallContext() {
            super("Container installation context (" + Thread.currentThread().getName() + ")");
            threadName = Thread.currentThread().getName();
        }
    }

    /**
     * Install naming and bind transaction and connection management factories "by hand".
     */
    protected static void install() throws NamingException {
        if (installContext != null) {
            throw new RuntimeException("Nuxeo container already installed");
        }
        installContext = new InstallContext();
        log.trace("Installing nuxeo container", installContext);
        rootContext = new NamingContext();
        parentContext = InitialContextAccessor.getInitialContext();
        if (parentContext != null && parentContext != rootContext) {
            installTransactionManager(parentContext);
        } else {
            addDeepBinding(nameOf("TransactionManager"), new Reference(TransactionManager.class.getName(),
                    NuxeoTransactionManagerFactory.class.getName(), null));
            installTransactionManager(rootContext);
        }
    }

    protected static void installTransactionManager(TransactionManagerConfiguration config) throws NamingException {
        initTransactionManager(config);
        addDeepBinding(rootContext, new CompositeName(nameOf("TransactionManager")),
                getTransactionManagerReference());
        addDeepBinding(rootContext, new CompositeName(nameOf("UserTransaction")), getUserTransactionReference());
    }

    /**
     * Creates and installs in the container a new ConnectionManager.
     *
     * @param name the repository name
     * @param config the pool configuration
     * @return the created connection manager
     */
    public static synchronized ConnectionManagerWrapper installConnectionManager(
            NuxeoConnectionManagerConfiguration config) {
        String name = config.getName();
        ConnectionManagerWrapper cm = connectionManagers.get(name);
        if (cm != null) {
            return cm;
        }
        cm = initConnectionManager(config);
        // also bind it in JNDI
        if (rootContext != null) {
            String jndiName = nameOf("ConnectionManager/".concat(name));
            try {
                addDeepBinding(rootContext, new CompositeName(jndiName), getConnectionManagerReference(name));
            } catch (NamingException e) {
                log.error("Cannot bind in JNDI connection manager " + config.getName() + " to name " + jndiName);
            }
        }
        return cm;
    }

    public static boolean isInstalled() {
        return installContext != null;
    }

    protected static void uninstall() throws NamingException {
        if (installContext == null) {
            throw new RuntimeException("Nuxeo container not installed");
        }
        try {
            NamingException errors = new NamingException("Cannot shutdown connection managers");
            for (ConnectionManagerWrapper cm : connectionManagers.values()) {
                try {
                    cm.dispose();
                } catch (RuntimeException cause) {
                    errors.addSuppressed(cause);
                }
            }
            if (errors.getSuppressed().length > 0) {
                log.error("Cannot shutdown some pools", errors);
                throw errors;
            }
        } finally {
            log.trace("Uninstalling nuxeo container", installContext);
            installContext = null;
            rootContext = null;
            tm = null;
            tmRecoverable = null;
            tmSynchRegistry = null;
            ut = null;
            connectionManagers.clear();
        }
    }

    /**
     * @since 5.8
     */
    public static void addListener(NuxeoContainerListener listener) {
        synchronized (listeners) {
            listeners.add(listener);
        }
        for (Map.Entry<String, ConnectionManagerWrapper> entry : connectionManagers.entrySet()) {
            listener.handleNewConnectionManager(entry.getKey(), entry.getValue().cm);
        }
    }

    /**
     * @since 5.8
     */
    public static void removeListener(NuxeoContainerListener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }
    }

    protected static String detectJNDIPrefix(Context context) {
        String name = context.getClass().getName();
        if ("org.jnp.interfaces.NamingContext".equals(name)) { // JBoss
            return "java:";
        } else if ("org.jboss.as.naming.InitialContext".equals(name)) { // Wildfly
            return "java:jboss/";
        } else if ("org.mortbay.naming.local.localContextRoot".equals(name)) { // Jetty
            return "jdbc/";
        }
        // Standard JEE containers (Nuxeo-Embedded, Tomcat, GlassFish,
        // ...
        return "java:comp/env/";
    }

    public static String nameOf(String name) {
        return jndiPrefix.concat(name);
    }

    /**
     * Exposes the {@link #rootContext}.
     *
     * @since 5.7
     * @see https://jira.nuxeo.com/browse/NXP-10331
     */
    public static Context getRootContext() {
        return rootContext;
    }

    /**
     * Bind object in root context. Create needed sub contexts. since 5.6
     */
    public static void addDeepBinding(String name, Object obj) throws NamingException {
        addDeepBinding(rootContext, new CompositeName(name), obj);
    }

    protected static void addDeepBinding(Context dir, CompositeName comp, Object obj) throws NamingException {
        Name name = comp.getPrefix(1);
        if (comp.size() == 1) {
            addBinding(dir, name, obj);
            return;
        }
        Context subdir;
        try {
            subdir = (Context) dir.lookup(name);
        } catch (NamingException e) {
            subdir = dir.createSubcontext(name);
        }
        addDeepBinding(subdir, (CompositeName) comp.getSuffix(1), obj);
    }

    protected static void addBinding(Context dir, Name name, Object obj) throws NamingException {
        try {
            dir.rebind(name, obj);
        } catch (NamingException e) {
            dir.bind(name, obj);
        }
    }

    protected static void removeBinding(String name) throws NamingException {
        rootContext.unbind(name);
    }

    /**
     * Gets the transaction manager used by the container.
     *
     * @return the transaction manager
     */
    public static TransactionManager getTransactionManager() {
        return tm;
    }

    protected static Reference getTransactionManagerReference() {
        return new SimpleReference() {
            private static final long serialVersionUID = 1L;

            @Override
            public Object getContent() throws NamingException {
                return NuxeoContainer.getTransactionManager();
            }
        };
    }

    /**
     * Gets the user transaction used by the container.
     *
     * @return the user transaction
     */
    public static UserTransaction getUserTransaction() {
        return ut;
    }

    protected static Reference getUserTransactionReference() {
        return new SimpleReference() {
            private static final long serialVersionUID = 1L;

            @Override
            public Object getContent() throws NamingException {
                return getUserTransaction();
            }
        };
    }

    /**
     * Gets the Nuxeo connection manager used by the container.
     *
     * @return the connection manager
     */
    public static NuxeoConnectionManager getConnectionManager(String repositoryName) {
        ConnectionManagerWrapper wrapper = connectionManagers.get(repositoryName);
        if (wrapper == null) {
            return null;
        }
        return wrapper.cm;
    }

    public static void installConnectionManager(ConnectionManagerWrapper wrapper) {
        String name = wrapper.config.getName();
        if (connectionManagers.containsKey(name)) {
            log.error("Connection manager " + name + " already set up", new Exception());
        }
        connectionManagers.put(name, wrapper);
        for (NuxeoContainerListener listener : listeners) {
            listener.handleNewConnectionManager(name, wrapper.cm);
        }
    }

    protected static Reference getConnectionManagerReference(final String name) {
        return new SimpleReference() {
            private static final long serialVersionUID = 1L;

            @Override
            public Object getContent() throws NamingException {
                return getConnectionManager(name);
            }
        };
    }

    protected static synchronized TransactionManager initTransactionManager(
            TransactionManagerConfiguration config) {
        TransactionManagerImpl impl = createTransactionManager(config);
        tm = impl;
        tmRecoverable = impl;
        tmSynchRegistry = impl;
        ut = new UserTransactionImpl(tm);
        return tm;
    }

    protected static TransactionManagerWrapper wrapTransactionManager(TransactionManager tm) {
        if (tm == null) {
            return null;
        }
        if (tm instanceof TransactionManagerWrapper) {
            return (TransactionManagerWrapper) tm;
        }
        return new TransactionManagerWrapper(tm);
    }

    public static synchronized ConnectionManagerWrapper initConnectionManager(
            NuxeoConnectionManagerConfiguration config) {
        NuxeoConnectionTrackingCoordinator coordinator = new NuxeoConnectionTrackingCoordinator();
        NuxeoConnectionManager cm = createConnectionManager(coordinator, config);
        ConnectionManagerWrapper cmw = new ConnectionManagerWrapper(coordinator, cm, config);
        installConnectionManager(cmw);
        return cmw;
    }

    public static synchronized void disposeConnectionManager(ConnectionManager mgr) {
        ((ConnectionManagerWrapper) mgr).dispose();
    }

    // called by reflection from RepositoryReloader
    public static synchronized void resetConnectionManager() {
        RuntimeException errors = new RuntimeException("Cannot reset connection managers");
        for (ConnectionManagerWrapper wrapper : connectionManagers.values()) {
            try {
                wrapper.reset();
            } catch (RuntimeException cause) {
                errors.addSuppressed(cause);
            }
        }
        if (errors.getSuppressed().length > 0) {
            throw errors;
        }
    }

    public static synchronized void resetConnectionManager(String name) {
        connectionManagers.get(name).reset();
    }

    public static <T> T lookup(String name, Class<T> type) throws NamingException {
        if (rootContext == null) {
            throw new NamingException("no naming context available");
        }
        return lookup(rootContext, name, type);
    }

    public static <T> T lookup(Context context, String name, Class<T> type) throws NamingException {
        Object resolved;
        try {
            resolved = context.lookup(detectJNDIPrefix(context).concat(name));
        } catch (NamingException cause) {
            if (parentContext == null) {
                throw cause;
            }
            return type.cast(parentContext.lookup(detectJNDIPrefix(parentContext).concat(name)));
        }
        if (resolved instanceof Reference) {
            try {
                resolved = NamingManager.getObjectInstance(resolved, new CompositeName(name), rootContext, null);
            } catch (NamingException e) {
                throw e;
            } catch (Exception e) { // stupid JNDI API throws Exception
                throw ExceptionUtils.runtimeException(e);
            }
        }
        return type.cast(resolved);
    }

    protected static void installTransactionManager(Context context) throws NamingException {
        TransactionManager actual = lookup(context, "TransactionManager", TransactionManager.class);
        if (tm != null) {
            return;
        }
        tm = actual;
        tmRecoverable = wrapTransactionManager(tm);
        ut = new UserTransactionImpl(tm);
        tmSynchRegistry = lookup(context, "TransactionSynchronizationRegistry",
                TransactionSynchronizationRegistry.class);
    }

    protected static ConnectionManagerWrapper lookupConnectionManager(String repositoryName)
            throws NamingException {
        ConnectionManager cm = lookup(rootContext, "ConnectionManager/".concat(repositoryName),
                ConnectionManager.class);
        if (cm instanceof ConnectionManagerWrapper) {
            return (ConnectionManagerWrapper) cm;
        }
        log.warn("Connection manager not a wrapper, check your configuration");
        throw new RuntimeException(
                "Connection manager of " + repositoryName + " not a wrapper, check your configuration");
    }

    protected static TransactionManagerImpl createTransactionManager(TransactionManagerConfiguration config) {
        if (config == null) {
            config = new TransactionManagerConfiguration();
        }
        try {
            return new TransactionManagerImpl(config.transactionTimeoutSeconds);
        } catch (XAException e) {
            // failed in recovery somewhere
            throw new RuntimeException(e.toString(), e);
        }
    }

    /**
     * User transaction that uses this container's transaction manager.
     *
     * @since 5.6
     */
    public static class UserTransactionImpl implements UserTransaction {

        protected final TransactionManager transactionManager;

        public UserTransactionImpl(TransactionManager manager) {
            transactionManager = manager;
        }

        @Override
        public int getStatus() throws SystemException {
            return transactionManager.getStatus();
        }

        @Override
        public void setRollbackOnly() throws IllegalStateException, SystemException {
            transactionManager.setRollbackOnly();
        }

        @Override
        public void setTransactionTimeout(int seconds) throws SystemException {
            transactionManager.setTransactionTimeout(seconds);
        }

        @Override
        public void begin() throws NotSupportedException, SystemException {
            SequenceTracer.start("tx begin", "#DarkSalmon");
            transactionManager.begin();
            timers.put(transactionManager.getTransaction(), transactionTimer.time());
            concurrentCount.inc();
            if (concurrentCount.getCount() > concurrentMaxCount.getCount()) {
                concurrentMaxCount.inc();
            }
        }

        @Override
        public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
                RollbackException, SecurityException, SystemException {
            SequenceTracer.start("tx commiting", "#de6238");
            Timer.Context timerContext = timers.remove(transactionManager.getTransaction());
            transactionManager.commit();
            if (timerContext != null) {
                long elapsed = timerContext.stop();
                SequenceTracer.stop("tx commited");
                SequenceTracer.stop("tx end " + elapsed / 1000000 + " ms");
            }
            concurrentCount.dec();
        }

        @Override
        public void rollback() throws IllegalStateException, SecurityException, SystemException {
            SequenceTracer.mark("tx rollbacking");
            Timer.Context timerContext = timers.remove(transactionManager.getTransaction());
            transactionManager.rollback();
            concurrentCount.dec();
            if (timerContext != null) {
                long elapsed = timerContext.stop();
                SequenceTracer.destroy("tx rollbacked " + elapsed / 1000000 + " ms");
            }
            rollbackCount.inc();
        }
    }

    /**
     * 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.
     *
     * @throws NamingException
     */
    public static NuxeoConnectionManager createConnectionManager(NuxeoConnectionTrackingCoordinator coordinator,
            NuxeoConnectionManagerConfiguration config) {
        TransactionSupport transactionSupport = createTransactionSupport(config);
        PoolingSupport poolingSupport = createPoolingSupport(config);
        NuxeoValidationSupport validationSupport = createValidationSupport(config);
        return new NuxeoConnectionManager(config.getActiveTimeoutMinutes() * 60 * 1000, validationSupport,
                transactionSupport, poolingSupport, null, coordinator, tmRecoverable, config.getName(),
                Thread.currentThread().getContextClassLoader());
    }

    protected static PoolingSupport createPoolingSupport(NuxeoConnectionManagerConfiguration config) {
        return new NuxeoPool(config);
    }

    protected static TransactionSupport createTransactionSupport(NuxeoConnectionManagerConfiguration config) {
        if (config.getXAMode()) {
            // note: XATransactions -> TransactionCachingInterceptor ->
            // ConnectorTransactionContext casts transaction to Geronimo's
            // TransactionImpl (from TransactionManagerImpl)
            return new XATransactions(config.getUseTransactionCaching(), config.getUseThreadCaching());
        }
        return LocalTransactions.INSTANCE;
    }

    protected static NuxeoValidationSupport createValidationSupport(NuxeoConnectionManagerConfiguration config) {
        return new NuxeoValidationSupport(config.testOnBorrow, config.testOnReturn);
    }

    public static class TransactionManagerConfiguration {
        public int transactionTimeoutSeconds = 600;

        public void setTransactionTimeoutSeconds(int transactionTimeoutSeconds) {
            this.transactionTimeoutSeconds = transactionTimeoutSeconds;
        }
    }

    /**
     * Wraps a transaction manager for providing a dummy recoverable interface.
     *
     * @author matic
     */
    public static class TransactionManagerWrapper implements RecoverableTransactionManager {

        protected TransactionManager tm;

        public TransactionManagerWrapper(TransactionManager tm) {
            this.tm = tm;
        }

        @Override
        public Transaction suspend() throws SystemException {
            return tm.suspend();
        }

        @Override
        public void setTransactionTimeout(int seconds) throws SystemException {
            tm.setTransactionTimeout(seconds);
        }

        @Override
        public void setRollbackOnly() throws IllegalStateException, SystemException {
            tm.setRollbackOnly();
        }

        @Override
        public void rollback() throws IllegalStateException, SecurityException, SystemException {
            tm.rollback();
        }

        @Override
        public void resume(Transaction tobj)
                throws IllegalStateException, InvalidTransactionException, SystemException {
            tm.resume(tobj);
        }

        @Override
        public int getStatus() throws SystemException {
            return tm.getStatus();
        }

        @Override
        public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
                RollbackException, SecurityException, SystemException {
            tm.commit();
        }

        @Override
        public void begin() throws SystemException {
            try {
                tm.begin();
            } catch (javax.transaction.NotSupportedException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void recoveryError(Exception e) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void registerNamedXAResourceFactory(NamedXAResourceFactory factory) {
            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
                throw new UnsupportedOperationException();
            }
            ((RecoverableTransactionManager) tm).registerNamedXAResourceFactory(factory);
        }

        @Override
        public void unregisterNamedXAResourceFactory(String factory) {
            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
                throw new UnsupportedOperationException();
            }
            ((RecoverableTransactionManager) tm).unregisterNamedXAResourceFactory(factory);
        }

        @Override
        public Transaction getTransaction() throws SystemException {
            final Transaction tx = tm.getTransaction();
            if (tx instanceof TransactionImpl) {
                return tx;
            }
            return new TransactionImpl(null, null) {
                @Override
                public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException,
                        SecurityException, SystemException {
                    tx.commit();
                }

                @Override
                public void rollback() throws IllegalStateException, SystemException {
                    tx.rollback();
                }

                @Override
                public synchronized boolean enlistResource(XAResource xaRes)
                        throws IllegalStateException, RollbackException, SystemException {
                    return tx.enlistResource(xaRes);
                }

                @Override
                public synchronized boolean delistResource(XAResource xaRes, int flag)
                        throws IllegalStateException, SystemException {
                    return super.delistResource(xaRes, flag);
                }

                @Override
                public synchronized void setRollbackOnly() throws IllegalStateException {
                    try {
                        tx.setRollbackOnly();
                    } catch (SystemException e) {
                        throw new IllegalStateException(e);
                    }
                }

                @Override
                public void registerInterposedSynchronization(javax.transaction.Synchronization synchronization) {
                    try {
                        TransactionHelper.lookupSynchronizationRegistry()
                                .registerInterposedSynchronization(synchronization);
                    } catch (NamingException e) {
                        ;
                    }
                }
            };
        }
    }

    /**
     * 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 NuxeoConnectionTrackingCoordinator coordinator;

        protected volatile NuxeoConnectionManager cm;

        protected final NuxeoConnectionManagerConfiguration config;

        public ConnectionManagerWrapper(NuxeoConnectionTrackingCoordinator coordinator, NuxeoConnectionManager cm,
                NuxeoConnectionManagerConfiguration config) {
            this.coordinator = coordinator;
            this.cm = cm;
            this.config = config;
        }

        @Override
        public Object allocateConnection(ManagedConnectionFactory managedConnectionFactory,
                ConnectionRequestInfo connectionRequestInfo) throws ResourceException {
            return cm.allocateConnection(managedConnectionFactory, connectionRequestInfo);
        }

        public void reset() {
            AbstractConnectionManager last = cm;
            cm = createConnectionManager(coordinator, config);
            try {
                last.doStop();
            } catch (Exception e) { // stupid Geronimo API throws Exception
                throw ExceptionUtils.runtimeException(e);
            }
            for (NuxeoContainerListener listener : listeners) {
                listener.handleConnectionManagerReset(config.getName(), cm);
            }
        }

        public List<ActiveMonitor.TimeToLive> killActiveTimedoutConnections(long clock) {
            return cm.activemonitor.killTimedoutConnections(clock);
        }

        public void dispose() {
            for (NuxeoContainerListener listener : listeners) {
                listener.handleConnectionManagerDispose(config.getName(), cm);
            }
            cm.activemonitor.cancelCleanups();
            NuxeoContainer.connectionManagers.remove(config.getName());
            try {
                cm.doStop();
            } catch (Exception e) { // stupid Geronimo API throws Exception
                throw ExceptionUtils.runtimeException(e);
            }
        }

        public NuxeoConnectionManagerConfiguration getConfiguration() {
            return config;
        }

        public NuxeoConnectionManager getManager() {
            return cm;
        }

    }

    public static TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
        return tmSynchRegistry;
    }

}