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}