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            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, transactionSupport, poolingSupport, null, coordinator, tmRecoverable,
579                config.getName(), Thread.currentThread().getContextClassLoader());
580    }
581
582    protected static PoolingSupport createPoolingSupport(NuxeoConnectionManagerConfiguration config) {
583        return new NuxeoPool(config);
584    }
585
586    protected static TransactionSupport createTransactionSupport(NuxeoConnectionManagerConfiguration config) {
587        if (config.getXAMode()) {
588            // note: XATransactions -> TransactionCachingInterceptor ->
589            // ConnectorTransactionContext casts transaction to Geronimo's
590            // TransactionImpl (from TransactionManagerImpl)
591            return new XATransactions(config.getUseTransactionCaching(), config.getUseThreadCaching());
592        }
593        return LocalTransactions.INSTANCE;
594    }
595
596    protected static NuxeoValidationSupport createValidationSupport(NuxeoConnectionManagerConfiguration config) {
597        return new NuxeoValidationSupport(config.testOnBorrow, config.testOnReturn);
598    }
599
600    public static class TransactionManagerConfiguration {
601        public int transactionTimeoutSeconds = 600;
602
603        public void setTransactionTimeoutSeconds(int transactionTimeoutSeconds) {
604            this.transactionTimeoutSeconds = transactionTimeoutSeconds;
605        }
606    }
607
608    /**
609     * Wraps a transaction manager for providing a dummy recoverable interface.
610     *
611     * @author matic
612     */
613    public static class TransactionManagerWrapper implements RecoverableTransactionManager {
614
615        protected TransactionManager tm;
616
617        public TransactionManagerWrapper(TransactionManager tm) {
618            this.tm = tm;
619        }
620
621        @Override
622        public Transaction suspend() throws SystemException {
623            return tm.suspend();
624        }
625
626        @Override
627        public void setTransactionTimeout(int seconds) throws SystemException {
628            tm.setTransactionTimeout(seconds);
629        }
630
631        @Override
632        public void setRollbackOnly() throws IllegalStateException, SystemException {
633            tm.setRollbackOnly();
634        }
635
636        @Override
637        public void rollback() throws IllegalStateException, SecurityException, SystemException {
638            tm.rollback();
639        }
640
641        @Override
642        public void resume(Transaction tobj) throws IllegalStateException, InvalidTransactionException, SystemException {
643            tm.resume(tobj);
644        }
645
646        @Override
647        public int getStatus() throws SystemException {
648            return tm.getStatus();
649        }
650
651        @Override
652        public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
653                RollbackException, SecurityException, SystemException {
654            tm.commit();
655        }
656
657        @Override
658        public void begin() throws SystemException {
659            try {
660                tm.begin();
661            } catch (javax.transaction.NotSupportedException e) {
662                throw new RuntimeException(e);
663            }
664        }
665
666        @Override
667        public void recoveryError(Exception e) {
668            throw new UnsupportedOperationException();
669        }
670
671        @Override
672        public void registerNamedXAResourceFactory(NamedXAResourceFactory factory) {
673            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
674                throw new UnsupportedOperationException();
675            }
676            ((RecoverableTransactionManager) tm).registerNamedXAResourceFactory(factory);
677        }
678
679        @Override
680        public void unregisterNamedXAResourceFactory(String factory) {
681            if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) {
682                throw new UnsupportedOperationException();
683            }
684            ((RecoverableTransactionManager) tm).unregisterNamedXAResourceFactory(factory);
685        }
686
687        @Override
688        public Transaction getTransaction() throws SystemException {
689            final Transaction tx = tm.getTransaction();
690            if (tx instanceof TransactionImpl) {
691                return tx;
692            }
693            return new TransactionImpl(null, null) {
694                @Override
695                public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException,
696                        SecurityException, SystemException {
697                    tx.commit();
698                }
699
700                @Override
701                public void rollback() throws IllegalStateException, SystemException {
702                    tx.rollback();
703                }
704
705                @Override
706                public synchronized boolean enlistResource(XAResource xaRes) throws IllegalStateException,
707                        RollbackException, SystemException {
708                    return tx.enlistResource(xaRes);
709                }
710
711                @Override
712                public synchronized boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException,
713                        SystemException {
714                    return super.delistResource(xaRes, flag);
715                }
716
717                @Override
718                public synchronized void setRollbackOnly() throws IllegalStateException {
719                    try {
720                        tx.setRollbackOnly();
721                    } catch (SystemException e) {
722                        throw new IllegalStateException(e);
723                    }
724                }
725
726                @Override
727                public void registerInterposedSynchronization(javax.transaction.Synchronization synchronization) {
728                    try {
729                        TransactionHelper.lookupSynchronizationRegistry().registerInterposedSynchronization(
730                                synchronization);
731                    } catch (NamingException e) {;
732                    }
733                }
734            };
735        }
736    }
737
738    /**
739     * Wraps a Geronimo ConnectionManager and adds a {@link #reset} method to flush the pool.
740     */
741    public static class ConnectionManagerWrapper implements ConnectionManager {
742
743        private static final long serialVersionUID = 1L;
744
745        protected NuxeoConnectionTrackingCoordinator coordinator;
746
747        protected volatile NuxeoConnectionManager cm;
748
749        protected final NuxeoConnectionManagerConfiguration config;
750
751        public ConnectionManagerWrapper(NuxeoConnectionTrackingCoordinator coordinator, NuxeoConnectionManager cm,
752                NuxeoConnectionManagerConfiguration config) {
753            this.coordinator = coordinator;
754            this.cm = cm;
755            this.config = config;
756        }
757
758        @Override
759        public Object allocateConnection(ManagedConnectionFactory managedConnectionFactory,
760                ConnectionRequestInfo connectionRequestInfo) throws ResourceException {
761            return cm.allocateConnection(managedConnectionFactory, connectionRequestInfo);
762        }
763
764        public void reset() {
765            AbstractConnectionManager last = cm;
766            cm = createConnectionManager(coordinator, config);
767            try {
768                last.doStop();
769            } catch (Exception e) { // stupid Geronimo API throws Exception
770                throw ExceptionUtils.runtimeException(e);
771            }
772            for (NuxeoContainerListener listener : listeners) {
773                listener.handleConnectionManagerReset(config.getName(), cm);
774            }
775        }
776
777        public List<ActiveMonitor.TimeToLive> killActiveTimedoutConnections(long clock) {
778            return cm.activemonitor.killTimedoutConnections(clock);
779        }
780
781        public void dispose() {
782            for (NuxeoContainerListener listener : listeners) {
783                listener.handleConnectionManagerDispose(config.getName(), cm);
784            }
785            cm.activemonitor.cancelCleanups();
786            NuxeoContainer.connectionManagers.remove(config.getName());
787            try {
788                cm.doStop();
789            } catch (Exception e) { // stupid Geronimo API throws Exception
790                throw ExceptionUtils.runtimeException(e);
791            }
792        }
793
794        public NuxeoConnectionManagerConfiguration getConfiguration() {
795            return config;
796        }
797
798        public NuxeoConnectionManager getManager() {
799            return cm;
800        }
801
802    }
803
804    public static TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() {
805        return tmSynchRegistry;
806    }
807
808}