001/*
002 * (C) Copyright 2006-2017 Nuxeo (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<>(8, 0.75f, 2);
091
092    private static final List<NuxeoContainerListener> listeners = new ArrayList<>();
093
094    private static volatile InstallContext installContext;
095
096    protected static Context rootContext;
097
098    protected static Context parentContext;
099
100    protected static String jndiPrefix = "java:comp/env/";
101
102    // @since 5.7
103    protected static final MetricRegistry registry = SharedMetricRegistries.getOrCreate(MetricsService.class.getName());
104
105    protected static final Counter rollbackCount = registry.counter(
106            MetricRegistry.name("nuxeo", "transactions", "rollbacks"));
107
108    protected static final Counter concurrentCount = registry.counter(
109            MetricRegistry.name("nuxeo", "transactions", "concurrents", "count"));
110
111    protected static final Counter concurrentMaxCount = registry.counter(
112            MetricRegistry.name("nuxeo", "transactions", "concurrents", "max"));
113
114    protected static final Timer transactionTimer = registry.timer(
115            MetricRegistry.name("nuxeo", "transactions", "duration"));
116
117    protected static final ConcurrentHashMap<Transaction, Timer.Context> timers = new ConcurrentHashMap<>();
118
119    private NuxeoContainer() {
120    }
121
122    public static class InstallContext extends Throwable {
123        private static final long serialVersionUID = 1L;
124
125        public final String threadName;
126
127        InstallContext() {
128            super("Container installation context (" + Thread.currentThread().getName() + ")");
129            threadName = Thread.currentThread().getName();
130        }
131    }
132
133    /**
134     * Install naming and bind transaction and connection management factories "by hand".
135     */
136    protected static void install() throws NamingException {
137        if (installContext != null) {
138            throw new RuntimeException("Nuxeo container already installed");
139        }
140        installContext = new InstallContext();
141        log.trace("Installing nuxeo container", installContext);
142        rootContext = new NamingContext();
143        parentContext = InitialContextAccessor.getInitialContext();
144        if (parentContext != null && parentContext != rootContext) {
145            installTransactionManager(parentContext);
146        } else {
147            addDeepBinding(nameOf("TransactionManager"), new Reference(TransactionManager.class.getName(),
148                    NuxeoTransactionManagerFactory.class.getName(), null));
149            installTransactionManager(rootContext);
150        }
151    }
152
153    protected static void installTransactionManager(TransactionManagerConfiguration config) throws NamingException {
154        initTransactionManager(config);
155        addDeepBinding(rootContext, new CompositeName(nameOf("TransactionManager")), getTransactionManagerReference());
156        addDeepBinding(rootContext, new CompositeName(nameOf("UserTransaction")), getUserTransactionReference());
157    }
158
159    /**
160     * Creates and installs in the container a new ConnectionManager.
161     *
162     * @param name the repository name
163     * @param config the pool configuration
164     * @return the created connection manager
165     */
166    public static synchronized ConnectionManagerWrapper installConnectionManager(
167            NuxeoConnectionManagerConfiguration config) {
168        String name = config.getName();
169        ConnectionManagerWrapper cm = connectionManagers.get(name);
170        if (cm != null) {
171            return cm;
172        }
173        cm = initConnectionManager(config);
174        // also bind it in JNDI
175        if (rootContext != null) {
176            String jndiName = nameOf("ConnectionManager/".concat(name));
177            try {
178                addDeepBinding(rootContext, new CompositeName(jndiName), getConnectionManagerReference(name));
179            } catch (NamingException e) {
180                log.error("Cannot bind in JNDI connection manager " + config.getName() + " to name " + jndiName);
181            }
182        }
183        return cm;
184    }
185
186    public static boolean isInstalled() {
187        return installContext != null;
188    }
189
190    protected static void uninstall() throws NamingException {
191        if (installContext == null) {
192            throw new RuntimeException("Nuxeo container not installed");
193        }
194        try {
195            NamingException errors = new NamingException("Cannot shutdown connection managers");
196            for (ConnectionManagerWrapper cm : connectionManagers.values()) {
197                try {
198                    cm.dispose();
199                } catch (RuntimeException cause) {
200                    errors.addSuppressed(cause);
201                }
202            }
203            if (errors.getSuppressed().length > 0) {
204                log.error("Cannot shutdown some pools", errors);
205                throw errors;
206            }
207        } finally {
208            log.trace("Uninstalling nuxeo container", installContext);
209            installContext = null;
210            rootContext = null;
211            tm = null;
212            tmRecoverable = null;
213            tmSynchRegistry = null;
214            ut = null;
215            connectionManagers.clear();
216        }
217    }
218
219    /**
220     * @since 5.8
221     */
222    public static void addListener(NuxeoContainerListener listener) {
223        synchronized (listeners) {
224            listeners.add(listener);
225        }
226        for (Map.Entry<String, ConnectionManagerWrapper> entry : connectionManagers.entrySet()) {
227            listener.handleNewConnectionManager(entry.getKey(), entry.getValue().cm);
228        }
229    }
230
231    /**
232     * @since 5.8
233     */
234    public static void removeListener(NuxeoContainerListener listener) {
235        synchronized (listeners) {
236            listeners.remove(listener);
237        }
238    }
239
240    protected static String detectJNDIPrefix(Context context) {
241        String name = context.getClass().getName();
242        if ("org.jnp.interfaces.NamingContext".equals(name)) { // JBoss
243            return "java:";
244        } else if ("org.jboss.as.naming.InitialContext".equals(name)) { // Wildfly
245            return "java:jboss/";
246        } else if ("org.mortbay.naming.local.localContextRoot".equals(name)) { // Jetty
247            return "jdbc/";
248        }
249        // Standard JEE containers (Nuxeo-Embedded, Tomcat, GlassFish,
250        // ...
251        return "java:comp/env/";
252    }
253
254    public static String nameOf(String name) {
255        return jndiPrefix.concat(name);
256    }
257
258    /**
259     * Exposes the {@link #rootContext}.
260     *
261     * @since 5.7
262     * @see https://jira.nuxeo.com/browse/NXP-10331
263     */
264    public static Context getRootContext() {
265        return rootContext;
266    }
267
268    /**
269     * Bind object in root context. Create needed sub contexts. since 5.6
270     */
271    public static void addDeepBinding(String name, Object obj) throws NamingException {
272        addDeepBinding(rootContext, new CompositeName(name), obj);
273    }
274
275    protected static void addDeepBinding(Context dir, CompositeName comp, Object obj) throws NamingException {
276        Name name = comp.getPrefix(1);
277        if (comp.size() == 1) {
278            addBinding(dir, name, obj);
279            return;
280        }
281        Context subdir;
282        try {
283            subdir = (Context) dir.lookup(name);
284        } catch (NamingException e) {
285            subdir = dir.createSubcontext(name);
286        }
287        addDeepBinding(subdir, (CompositeName) comp.getSuffix(1), obj);
288    }
289
290    protected static void addBinding(Context dir, Name name, Object obj) throws NamingException {
291        try {
292            dir.rebind(name, obj);
293        } catch (NamingException e) {
294            dir.bind(name, obj);
295        }
296    }
297
298    protected static void removeBinding(String name) throws NamingException {
299        rootContext.unbind(name);
300    }
301
302    /**
303     * Gets the transaction manager used by the container.
304     *
305     * @return the transaction manager
306     */
307    public static TransactionManager getTransactionManager() {
308        return tm;
309    }
310
311    protected static Reference getTransactionManagerReference() {
312        return new SimpleReference() {
313            private static final long serialVersionUID = 1L;
314
315            @Override
316            public Object getContent() throws NamingException {
317                return NuxeoContainer.getTransactionManager();
318            }
319        };
320    }
321
322    /**
323     * Gets the user transaction used by the container.
324     *
325     * @return the user transaction
326     */
327    public static UserTransaction getUserTransaction() {
328        return ut;
329    }
330
331    protected static Reference getUserTransactionReference() {
332        return new SimpleReference() {
333            private static final long serialVersionUID = 1L;
334
335            @Override
336            public Object getContent() throws NamingException {
337                return getUserTransaction();
338            }
339        };
340    }
341
342    /**
343     * Gets the Nuxeo connection manager used by the container.
344     *
345     * @return the connection manager
346     */
347    public static NuxeoConnectionManager getConnectionManager(String repositoryName) {
348        ConnectionManagerWrapper wrapper = connectionManagers.get(repositoryName);
349        if (wrapper == null) {
350            return null;
351        }
352        return wrapper.cm;
353    }
354
355    public static void installConnectionManager(ConnectionManagerWrapper wrapper) {
356        String name = wrapper.config.getName();
357        if (connectionManagers.containsKey(name)) {
358            log.error("Connection manager " + name + " already set up", new Exception());
359        }
360        connectionManagers.put(name, wrapper);
361        for (NuxeoContainerListener listener : listeners) {
362            listener.handleNewConnectionManager(name, wrapper.cm);
363        }
364    }
365
366    protected static Reference getConnectionManagerReference(final String name) {
367        return new SimpleReference() {
368            private static final long serialVersionUID = 1L;
369
370            @Override
371            public Object getContent() throws NamingException {
372                return getConnectionManager(name);
373            }
374        };
375    }
376
377    protected static synchronized TransactionManager initTransactionManager(TransactionManagerConfiguration config) {
378        TransactionManagerImpl impl = createTransactionManager(config);
379        tm = impl;
380        tmRecoverable = impl;
381        tmSynchRegistry = impl;
382        ut = new UserTransactionImpl(tm);
383        return tm;
384    }
385
386    protected static TransactionManagerWrapper wrapTransactionManager(TransactionManager tm) {
387        if (tm == null) {
388            return null;
389        }
390        if (tm instanceof TransactionManagerWrapper) {
391            return (TransactionManagerWrapper) tm;
392        }
393        return new TransactionManagerWrapper(tm);
394    }
395
396    public static synchronized ConnectionManagerWrapper initConnectionManager(
397            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 = (TransactionSynchronizationRegistry) tm;
466    }
467
468    protected static ConnectionManagerWrapper lookupConnectionManager(String repositoryName) throws NamingException {
469        ConnectionManager cm = lookup(rootContext, "ConnectionManager/".concat(repositoryName),
470                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(
476                "Connection manager of " + repositoryName + " 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            Transaction transaction = transactionManager.getTransaction();
535            if (transaction == null) {
536                throw new IllegalStateException("No transaction associated with current thread");
537            }
538            Timer.Context timerContext = timers.remove(transaction);
539            transactionManager.commit();
540            if (timerContext != null) {
541                long elapsed = timerContext.stop();
542                SequenceTracer.stop("tx commited");
543                SequenceTracer.stop("tx end " + elapsed / 1000000 + " ms");
544            }
545            concurrentCount.dec();
546        }
547
548        @Override
549        public void rollback() throws IllegalStateException, SecurityException, SystemException {
550            SequenceTracer.mark("tx rollbacking");
551            Transaction transaction = transactionManager.getTransaction();
552            if (transaction == null) {
553                throw new IllegalStateException("No transaction associated with current thread");
554            }
555            Timer.Context timerContext = timers.remove(transaction);
556            transactionManager.rollback();
557            concurrentCount.dec();
558            if (timerContext != null) {
559                long elapsed = timerContext.stop();
560                SequenceTracer.destroy("tx rollbacked " + elapsed / 1000000 + " ms");
561            }
562            rollbackCount.inc();
563        }
564    }
565
566    /**
567     * Creates a Geronimo pooled connection manager using a Geronimo transaction manager.
568     * <p>
569     * The pool uses the transaction manager for recovery, and when using XATransactions for cache + enlist/delist.
570     *
571     * @throws NamingException
572     */
573    public static NuxeoConnectionManager createConnectionManager(NuxeoConnectionTrackingCoordinator coordinator,
574            NuxeoConnectionManagerConfiguration config) {
575        TransactionSupport transactionSupport = createTransactionSupport(config);
576        PoolingSupport poolingSupport = createPoolingSupport(config);
577        NuxeoValidationSupport validationSupport = createValidationSupport(config);
578        return new NuxeoConnectionManager(config.getActiveTimeoutMinutes() * 60 * 1000, validationSupport,
579                transactionSupport, poolingSupport, null, coordinator, tmRecoverable, config.getName(),
580                Thread.currentThread().getContextClassLoader());
581    }
582
583    protected static PoolingSupport createPoolingSupport(NuxeoConnectionManagerConfiguration config) {
584        return new NuxeoPool(config);
585    }
586
587    protected static TransactionSupport createTransactionSupport(NuxeoConnectionManagerConfiguration config) {
588        if (config.getXAMode()) {
589            // note: XATransactions -> TransactionCachingInterceptor ->
590            // ConnectorTransactionContext casts transaction to Geronimo's
591            // TransactionImpl (from TransactionManagerImpl)
592            return new XATransactions(config.getUseTransactionCaching(), config.getUseThreadCaching());
593        }
594        return LocalTransactions.INSTANCE;
595    }
596
597    protected static NuxeoValidationSupport createValidationSupport(NuxeoConnectionManagerConfiguration config) {
598        return new NuxeoValidationSupport(config.testOnBorrow, config.testOnReturn);
599    }
600
601    public static class TransactionManagerConfiguration {
602        public int transactionTimeoutSeconds = 600;
603
604        public void setTransactionTimeoutSeconds(int transactionTimeoutSeconds) {
605            this.transactionTimeoutSeconds = transactionTimeoutSeconds;
606        }
607    }
608
609    /**
610     * Wraps a transaction manager for providing a dummy recoverable interface.
611     *
612     * @author matic
613     */
614    public static class TransactionManagerWrapper implements RecoverableTransactionManager {
615
616        protected TransactionManager tm;
617
618        public TransactionManagerWrapper(TransactionManager tm) {
619            this.tm = tm;
620        }
621
622        @Override
623        public Transaction suspend() throws SystemException {
624            return tm.suspend();
625        }
626
627        @Override
628        public void setTransactionTimeout(int seconds) throws SystemException {
629            tm.setTransactionTimeout(seconds);
630        }
631
632        @Override
633        public void setRollbackOnly() throws IllegalStateException, SystemException {
634            tm.setRollbackOnly();
635        }
636
637        @Override
638        public void rollback() throws IllegalStateException, SecurityException, SystemException {
639            tm.rollback();
640        }
641
642        @Override
643        public void resume(Transaction tobj)
644                throws IllegalStateException, InvalidTransactionException, SystemException {
645            tm.resume(tobj);
646        }
647
648        @Override
649        public int getStatus() throws SystemException {
650            return tm.getStatus();
651        }
652
653        @Override
654        public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
655                RollbackException, SecurityException, SystemException {
656            tm.commit();
657        }
658
659        @Override
660        public void begin() throws SystemException {
661            try {
662                tm.begin();
663            } catch (javax.transaction.NotSupportedException e) {
664                throw new RuntimeException(e);
665            }
666        }
667
668        @Override
669        public void recoveryError(Exception e) {
670            throw new UnsupportedOperationException();
671        }
672
673        @Override
674        public void registerNamedXAResourceFactory(NamedXAResourceFactory factory) {
675            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
676                throw new UnsupportedOperationException();
677            }
678            ((RecoverableTransactionManager) tm).registerNamedXAResourceFactory(factory);
679        }
680
681        @Override
682        public void unregisterNamedXAResourceFactory(String factory) {
683            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
684                throw new UnsupportedOperationException();
685            }
686            ((RecoverableTransactionManager) tm).unregisterNamedXAResourceFactory(factory);
687        }
688
689        @Override
690        public Transaction getTransaction() throws SystemException {
691            final Transaction tx = tm.getTransaction();
692            if (tx instanceof TransactionImpl) {
693                return tx;
694            }
695            return new TransactionImpl(null, null) {
696                @Override
697                public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException,
698                        SecurityException, SystemException {
699                    tx.commit();
700                }
701
702                @Override
703                public void rollback() throws IllegalStateException, SystemException {
704                    tx.rollback();
705                }
706
707                @Override
708                public synchronized boolean enlistResource(XAResource xaRes)
709                        throws IllegalStateException, RollbackException, SystemException {
710                    return tx.enlistResource(xaRes);
711                }
712
713                @Override
714                public synchronized boolean delistResource(XAResource xaRes, int flag)
715                        throws IllegalStateException, SystemException {
716                    return super.delistResource(xaRes, flag);
717                }
718
719                @Override
720                public synchronized void setRollbackOnly() throws IllegalStateException {
721                    try {
722                        tx.setRollbackOnly();
723                    } catch (SystemException e) {
724                        throw new IllegalStateException(e);
725                    }
726                }
727
728                @Override
729                public void registerInterposedSynchronization(javax.transaction.Synchronization synchronization) {
730                    try {
731                        TransactionHelper.lookupSynchronizationRegistry()
732                                         .registerInterposedSynchronization(synchronization);
733                    } catch (NamingException e) {
734                        ;
735                    }
736                }
737            };
738        }
739    }
740
741    /**
742     * Wraps a Geronimo ConnectionManager and adds a {@link #reset} method to flush the pool.
743     */
744    public static class ConnectionManagerWrapper implements ConnectionManager {
745
746        private static final long serialVersionUID = 1L;
747
748        protected NuxeoConnectionTrackingCoordinator coordinator;
749
750        protected volatile NuxeoConnectionManager cm;
751
752        protected final NuxeoConnectionManagerConfiguration config;
753
754        public ConnectionManagerWrapper(NuxeoConnectionTrackingCoordinator coordinator, NuxeoConnectionManager cm,
755                NuxeoConnectionManagerConfiguration config) {
756            this.coordinator = coordinator;
757            this.cm = cm;
758            this.config = config;
759        }
760
761        @Override
762        public Object allocateConnection(ManagedConnectionFactory managedConnectionFactory,
763                ConnectionRequestInfo connectionRequestInfo) throws ResourceException {
764            return cm.allocateConnection(managedConnectionFactory, connectionRequestInfo);
765        }
766
767        public void reset() {
768            AbstractConnectionManager last = cm;
769            cm = createConnectionManager(coordinator, config);
770            try {
771                last.doStop();
772            } catch (Exception e) { // stupid Geronimo API throws Exception
773                throw ExceptionUtils.runtimeException(e);
774            }
775            for (NuxeoContainerListener listener : listeners) {
776                listener.handleConnectionManagerReset(config.getName(), cm);
777            }
778        }
779
780        public List<ActiveMonitor.TimeToLive> killActiveTimedoutConnections(long clock) {
781            return cm.activemonitor.killTimedoutConnections(clock);
782        }
783
784        public void dispose() {
785            for (NuxeoContainerListener listener : listeners) {
786                listener.handleConnectionManagerDispose(config.getName(), cm);
787            }
788            cm.activemonitor.cancelCleanups();
789            NuxeoContainer.connectionManagers.remove(config.getName());
790            try {
791                cm.doStop();
792            } catch (Exception e) { // stupid Geronimo API throws Exception
793                throw ExceptionUtils.runtimeException(e);
794            }
795        }
796
797        public NuxeoConnectionManagerConfiguration getConfiguration() {
798            return config;
799        }
800
801        public NuxeoConnectionManager getManager() {
802            return cm;
803        }
804
805    }
806
807    public static TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
808        return tmSynchRegistry;
809    }
810
811}