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 synchronized void install() throws NamingException { 137 if (installContext != null) { 138 throw new RuntimeException("Nuxeo container already installed"); 139 } 140 installContext = new InstallContext(); 141 rootContext = new NamingContext(); 142 parentContext = InitialContextAccessor.getInitialContext(); 143 if (parentContext != null && parentContext != rootContext) { 144 installTransactionManager(parentContext); 145 } else { 146 addDeepBinding(nameOf("TransactionManager"), new Reference(TransactionManager.class.getName(), 147 NuxeoTransactionManagerFactory.class.getName(), null)); 148 installTransactionManager(rootContext); 149 } 150 } 151 152 protected static void installTransactionManager(TransactionManagerConfiguration config) throws NamingException { 153 initTransactionManager(config); 154 addDeepBinding(rootContext, new CompositeName(nameOf("TransactionManager")), getTransactionManagerReference()); 155 addDeepBinding(rootContext, new CompositeName(nameOf("UserTransaction")), getUserTransactionReference()); 156 } 157 158 /** 159 * Creates and installs in the container a new ConnectionManager. 160 * 161 * @param name the repository name 162 * @param config the pool configuration 163 * @return the created connection manager 164 */ 165 public static synchronized ConnectionManagerWrapper installConnectionManager( 166 NuxeoConnectionManagerConfiguration config) { 167 String name = config.getName(); 168 ConnectionManagerWrapper cm = connectionManagers.get(name); 169 if (cm != null) { 170 return cm; 171 } 172 cm = initConnectionManager(config); 173 // also bind it in JNDI 174 if (rootContext != null) { 175 String jndiName = nameOf("ConnectionManager/".concat(name)); 176 try { 177 addDeepBinding(rootContext, new CompositeName(jndiName), getConnectionManagerReference(name)); 178 } catch (NamingException e) { 179 log.error("Cannot bind in JNDI connection manager " + config.getName() + " to name " + jndiName); 180 } 181 } 182 return cm; 183 } 184 185 public static boolean isInstalled() { 186 return installContext != null; 187 } 188 189 protected static void uninstall() throws NamingException { 190 if (installContext == null) { 191 throw new RuntimeException("Nuxeo container not installed"); 192 } 193 try { 194 NamingException errors = new NamingException("Cannot shutdown connection managers"); 195 for (ConnectionManagerWrapper cm : connectionManagers.values()) { 196 try { 197 cm.dispose(); 198 } catch (RuntimeException cause) { 199 errors.addSuppressed(cause); 200 } 201 } 202 if (errors.getSuppressed().length > 0) { 203 log.error("Cannot shutdown some pools", errors); 204 throw errors; 205 } 206 } finally { 207 log.trace("Uninstalling nuxeo container", installContext); 208 installContext = null; 209 rootContext = null; 210 tm = null; 211 tmRecoverable = null; 212 tmSynchRegistry = null; 213 ut = null; 214 connectionManagers.clear(); 215 } 216 } 217 218 /** 219 * @since 5.8 220 */ 221 public static void addListener(NuxeoContainerListener listener) { 222 synchronized (listeners) { 223 listeners.add(listener); 224 } 225 for (Map.Entry<String, ConnectionManagerWrapper> entry : connectionManagers.entrySet()) { 226 listener.handleNewConnectionManager(entry.getKey(), entry.getValue().cm); 227 } 228 } 229 230 /** 231 * @since 5.8 232 */ 233 public static void removeListener(NuxeoContainerListener listener) { 234 synchronized (listeners) { 235 listeners.remove(listener); 236 } 237 } 238 239 protected static String detectJNDIPrefix(Context context) { 240 String name = context.getClass().getName(); 241 if ("org.jnp.interfaces.NamingContext".equals(name)) { // JBoss 242 return "java:"; 243 } else if ("org.jboss.as.naming.InitialContext".equals(name)) { // Wildfly 244 return "java:jboss/"; 245 } else if ("org.mortbay.naming.local.localContextRoot".equals(name)) { // Jetty 246 return "jdbc/"; 247 } 248 // Standard JEE containers (Nuxeo-Embedded, Tomcat, GlassFish, 249 // ... 250 return "java:comp/env/"; 251 } 252 253 public static String nameOf(String name) { 254 return jndiPrefix.concat(name); 255 } 256 257 /** 258 * Exposes the {@link #rootContext}. 259 * 260 * @since 5.7 261 * @see https://jira.nuxeo.com/browse/NXP-10331 262 */ 263 public static Context getRootContext() { 264 return rootContext; 265 } 266 267 /** 268 * Bind object in root context. Create needed sub contexts. since 5.6 269 */ 270 public static void addDeepBinding(String name, Object obj) throws NamingException { 271 addDeepBinding(rootContext, new CompositeName(name), obj); 272 } 273 274 protected static void addDeepBinding(Context dir, CompositeName comp, Object obj) throws NamingException { 275 Name name = comp.getPrefix(1); 276 if (comp.size() == 1) { 277 addBinding(dir, name, obj); 278 return; 279 } 280 Context subdir; 281 try { 282 subdir = (Context) dir.lookup(name); 283 } catch (NamingException e) { 284 subdir = dir.createSubcontext(name); 285 } 286 addDeepBinding(subdir, (CompositeName) comp.getSuffix(1), obj); 287 } 288 289 protected static void addBinding(Context dir, Name name, Object obj) throws NamingException { 290 try { 291 dir.rebind(name, obj); 292 } catch (NamingException e) { 293 dir.bind(name, obj); 294 } 295 } 296 297 protected static void removeBinding(String name) throws NamingException { 298 rootContext.unbind(name); 299 } 300 301 /** 302 * Gets the transaction manager used by the container. 303 * 304 * @return the transaction manager 305 */ 306 public static TransactionManager getTransactionManager() { 307 return tm; 308 } 309 310 protected static Reference getTransactionManagerReference() { 311 return new SimpleReference() { 312 private static final long serialVersionUID = 1L; 313 314 @Override 315 public Object getContent() throws NamingException { 316 return NuxeoContainer.getTransactionManager(); 317 } 318 }; 319 } 320 321 /** 322 * Gets the user transaction used by the container. 323 * 324 * @return the user transaction 325 */ 326 public static UserTransaction getUserTransaction() { 327 return ut; 328 } 329 330 protected static Reference getUserTransactionReference() { 331 return new SimpleReference() { 332 private static final long serialVersionUID = 1L; 333 334 @Override 335 public Object getContent() throws NamingException { 336 return getUserTransaction(); 337 } 338 }; 339 } 340 341 /** 342 * Gets the Nuxeo connection manager used by the container. 343 * 344 * @return the connection manager 345 */ 346 public static NuxeoConnectionManager getConnectionManager(String repositoryName) { 347 ConnectionManagerWrapper wrapper = connectionManagers.get(repositoryName); 348 if (wrapper == null) { 349 return null; 350 } 351 return wrapper.cm; 352 } 353 354 public static void installConnectionManager(ConnectionManagerWrapper wrapper) { 355 String name = wrapper.config.getName(); 356 if (connectionManagers.containsKey(name)) { 357 log.error("Connection manager " + name + " already set up", new Exception()); 358 } 359 connectionManagers.put(name, wrapper); 360 for (NuxeoContainerListener listener : listeners) { 361 listener.handleNewConnectionManager(name, wrapper.cm); 362 } 363 } 364 365 protected static Reference getConnectionManagerReference(final String name) { 366 return new SimpleReference() { 367 private static final long serialVersionUID = 1L; 368 369 @Override 370 public Object getContent() throws NamingException { 371 return getConnectionManager(name); 372 } 373 }; 374 } 375 376 protected static synchronized TransactionManager initTransactionManager(TransactionManagerConfiguration config) { 377 TransactionManagerImpl impl = createTransactionManager(config); 378 tm = impl; 379 tmRecoverable = impl; 380 tmSynchRegistry = impl; 381 ut = new UserTransactionImpl(tm); 382 return tm; 383 } 384 385 protected static TransactionManagerWrapper wrapTransactionManager(TransactionManager tm) { 386 if (tm == null) { 387 return null; 388 } 389 if (tm instanceof TransactionManagerWrapper) { 390 return (TransactionManagerWrapper) tm; 391 } 392 return new TransactionManagerWrapper(tm); 393 } 394 395 public static synchronized ConnectionManagerWrapper initConnectionManager( 396 NuxeoConnectionManagerConfiguration config) { 397 NuxeoConnectionTrackingCoordinator coordinator = new NuxeoConnectionTrackingCoordinator(); 398 NuxeoConnectionManager cm = createConnectionManager(coordinator, config); 399 ConnectionManagerWrapper cmw = new ConnectionManagerWrapper(coordinator, cm, config); 400 installConnectionManager(cmw); 401 return cmw; 402 } 403 404 public static synchronized void disposeConnectionManager(ConnectionManager mgr) { 405 ((ConnectionManagerWrapper) mgr).dispose(); 406 } 407 408 // called by reflection from RepositoryReloader 409 public static synchronized void resetConnectionManager() { 410 RuntimeException errors = new RuntimeException("Cannot reset connection managers"); 411 for (ConnectionManagerWrapper wrapper : connectionManagers.values()) { 412 try { 413 wrapper.reset(); 414 } catch (RuntimeException cause) { 415 errors.addSuppressed(cause); 416 } 417 } 418 if (errors.getSuppressed().length > 0) { 419 throw errors; 420 } 421 } 422 423 public static synchronized void resetConnectionManager(String name) { 424 connectionManagers.get(name).reset(); 425 } 426 427 public static <T> T lookup(String name, Class<T> type) throws NamingException { 428 if (rootContext == null) { 429 throw new NamingException("no naming context available"); 430 } 431 return lookup(rootContext, name, type); 432 } 433 434 public static <T> T lookup(Context context, String name, Class<T> type) throws NamingException { 435 Object resolved; 436 try { 437 resolved = context.lookup(detectJNDIPrefix(context).concat(name)); 438 } catch (NamingException cause) { 439 if (parentContext == null) { 440 throw cause; 441 } 442 return type.cast(parentContext.lookup(detectJNDIPrefix(parentContext).concat(name))); 443 } 444 if (resolved instanceof Reference) { 445 try { 446 resolved = NamingManager.getObjectInstance(resolved, new CompositeName(name), rootContext, null); 447 } catch (NamingException e) { 448 throw e; 449 } catch (Exception e) { // stupid JNDI API throws Exception 450 throw ExceptionUtils.runtimeException(e); 451 } 452 } 453 return type.cast(resolved); 454 } 455 456 protected static void installTransactionManager(Context context) throws NamingException { 457 TransactionManager actual = lookup(context, "TransactionManager", TransactionManager.class); 458 if (tm != null) { 459 return; 460 } 461 tm = actual; 462 tmRecoverable = wrapTransactionManager(tm); 463 ut = new UserTransactionImpl(tm); 464 tmSynchRegistry = (TransactionSynchronizationRegistry) tm; 465 } 466 467 protected static ConnectionManagerWrapper lookupConnectionManager(String repositoryName) throws NamingException { 468 ConnectionManager cm = lookup(rootContext, "ConnectionManager/".concat(repositoryName), 469 ConnectionManager.class); 470 if (cm instanceof ConnectionManagerWrapper) { 471 return (ConnectionManagerWrapper) cm; 472 } 473 log.warn("Connection manager not a wrapper, check your configuration"); 474 throw new RuntimeException( 475 "Connection manager of " + repositoryName + " not a wrapper, check your configuration"); 476 } 477 478 protected static TransactionManagerImpl createTransactionManager(TransactionManagerConfiguration config) { 479 if (config == null) { 480 config = new TransactionManagerConfiguration(); 481 } 482 try { 483 return new TransactionManagerImpl(config.transactionTimeoutSeconds); 484 } catch (XAException e) { 485 // failed in recovery somewhere 486 throw new RuntimeException(e.toString(), e); 487 } 488 } 489 490 /** 491 * User transaction that uses this container's transaction manager. 492 * 493 * @since 5.6 494 */ 495 public static class UserTransactionImpl implements UserTransaction { 496 497 protected final TransactionManager transactionManager; 498 499 public UserTransactionImpl(TransactionManager manager) { 500 transactionManager = manager; 501 } 502 503 @Override 504 public int getStatus() throws SystemException { 505 return transactionManager.getStatus(); 506 } 507 508 @Override 509 public void setRollbackOnly() throws IllegalStateException, SystemException { 510 transactionManager.setRollbackOnly(); 511 } 512 513 @Override 514 public void setTransactionTimeout(int seconds) throws SystemException { 515 transactionManager.setTransactionTimeout(seconds); 516 } 517 518 @Override 519 public void begin() throws NotSupportedException, SystemException { 520 SequenceTracer.start("tx begin", "#DarkSalmon"); 521 transactionManager.begin(); 522 timers.put(transactionManager.getTransaction(), transactionTimer.time()); 523 concurrentCount.inc(); 524 if (concurrentCount.getCount() > concurrentMaxCount.getCount()) { 525 concurrentMaxCount.inc(); 526 } 527 } 528 529 @Override 530 public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, 531 RollbackException, SecurityException, SystemException { 532 SequenceTracer.start("tx commiting", "#de6238"); 533 Transaction transaction = transactionManager.getTransaction(); 534 if (transaction == null) { 535 throw new IllegalStateException("No transaction associated with current thread"); 536 } 537 Timer.Context timerContext = timers.remove(transaction); 538 transactionManager.commit(); 539 if (timerContext != null) { 540 long elapsed = timerContext.stop(); 541 SequenceTracer.stop("tx commited"); 542 SequenceTracer.stop("tx end " + elapsed / 1000000 + " ms"); 543 } 544 concurrentCount.dec(); 545 } 546 547 @Override 548 public void rollback() throws IllegalStateException, SecurityException, SystemException { 549 SequenceTracer.mark("tx rollbacking"); 550 Transaction transaction = transactionManager.getTransaction(); 551 if (transaction == null) { 552 throw new IllegalStateException("No transaction associated with current thread"); 553 } 554 Timer.Context timerContext = timers.remove(transaction); 555 transactionManager.rollback(); 556 concurrentCount.dec(); 557 if (timerContext != null) { 558 long elapsed = timerContext.stop(); 559 SequenceTracer.destroy("tx rollbacked " + elapsed / 1000000 + " ms"); 560 } 561 rollbackCount.inc(); 562 } 563 } 564 565 /** 566 * Creates a Geronimo pooled connection manager using a Geronimo transaction manager. 567 * <p> 568 * The pool uses the transaction manager for recovery, and when using XATransactions for cache + enlist/delist. 569 * 570 * @throws NamingException 571 */ 572 public static NuxeoConnectionManager createConnectionManager(NuxeoConnectionTrackingCoordinator coordinator, 573 NuxeoConnectionManagerConfiguration config) { 574 TransactionSupport transactionSupport = createTransactionSupport(config); 575 PoolingSupport poolingSupport = createPoolingSupport(config); 576 NuxeoValidationSupport validationSupport = createValidationSupport(config); 577 return new NuxeoConnectionManager(config.getActiveTimeoutMinutes() * 60 * 1000, validationSupport, 578 transactionSupport, poolingSupport, null, coordinator, tmRecoverable, config.getName(), 579 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) 643 throws IllegalStateException, InvalidTransactionException, SystemException { 644 tm.resume(tobj); 645 } 646 647 @Override 648 public int getStatus() throws SystemException { 649 return tm.getStatus(); 650 } 651 652 @Override 653 public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, 654 RollbackException, SecurityException, SystemException { 655 tm.commit(); 656 } 657 658 @Override 659 public void begin() throws SystemException { 660 try { 661 tm.begin(); 662 } catch (javax.transaction.NotSupportedException e) { 663 throw new RuntimeException(e); 664 } 665 } 666 667 @Override 668 public void recoveryError(Exception e) { 669 throw new UnsupportedOperationException(); 670 } 671 672 @Override 673 public void registerNamedXAResourceFactory(NamedXAResourceFactory factory) { 674 if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) { 675 throw new UnsupportedOperationException(); 676 } 677 ((RecoverableTransactionManager) tm).registerNamedXAResourceFactory(factory); 678 } 679 680 @Override 681 public void unregisterNamedXAResourceFactory(String factory) { 682 if (!RecoverableTransactionManager.class.isAssignableFrom(tm.getClass())) { 683 throw new UnsupportedOperationException(); 684 } 685 ((RecoverableTransactionManager) tm).unregisterNamedXAResourceFactory(factory); 686 } 687 688 @Override 689 public Transaction getTransaction() throws SystemException { 690 final Transaction tx = tm.getTransaction(); 691 if (tx instanceof TransactionImpl) { 692 return tx; 693 } 694 return new TransactionImpl(null, null) { 695 @Override 696 public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, 697 SecurityException, SystemException { 698 tx.commit(); 699 } 700 701 @Override 702 public void rollback() throws IllegalStateException, SystemException { 703 tx.rollback(); 704 } 705 706 @Override 707 public synchronized boolean enlistResource(XAResource xaRes) 708 throws IllegalStateException, RollbackException, SystemException { 709 return tx.enlistResource(xaRes); 710 } 711 712 @Override 713 public synchronized boolean delistResource(XAResource xaRes, int flag) 714 throws IllegalStateException, SystemException { 715 return super.delistResource(xaRes, flag); 716 } 717 718 @Override 719 public synchronized void setRollbackOnly() throws IllegalStateException { 720 try { 721 tx.setRollbackOnly(); 722 } catch (SystemException e) { 723 throw new IllegalStateException(e); 724 } 725 } 726 727 @Override 728 public void registerInterposedSynchronization(javax.transaction.Synchronization synchronization) { 729 try { 730 TransactionHelper.lookupSynchronizationRegistry() 731 .registerInterposedSynchronization(synchronization); 732 } catch (NamingException e) { 733 ; 734 } 735 } 736 }; 737 } 738 } 739 740 /** 741 * Wraps a Geronimo ConnectionManager and adds a {@link #reset} method to flush the pool. 742 */ 743 public static class ConnectionManagerWrapper implements ConnectionManager { 744 745 private static final long serialVersionUID = 1L; 746 747 protected NuxeoConnectionTrackingCoordinator coordinator; 748 749 protected volatile NuxeoConnectionManager cm; 750 751 protected final NuxeoConnectionManagerConfiguration config; 752 753 public ConnectionManagerWrapper(NuxeoConnectionTrackingCoordinator coordinator, NuxeoConnectionManager cm, 754 NuxeoConnectionManagerConfiguration config) { 755 this.coordinator = coordinator; 756 this.cm = cm; 757 this.config = config; 758 } 759 760 @Override 761 public Object allocateConnection(ManagedConnectionFactory managedConnectionFactory, 762 ConnectionRequestInfo connectionRequestInfo) throws ResourceException { 763 return cm.allocateConnection(managedConnectionFactory, connectionRequestInfo); 764 } 765 766 public void reset() { 767 AbstractConnectionManager last = cm; 768 cm = createConnectionManager(coordinator, config); 769 try { 770 last.doStop(); 771 } catch (Exception e) { // stupid Geronimo API throws Exception 772 throw ExceptionUtils.runtimeException(e); 773 } 774 for (NuxeoContainerListener listener : listeners) { 775 listener.handleConnectionManagerReset(config.getName(), cm); 776 } 777 } 778 779 public List<ActiveMonitor.TimeToLive> killActiveTimedoutConnections(long clock) { 780 return cm.activemonitor.killTimedoutConnections(clock); 781 } 782 783 public void dispose() { 784 for (NuxeoContainerListener listener : listeners) { 785 listener.handleConnectionManagerDispose(config.getName(), cm); 786 } 787 cm.activemonitor.cancelCleanups(); 788 NuxeoContainer.connectionManagers.remove(config.getName()); 789 try { 790 cm.doStop(); 791 } catch (Exception e) { // stupid Geronimo API throws Exception 792 throw ExceptionUtils.runtimeException(e); 793 } 794 } 795 796 public NuxeoConnectionManagerConfiguration getConfiguration() { 797 return config; 798 } 799 800 public NuxeoConnectionManager getManager() { 801 return cm; 802 } 803 804 } 805 806 public static TransactionSynchronizationRegistry getTransactionSynchronizationRegistry() { 807 return tmSynchRegistry; 808 } 809 810}