001/*
002 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Florent Guillaume
018 *     Julien Carsique
019 */
020package org.nuxeo.runtime.jtajca;
021
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Map;
025import java.util.concurrent.ConcurrentHashMap;
026
027import javax.naming.CompositeName;
028import javax.naming.Context;
029import javax.naming.Name;
030import javax.naming.NamingException;
031import javax.naming.Reference;
032import javax.naming.spi.NamingManager;
033import javax.resource.ResourceException;
034import javax.resource.spi.ConnectionManager;
035import javax.resource.spi.ConnectionRequestInfo;
036import javax.resource.spi.ManagedConnectionFactory;
037import javax.transaction.HeuristicMixedException;
038import javax.transaction.HeuristicRollbackException;
039import javax.transaction.InvalidTransactionException;
040import javax.transaction.NotSupportedException;
041import javax.transaction.RollbackException;
042import javax.transaction.SystemException;
043import javax.transaction.Transaction;
044import javax.transaction.TransactionManager;
045import javax.transaction.TransactionSynchronizationRegistry;
046import javax.transaction.UserTransaction;
047import javax.transaction.xa.XAException;
048import javax.transaction.xa.XAResource;
049
050import org.apache.commons.logging.Log;
051import org.apache.commons.logging.LogFactory;
052import org.apache.geronimo.connector.outbound.AbstractConnectionManager;
053import org.apache.geronimo.connector.outbound.connectionmanagerconfig.LocalTransactions;
054import org.apache.geronimo.connector.outbound.connectionmanagerconfig.PoolingSupport;
055import org.apache.geronimo.connector.outbound.connectionmanagerconfig.TransactionSupport;
056import org.apache.geronimo.connector.outbound.connectionmanagerconfig.XATransactions;
057import org.apache.geronimo.transaction.manager.NamedXAResourceFactory;
058import org.apache.geronimo.transaction.manager.RecoverableTransactionManager;
059import org.apache.geronimo.transaction.manager.TransactionImpl;
060import org.apache.geronimo.transaction.manager.TransactionManagerImpl;
061import org.apache.xbean.naming.reference.SimpleReference;
062import org.nuxeo.common.logging.SequenceTracer;
063import org.nuxeo.common.utils.ExceptionUtils;
064import org.nuxeo.runtime.jtajca.NuxeoConnectionManager.ActiveMonitor;
065import org.nuxeo.runtime.metrics.MetricsService;
066import org.nuxeo.runtime.transaction.TransactionHelper;
067
068import com.codahale.metrics.Counter;
069import com.codahale.metrics.MetricRegistry;
070import com.codahale.metrics.SharedMetricRegistries;
071import com.codahale.metrics.Timer;
072
073/**
074 * Internal helper for the Nuxeo-defined transaction manager and connection manager.
075 * <p>
076 * This code is called by the factories registered through JNDI, or by unit tests mimicking JNDI bindings.
077 */
078public class NuxeoContainer {
079
080    protected static final Log log = LogFactory.getLog(NuxeoContainer.class);
081
082    protected static RecoverableTransactionManager tmRecoverable;
083
084    protected static TransactionManager tm;
085
086    protected static TransactionSynchronizationRegistry tmSynchRegistry;
087
088    protected static UserTransaction ut;
089
090    protected static Map<String, ConnectionManagerWrapper> connectionManagers = new ConcurrentHashMap<>(
091            8, 0.75f, 2);
092
093    private static final List<NuxeoContainerListener> listeners = new ArrayList<>();
094
095    private static volatile InstallContext installContext;
096
097    protected static Context rootContext;
098
099    protected static Context parentContext;
100
101    protected static String jndiPrefix = "java:comp/env/";
102
103    // @since 5.7
104    protected static final MetricRegistry registry = SharedMetricRegistries.getOrCreate(MetricsService.class.getName());
105
106    protected static final Counter rollbackCount = registry.counter(MetricRegistry.name("nuxeo", "transactions",
107            "rollbacks"));
108
109    protected static final Counter concurrentCount = registry.counter(MetricRegistry.name("nuxeo", "transactions",
110            "concurrents", "count"));
111
112    protected static final Counter concurrentMaxCount = registry.counter(MetricRegistry.name("nuxeo", "transactions",
113            "concurrents", "max"));
114
115    protected static final Timer transactionTimer = registry.timer(MetricRegistry.name("nuxeo", "transactions",
116            "duration"));
117
118    protected static final ConcurrentHashMap<Transaction, Timer.Context> timers = new ConcurrentHashMap<>();
119
120    private NuxeoContainer() {
121    }
122
123    public static class InstallContext extends Throwable {
124        private static final long serialVersionUID = 1L;
125
126        public final String threadName;
127
128        InstallContext() {
129            super("Container installation context (" + Thread.currentThread().getName() + ")");
130            threadName = Thread.currentThread().getName();
131        }
132    }
133
134    /**
135     * Install naming and bind transaction and connection management factories "by hand".
136     */
137    protected static void install() throws NamingException {
138        if (installContext != null) {
139            throw new RuntimeException("Nuxeo container already installed");
140        }
141        installContext = new InstallContext();
142        log.trace("Installing nuxeo container", installContext);
143        rootContext = new NamingContext();
144        parentContext = InitialContextAccessor.getInitialContext();
145        if (parentContext != null && parentContext != rootContext) {
146            installTransactionManager(parentContext);
147        } else {
148            addDeepBinding(nameOf("TransactionManager"), new Reference(TransactionManager.class.getName(),
149                    NuxeoTransactionManagerFactory.class.getName(), null));
150            installTransactionManager(rootContext);
151        }
152    }
153
154    protected static void installTransactionManager(TransactionManagerConfiguration config) throws NamingException {
155        initTransactionManager(config);
156        addDeepBinding(rootContext, new CompositeName(nameOf("TransactionManager")), getTransactionManagerReference());
157        addDeepBinding(rootContext, new CompositeName(nameOf("UserTransaction")), getUserTransactionReference());
158    }
159
160    /**
161     * Creates and installs in the container a new ConnectionManager.
162     *
163     * @param name the repository name
164     * @param config the pool configuration
165     * @return the created connection manager
166     */
167    public static synchronized ConnectionManagerWrapper installConnectionManager(
168            NuxeoConnectionManagerConfiguration config) {
169        String name = config.getName();
170        ConnectionManagerWrapper cm = connectionManagers.get(name);
171        if (cm != null) {
172            return cm;
173        }
174        cm = initConnectionManager(config);
175        // also bind it in JNDI
176        if (rootContext != null) {
177            String jndiName = nameOf("ConnectionManager/".concat(name));
178            try {
179                addDeepBinding(rootContext, new CompositeName(jndiName), getConnectionManagerReference(name));
180            } catch (NamingException e) {
181                log.error("Cannot bind in JNDI connection manager " + config.getName() + " to name " + jndiName);
182            }
183        }
184        return cm;
185    }
186
187    public static boolean isInstalled() {
188        return installContext != null;
189    }
190
191    protected static void uninstall() throws NamingException {
192        if (installContext == null) {
193            throw new RuntimeException("Nuxeo container not installed");
194        }
195        try {
196            NamingException errors = new NamingException("Cannot shutdown connection managers");
197            for (ConnectionManagerWrapper cm : connectionManagers.values()) {
198                try {
199                    cm.dispose();
200                } catch (RuntimeException cause) {
201                    errors.addSuppressed(cause);
202                }
203            }
204            if (errors.getSuppressed().length > 0) {
205                log.error("Cannot shutdown some pools", errors);
206                throw errors;
207            }
208        } finally {
209            log.trace("Uninstalling nuxeo container", installContext);
210            installContext = null;
211            rootContext = null;
212            tm = null;
213            tmRecoverable = null;
214            tmSynchRegistry = null;
215            ut = null;
216            connectionManagers.clear();
217        }
218    }
219
220    /**
221     * @since 5.8
222     */
223    public static void addListener(NuxeoContainerListener listener) {
224        synchronized (listeners) {
225            listeners.add(listener);
226        }
227        for (Map.Entry<String, ConnectionManagerWrapper> entry : connectionManagers.entrySet()) {
228            listener.handleNewConnectionManager(entry.getKey(), entry.getValue().cm);
229        }
230    }
231
232    /**
233     * @since 5.8
234     */
235    public static void removeListener(NuxeoContainerListener listener) {
236        synchronized (listeners) {
237            listeners.remove(listener);
238        }
239    }
240
241    protected static String detectJNDIPrefix(Context context) {
242        String name = context.getClass().getName();
243        if ("org.jnp.interfaces.NamingContext".equals(name)) { // JBoss
244            return "java:";
245        } else if ("org.jboss.as.naming.InitialContext".equals(name)) { // Wildfly
246            return "java:jboss/";
247        } else if ("org.mortbay.naming.local.localContextRoot".equals(name)) { // Jetty
248            return "jdbc/";
249        }
250        // Standard JEE containers (Nuxeo-Embedded, Tomcat, GlassFish,
251        // ...
252        return "java:comp/env/";
253    }
254
255    public static String nameOf(String name) {
256        return jndiPrefix.concat(name);
257    }
258
259    /**
260     * Exposes the {@link #rootContext}.
261     *
262     * @since 5.7
263     * @see https://jira.nuxeo.com/browse/NXP-10331
264     */
265    public static Context getRootContext() {
266        return rootContext;
267    }
268
269    /**
270     * Bind object in root context. Create needed sub contexts. since 5.6
271     */
272    public static void addDeepBinding(String name, Object obj) throws NamingException {
273        addDeepBinding(rootContext, new CompositeName(name), obj);
274    }
275
276    protected static void addDeepBinding(Context dir, CompositeName comp, Object obj) throws NamingException {
277        Name name = comp.getPrefix(1);
278        if (comp.size() == 1) {
279            addBinding(dir, name, obj);
280            return;
281        }
282        Context subdir;
283        try {
284            subdir = (Context) dir.lookup(name);
285        } catch (NamingException e) {
286            subdir = dir.createSubcontext(name);
287        }
288        addDeepBinding(subdir, (CompositeName) comp.getSuffix(1), obj);
289    }
290
291    protected static void addBinding(Context dir, Name name, Object obj) throws NamingException {
292        try {
293            dir.rebind(name, obj);
294        } catch (NamingException e) {
295            dir.bind(name, obj);
296        }
297    }
298
299    protected static void removeBinding(String name) throws NamingException {
300        rootContext.unbind(name);
301    }
302
303    /**
304     * Gets the transaction manager used by the container.
305     *
306     * @return the transaction manager
307     */
308    public static TransactionManager getTransactionManager() {
309        return tm;
310    }
311
312    protected static Reference getTransactionManagerReference() {
313        return new SimpleReference() {
314            private static final long serialVersionUID = 1L;
315
316            @Override
317            public Object getContent() throws NamingException {
318                return NuxeoContainer.getTransactionManager();
319            }
320        };
321    }
322
323    /**
324     * Gets the user transaction used by the container.
325     *
326     * @return the user transaction
327     */
328    public static UserTransaction getUserTransaction() {
329        return ut;
330    }
331
332    protected static Reference getUserTransactionReference() {
333        return new SimpleReference() {
334            private static final long serialVersionUID = 1L;
335
336            @Override
337            public Object getContent() throws NamingException {
338                return getUserTransaction();
339            }
340        };
341    }
342
343    /**
344     * Gets the Nuxeo connection manager used by the container.
345     *
346     * @return the connection manager
347     */
348    public static NuxeoConnectionManager getConnectionManager(String repositoryName) {
349        ConnectionManagerWrapper wrapper =  connectionManagers.get(repositoryName);
350        if (wrapper == null) {
351            return null;
352        }
353        return wrapper.cm;
354    }
355
356    public static void installConnectionManager(ConnectionManagerWrapper wrapper) {
357        String name = wrapper.config.getName();
358        if (connectionManagers.containsKey(name)) {
359            log.error("Connection manager " + name + " already set up", new Exception());
360        }
361        connectionManagers.put(name, wrapper);
362        for (NuxeoContainerListener listener : listeners) {
363            listener.handleNewConnectionManager(name, wrapper.cm);
364        }
365    }
366
367    protected static Reference getConnectionManagerReference(final String name) {
368        return new SimpleReference() {
369            private static final long serialVersionUID = 1L;
370
371            @Override
372            public Object getContent() throws NamingException {
373                return getConnectionManager(name);
374            }
375        };
376    }
377
378    protected static synchronized TransactionManager initTransactionManager(TransactionManagerConfiguration config) {
379        TransactionManagerImpl impl = createTransactionManager(config);
380        tm = impl;
381        tmRecoverable = impl;
382        tmSynchRegistry = impl;
383        ut = new UserTransactionImpl(tm);
384        return tm;
385    }
386
387    protected static TransactionManagerWrapper wrapTransactionManager(TransactionManager tm) {
388        if (tm == null) {
389            return null;
390        }
391        if (tm instanceof TransactionManagerWrapper) {
392            return (TransactionManagerWrapper) tm;
393        }
394        return new TransactionManagerWrapper(tm);
395    }
396
397    public static synchronized ConnectionManagerWrapper initConnectionManager(NuxeoConnectionManagerConfiguration config) {
398        NuxeoConnectionTrackingCoordinator coordinator = new NuxeoConnectionTrackingCoordinator();
399        NuxeoConnectionManager cm = createConnectionManager(coordinator, config);
400        ConnectionManagerWrapper cmw = new ConnectionManagerWrapper(coordinator, cm, config);
401        installConnectionManager(cmw);
402        return cmw;
403    }
404
405    public static synchronized void disposeConnectionManager(ConnectionManager mgr) {
406        ((ConnectionManagerWrapper) mgr).dispose();
407    }
408
409    // called by reflection from RepositoryReloader
410    public static synchronized void resetConnectionManager() {
411        RuntimeException errors = new RuntimeException("Cannot reset connection managers");
412        for (ConnectionManagerWrapper wrapper : connectionManagers.values()) {
413            try {
414                wrapper.reset();
415            } catch (RuntimeException cause) {
416                errors.addSuppressed(cause);
417            }
418        }
419        if (errors.getSuppressed().length > 0) {
420            throw errors;
421        }
422    }
423
424    public static synchronized void resetConnectionManager(String name) {
425        connectionManagers.get(name).reset();
426    }
427
428    public static <T> T lookup(String name, Class<T> type) throws NamingException {
429        if (rootContext == null) {
430            throw new NamingException("no naming context available");
431        }
432        return lookup(rootContext, name, type);
433    }
434
435    public static <T> T lookup(Context context, String name, Class<T> type) throws NamingException {
436        Object resolved;
437        try {
438            resolved = context.lookup(detectJNDIPrefix(context).concat(name));
439        } catch (NamingException cause) {
440            if (parentContext == null) {
441                throw cause;
442            }
443            return type.cast(parentContext.lookup(detectJNDIPrefix(parentContext).concat(name)));
444        }
445        if (resolved instanceof Reference) {
446            try {
447                resolved = NamingManager.getObjectInstance(resolved, new CompositeName(name), rootContext, null);
448            } catch (NamingException e) {
449                throw e;
450            } catch (Exception e) { // stupid JNDI API throws Exception
451                throw ExceptionUtils.runtimeException(e);
452            }
453        }
454        return type.cast(resolved);
455    }
456
457    protected static void installTransactionManager(Context context) throws NamingException {
458        TransactionManager actual = lookup(context, "TransactionManager", TransactionManager.class);
459        if (tm != null) {
460            return;
461        }
462        tm = actual;
463        tmRecoverable = wrapTransactionManager(tm);
464        ut = new UserTransactionImpl(tm);
465        tmSynchRegistry = lookup(context, "TransactionSynchronizationRegistry",
466                TransactionSynchronizationRegistry.class);
467    }
468
469    protected static ConnectionManagerWrapper lookupConnectionManager(String repositoryName) throws NamingException {
470        ConnectionManager cm = lookup(rootContext, "ConnectionManager/".concat(repositoryName), ConnectionManager.class);
471        if (cm instanceof ConnectionManagerWrapper) {
472            return (ConnectionManagerWrapper) cm;
473        }
474        log.warn("Connection manager not a wrapper, check your configuration");
475        throw new RuntimeException("Connection manager of " + repositoryName
476                + " not a wrapper, check your configuration");
477    }
478
479    protected static TransactionManagerImpl createTransactionManager(TransactionManagerConfiguration config) {
480        if (config == null) {
481            config = new TransactionManagerConfiguration();
482        }
483        try {
484            return new TransactionManagerImpl(config.transactionTimeoutSeconds);
485        } catch (XAException e) {
486            // failed in recovery somewhere
487            throw new RuntimeException(e.toString(), e);
488        }
489    }
490
491    /**
492     * User transaction that uses this container's transaction manager.
493     *
494     * @since 5.6
495     */
496    public static class UserTransactionImpl implements UserTransaction {
497
498        protected final TransactionManager transactionManager;
499
500        public UserTransactionImpl(TransactionManager manager) {
501            transactionManager = manager;
502        }
503
504        @Override
505        public int getStatus() throws SystemException {
506            return transactionManager.getStatus();
507        }
508
509        @Override
510        public void setRollbackOnly() throws IllegalStateException, SystemException {
511            transactionManager.setRollbackOnly();
512        }
513
514        @Override
515        public void setTransactionTimeout(int seconds) throws SystemException {
516            transactionManager.setTransactionTimeout(seconds);
517        }
518
519        @Override
520        public void begin() throws NotSupportedException, SystemException {
521            SequenceTracer.start("tx begin", "#DarkSalmon");
522            transactionManager.begin();
523            timers.put(transactionManager.getTransaction(), transactionTimer.time());
524            concurrentCount.inc();
525            if (concurrentCount.getCount() > concurrentMaxCount.getCount()) {
526                concurrentMaxCount.inc();
527            }
528        }
529
530        @Override
531        public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
532                RollbackException, SecurityException, SystemException {
533            SequenceTracer.start("tx commiting", "#de6238");
534            Timer.Context timerContext = timers.remove(transactionManager.getTransaction());
535            transactionManager.commit();
536            if (timerContext != null) {
537                long elapsed = timerContext.stop();
538                SequenceTracer.stop("tx commited");
539                SequenceTracer.stop("tx end "+ elapsed / 1000000 + " ms");
540            }
541            concurrentCount.dec();
542        }
543
544        @Override
545        public void rollback() throws IllegalStateException, SecurityException, SystemException {
546            SequenceTracer.mark("tx rollbacking");
547            Timer.Context timerContext = timers.remove(transactionManager.getTransaction());
548            transactionManager.rollback();
549            concurrentCount.dec();
550            if (timerContext != null) {
551                long elapsed = timerContext.stop();
552                SequenceTracer.destroy("tx rollbacked " + elapsed / 1000000 + " ms");
553            }
554            rollbackCount.inc();
555        }
556    }
557
558    /**
559     * Creates a Geronimo pooled connection manager using a Geronimo transaction manager.
560     * <p>
561     * The pool uses the transaction manager for recovery, and when using XATransactions for cache + enlist/delist.
562     *
563     * @throws NamingException
564     */
565    public static NuxeoConnectionManager createConnectionManager(NuxeoConnectionTrackingCoordinator coordinator,
566            NuxeoConnectionManagerConfiguration config) {
567        TransactionSupport transactionSupport = createTransactionSupport(config);
568        PoolingSupport poolingSupport = createPoolingSupport(config);
569        NuxeoValidationSupport validationSupport = createValidationSupport(config);
570        return new NuxeoConnectionManager(config.getActiveTimeoutMinutes()*60*1000, validationSupport, transactionSupport, poolingSupport, null, coordinator, tmRecoverable,
571                config.getName(), Thread.currentThread().getContextClassLoader());
572    }
573
574    protected static PoolingSupport createPoolingSupport(NuxeoConnectionManagerConfiguration config) {
575        return new NuxeoPool(config);
576    }
577
578    protected static TransactionSupport createTransactionSupport(NuxeoConnectionManagerConfiguration config) {
579        if (config.getXAMode()) {
580            // note: XATransactions -> TransactionCachingInterceptor ->
581            // ConnectorTransactionContext casts transaction to Geronimo's
582            // TransactionImpl (from TransactionManagerImpl)
583            return new XATransactions(config.getUseTransactionCaching(), config.getUseThreadCaching());
584        }
585        return LocalTransactions.INSTANCE;
586    }
587
588    protected static NuxeoValidationSupport createValidationSupport(NuxeoConnectionManagerConfiguration config) {
589        return new NuxeoValidationSupport(config.testOnBorrow, config.testOnReturn);
590    }
591
592    public static class TransactionManagerConfiguration {
593        public int transactionTimeoutSeconds = 600;
594
595        public void setTransactionTimeoutSeconds(int transactionTimeoutSeconds) {
596            this.transactionTimeoutSeconds = transactionTimeoutSeconds;
597        }
598    }
599
600    /**
601     * Wraps a transaction manager for providing a dummy recoverable interface.
602     *
603     * @author matic
604     */
605    public static class TransactionManagerWrapper implements RecoverableTransactionManager {
606
607        protected TransactionManager tm;
608
609        public TransactionManagerWrapper(TransactionManager tm) {
610            this.tm = tm;
611        }
612
613        @Override
614        public Transaction suspend() throws SystemException {
615            return tm.suspend();
616        }
617
618        @Override
619        public void setTransactionTimeout(int seconds) throws SystemException {
620            tm.setTransactionTimeout(seconds);
621        }
622
623        @Override
624        public void setRollbackOnly() throws IllegalStateException, SystemException {
625            tm.setRollbackOnly();
626        }
627
628        @Override
629        public void rollback() throws IllegalStateException, SecurityException, SystemException {
630            tm.rollback();
631        }
632
633        @Override
634        public void resume(Transaction tobj) throws IllegalStateException, InvalidTransactionException, SystemException {
635            tm.resume(tobj);
636        }
637
638        @Override
639        public int getStatus() throws SystemException {
640            return tm.getStatus();
641        }
642
643        @Override
644        public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
645                RollbackException, SecurityException, SystemException {
646            tm.commit();
647        }
648
649        @Override
650        public void begin() throws SystemException {
651            try {
652                tm.begin();
653            } catch (javax.transaction.NotSupportedException e) {
654                throw new RuntimeException(e);
655            }
656        }
657
658        @Override
659        public void recoveryError(Exception e) {
660            throw new UnsupportedOperationException();
661        }
662
663        @Override
664        public void registerNamedXAResourceFactory(NamedXAResourceFactory factory) {
665            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
666                throw new UnsupportedOperationException();
667            }
668            ((RecoverableTransactionManager) tm).registerNamedXAResourceFactory(factory);
669        }
670
671        @Override
672        public void unregisterNamedXAResourceFactory(String factory) {
673            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
674                throw new UnsupportedOperationException();
675            }
676            ((RecoverableTransactionManager) tm).unregisterNamedXAResourceFactory(factory);
677        }
678
679        @Override
680        public Transaction getTransaction() throws SystemException {
681            final Transaction tx = tm.getTransaction();
682            if (tx instanceof TransactionImpl) {
683                return tx;
684            }
685            return new TransactionImpl(null, null) {
686                @Override
687                public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException,
688                        SecurityException, SystemException {
689                    tx.commit();
690                }
691
692                @Override
693                public void rollback() throws IllegalStateException, SystemException {
694                    tx.rollback();
695                }
696
697                @Override
698                public synchronized boolean enlistResource(XAResource xaRes) throws IllegalStateException,
699                        RollbackException, SystemException {
700                    return tx.enlistResource(xaRes);
701                }
702
703                @Override
704                public synchronized boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException,
705                        SystemException {
706                    return super.delistResource(xaRes, flag);
707                }
708
709                @Override
710                public synchronized void setRollbackOnly() throws IllegalStateException {
711                    try {
712                        tx.setRollbackOnly();
713                    } catch (SystemException e) {
714                        throw new IllegalStateException(e);
715                    }
716                }
717
718                @Override
719                public void registerInterposedSynchronization(javax.transaction.Synchronization synchronization) {
720                    try {
721                        TransactionHelper.lookupSynchronizationRegistry().registerInterposedSynchronization(
722                                synchronization);
723                    } catch (NamingException e) {;
724                    }
725                }
726            };
727        }
728    }
729
730    /**
731     * Wraps a Geronimo ConnectionManager and adds a {@link #reset} method to flush the pool.
732     */
733    public static class ConnectionManagerWrapper implements ConnectionManager {
734
735        private static final long serialVersionUID = 1L;
736
737        protected NuxeoConnectionTrackingCoordinator coordinator;
738
739        protected volatile NuxeoConnectionManager cm;
740
741        protected final NuxeoConnectionManagerConfiguration config;
742
743        public ConnectionManagerWrapper(NuxeoConnectionTrackingCoordinator coordinator, NuxeoConnectionManager cm,
744                NuxeoConnectionManagerConfiguration config) {
745            this.coordinator = coordinator;
746            this.cm = cm;
747            this.config = config;
748        }
749
750        @Override
751        public Object allocateConnection(ManagedConnectionFactory managedConnectionFactory,
752                ConnectionRequestInfo connectionRequestInfo) throws ResourceException {
753            return cm.allocateConnection(managedConnectionFactory, connectionRequestInfo);
754        }
755
756        public void reset() {
757            AbstractConnectionManager last = cm;
758            cm = createConnectionManager(coordinator, config);
759            try {
760                last.doStop();
761            } catch (Exception e) { // stupid Geronimo API throws Exception
762                throw ExceptionUtils.runtimeException(e);
763            }
764            for (NuxeoContainerListener listener : listeners) {
765                listener.handleConnectionManagerReset(config.getName(), cm);
766            }
767        }
768
769        public List<ActiveMonitor.TimeToLive> killActiveTimedoutConnections(long clock) {
770            return cm.activemonitor.killTimedoutConnections(clock);
771        }
772
773        public void dispose() {
774            for (NuxeoContainerListener listener : listeners) {
775                listener.handleConnectionManagerDispose(config.getName(), cm);
776            }
777            cm.activemonitor.cancelCleanups();
778            NuxeoContainer.connectionManagers.remove(config.getName());
779            try {
780                cm.doStop();
781            } catch (Exception e) { // stupid Geronimo API throws Exception
782                throw ExceptionUtils.runtimeException(e);
783            }
784        }
785
786        public NuxeoConnectionManagerConfiguration getConfiguration() {
787            return config;
788        }
789
790        public NuxeoConnectionManager getManager() {
791            return cm;
792        }
793
794    }
795
796    public static TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
797        return tmSynchRegistry;
798    }
799
800}