1 17 package org.alfresco.repo.transaction; 18 19 import java.util.ArrayList ; 20 import java.util.HashMap ; 21 import java.util.HashSet ; 22 import java.util.List ; 23 import java.util.Map ; 24 import java.util.Set ; 25 26 import org.alfresco.error.AlfrescoRuntimeException; 27 import org.alfresco.repo.node.db.NodeDaoService; 28 import org.alfresco.repo.node.integrity.IntegrityChecker; 29 import org.alfresco.repo.search.impl.lucene.LuceneIndexerAndSearcherFactory; 30 import org.alfresco.service.cmr.rule.RuleService; 31 import org.alfresco.util.GUID; 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.springframework.orm.hibernate3.SessionFactoryUtils; 35 import org.springframework.transaction.support.TransactionSynchronization; 36 import org.springframework.transaction.support.TransactionSynchronizationAdapter; 37 import org.springframework.transaction.support.TransactionSynchronizationManager; 38 39 46 public abstract class AlfrescoTransactionSupport 47 { 48 55 56 59 public static final int SESSION_SYNCHRONIZATION_ORDER = 60 SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER - 100; 61 62 63 private static final String RESOURCE_KEY_TXN_SYNCH = "txnSynch"; 64 65 private static Log logger = LogFactory.getLog(AlfrescoTransactionSupport.class); 66 67 73 public static String getTransactionId() 74 { 75 79 80 TransactionSynchronizationImpl txnSynch = 81 (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); 82 if (txnSynch == null) 83 { 84 if (TransactionSynchronizationManager.isSynchronizationActive()) 85 { 86 return registerSynchronizations().getTransactionId(); 88 } 89 else 90 { 91 return null; } 93 } 94 else 95 { 96 return txnSynch.getTransactionId(); 97 } 98 } 99 100 105 public static boolean isDirty() 106 { 107 TransactionSynchronizationImpl synch = getSynchronization(); 108 109 Set <NodeDaoService> services = synch.getNodeDaoServices(); 110 for (NodeDaoService service : services) 111 { 112 if (service.isDirty()) 113 { 114 return true; 115 } 116 } 117 118 return false; 119 } 120 121 130 public static Object getResource(Object key) 131 { 132 TransactionSynchronizationImpl txnSynch = getSynchronization(); 134 Object resource = txnSynch.resources.get(key); 136 if (logger.isDebugEnabled()) 138 { 139 logger.debug("Fetched resource: \n" + 140 " key: " + key + "\n" + 141 " resource: " + resource); 142 } 143 return resource; 144 } 145 146 154 public static void bindResource(Object key, Object resource) 155 { 156 TransactionSynchronizationImpl txnSynch = getSynchronization(); 158 txnSynch.resources.put(key, resource); 160 if (logger.isDebugEnabled()) 162 { 163 logger.debug("Bound resource: \n" + 164 " key: " + key + "\n" + 165 " resource: " + resource); 166 } 167 } 168 169 176 public static void unbindResource(Object key) 177 { 178 TransactionSynchronizationImpl txnSynch = getSynchronization(); 180 txnSynch.resources.remove(key); 182 if (logger.isDebugEnabled()) 184 { 185 logger.debug("Unbound resource: \n" + 186 " key: " + key); 187 } 188 } 189 190 200 public static void bindNodeDaoService(NodeDaoService nodeDaoService) 201 { 202 TransactionSynchronizationImpl synch = getSynchronization(); 204 205 boolean bound = synch.getNodeDaoServices().add(nodeDaoService); 207 208 if (logger.isDebugEnabled()) 210 { 211 logBoundService(nodeDaoService, bound); 212 } 213 } 214 215 225 public static void bindIntegrityChecker(IntegrityChecker integrityChecker) 226 { 227 TransactionSynchronizationImpl synch = getSynchronization(); 229 230 boolean bound = synch.getIntegrityCheckers().add(integrityChecker); 232 233 if (logger.isDebugEnabled()) 235 { 236 logBoundService(integrityChecker, bound); 237 } 238 } 239 240 253 public static void bindLucene(LuceneIndexerAndSearcherFactory indexerAndSearcher) 254 { 255 TransactionSynchronizationImpl synch = getSynchronization(); 257 258 boolean bound = synch.getLucenes().add(indexerAndSearcher); 260 261 if (logger.isDebugEnabled()) 263 { 264 logBoundService(indexerAndSearcher, bound); 265 } 266 } 267 268 281 public static void bindListener(TransactionListener listener) 282 { 283 TransactionSynchronizationImpl synch = getSynchronization(); 285 286 boolean bound = synch.getListeners().add(listener); 288 289 if (logger.isDebugEnabled()) 291 { 292 logBoundService(listener, bound); 293 } 294 } 295 296 302 private static void logBoundService(Object service, boolean bound) 303 { 304 if (bound) 305 { 306 logger.debug("Bound service: \n" + 307 " transaction: " + getTransactionId() + "\n" + 308 " service: " + service); 309 } 310 else 311 { 312 logger.debug("Service already bound: \n" + 313 " transaction: " + getTransactionId() + "\n" + 314 " service: " + service); 315 } 316 } 317 318 329 public static void flush() 330 { 331 TransactionSynchronizationImpl synch = getSynchronization(); 333 synch.flush(); 335 } 336 337 346 private static TransactionSynchronizationImpl getSynchronization() 347 { 348 return registerSynchronizations(); 350 } 351 352 357 private static TransactionSynchronizationImpl registerSynchronizations() 358 { 359 362 if (!TransactionSynchronizationManager.isSynchronizationActive()) 363 { 364 throw new AlfrescoRuntimeException("Transaction must be active and synchronization is required"); 365 } 366 TransactionSynchronizationImpl txnSynch = 367 (TransactionSynchronizationImpl) TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); 368 if (txnSynch != null) 369 { 370 return txnSynch; 372 } 373 StringBuilder sb = new StringBuilder (56); 375 sb.append(System.currentTimeMillis()).append(":").append(GUID.generate()); 376 String txnId = sb.toString(); 377 txnSynch = new TransactionSynchronizationImpl(txnId); 379 TransactionSynchronizationManager.registerSynchronization(txnSynch); 380 TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); 382 if (logger.isDebugEnabled()) 384 { 385 logger.debug("Bound txn synch: " + txnSynch); 386 } 387 return txnSynch; 388 } 389 390 393 private static void clearSynchronization() 394 { 395 if (TransactionSynchronizationManager.hasResource(RESOURCE_KEY_TXN_SYNCH)) 396 { 397 Object txnSynch = TransactionSynchronizationManager.unbindResource(RESOURCE_KEY_TXN_SYNCH); 398 if (logger.isDebugEnabled()) 400 { 401 logger.debug("Unbound txn synch:" + txnSynch); 402 } 403 } 404 } 405 406 411 private static void rebindSynchronization(TransactionSynchronizationImpl txnSynch) 412 { 413 TransactionSynchronizationManager.bindResource(RESOURCE_KEY_TXN_SYNCH, txnSynch); 414 if (logger.isDebugEnabled()) 415 { 416 logger.debug("Bound txn synch: " + txnSynch); 417 } 418 } 419 420 424 private static class TransactionSynchronizationImpl extends TransactionSynchronizationAdapter 425 { 426 private final String txnId; 427 private final Set <NodeDaoService> nodeDaoServices; 428 private final Set <IntegrityChecker> integrityCheckers; 429 private final Set <LuceneIndexerAndSearcherFactory> lucenes; 430 private final Set <TransactionListener> listeners; 431 private final Map <Object , Object > resources; 432 433 438 public TransactionSynchronizationImpl(String txnId) 439 { 440 this.txnId = txnId; 441 nodeDaoServices = new HashSet <NodeDaoService>(3); 442 integrityCheckers = new HashSet <IntegrityChecker>(3); 443 lucenes = new HashSet <LuceneIndexerAndSearcherFactory>(3); 444 listeners = new HashSet <TransactionListener>(5); 445 resources = new HashMap <Object , Object >(17); 446 } 447 448 public String getTransactionId() 449 { 450 return txnId; 451 } 452 453 457 public Set <NodeDaoService> getNodeDaoServices() 458 { 459 return nodeDaoServices; 460 } 461 462 466 public Set <IntegrityChecker> getIntegrityCheckers() 467 { 468 return integrityCheckers; 469 } 470 471 475 public Set <LuceneIndexerAndSearcherFactory> getLucenes() 476 { 477 return lucenes; 478 } 479 480 484 public Set <TransactionListener> getListeners() 485 { 486 return listeners; 487 } 488 489 492 private List <TransactionListener> getListenersIterable() 493 { 494 return new ArrayList <TransactionListener>(listeners); 495 } 496 497 public String toString() 498 { 499 StringBuilder sb = new StringBuilder (50); 500 sb.append("TransactionSychronizationImpl") 501 .append("[ txnId=").append(txnId) 502 .append(", node service=").append(nodeDaoServices.size()) 503 .append(", integrity=").append(integrityCheckers.size()) 504 .append(", indexers=").append(lucenes.size()) 505 .append(", resources=").append(resources) 506 .append("]"); 507 return sb.toString(); 508 } 509 510 514 public void flush() 515 { 516 for (IntegrityChecker integrityChecker : integrityCheckers) 518 { 519 integrityChecker.checkIntegrity(); 520 } 521 for (TransactionListener listener : getListenersIterable()) 523 { 524 listener.flush(); 525 } 526 } 527 528 531 @Override 532 public int getOrder() 533 { 534 return AlfrescoTransactionSupport.SESSION_SYNCHRONIZATION_ORDER; 535 } 536 537 @Override 538 public void suspend() 539 { 540 if (logger.isDebugEnabled()) 541 { 542 logger.debug("Suspending transaction: " + this); 543 } 544 AlfrescoTransactionSupport.clearSynchronization(); 545 } 546 547 @Override 548 public void resume() 549 { 550 if (logger.isDebugEnabled()) 551 { 552 logger.debug("Resuming transaction: " + this); 553 } 554 AlfrescoTransactionSupport.rebindSynchronization(this); 555 } 556 557 563 @Override 564 public void beforeCommit(boolean readOnly) 565 { 566 if (logger.isDebugEnabled()) 567 { 568 logger.debug("Before commit " + (readOnly ? "read-only" : "" ) + ": " + this); 569 } 570 TransactionSynchronizationImpl synch = (TransactionSynchronizationImpl) 572 TransactionSynchronizationManager.getResource(RESOURCE_KEY_TXN_SYNCH); 573 if (synch == null) 574 { 575 throw new AlfrescoRuntimeException("No synchronization bound to thread"); 576 } 577 578 for (TransactionListener listener : getListenersIterable()) 580 { 581 listener.beforeCommit(readOnly); 582 } 583 584 flush(); 586 for (LuceneIndexerAndSearcherFactory lucene : lucenes) 588 { 589 lucene.prepare(); 590 } 591 } 592 593 @Override 594 public void beforeCompletion() 595 { 596 if (logger.isDebugEnabled()) 597 { 598 logger.debug("Before completion: " + this); 599 } 600 for (TransactionListener listener : getListenersIterable()) 602 { 603 listener.beforeCompletion(); 604 } 605 } 606 607 608 @Override 609 public void afterCompletion(int status) 610 { 611 String statusStr = "unknown"; 612 switch (status) 613 { 614 case TransactionSynchronization.STATUS_COMMITTED: 615 statusStr = "committed"; 616 break; 617 case TransactionSynchronization.STATUS_ROLLED_BACK: 618 statusStr = "rolled-back"; 619 break; 620 default: 621 } 622 if (logger.isDebugEnabled()) 623 { 624 logger.debug("After completion (" + statusStr + "): " + this); 625 } 626 627 for (LuceneIndexerAndSearcherFactory lucene : lucenes) 629 { 630 try 631 { 632 if (status == TransactionSynchronization.STATUS_COMMITTED) 633 { 634 lucene.commit(); 635 } 636 else 637 { 638 lucene.rollback(); 639 } 640 } 641 catch (RuntimeException e) 642 { 643 logger.error("After completion (" + statusStr + ") Lucene exception", e); 644 } 645 } 646 647 List <TransactionListener> iterableListeners = getListenersIterable(); 648 if (status == TransactionSynchronization.STATUS_COMMITTED) 650 { 651 for (TransactionListener listener : iterableListeners) 652 { 653 try 654 { 655 listener.afterCommit(); 656 } 657 catch (RuntimeException e) 658 { 659 logger.error("After completion (" + statusStr + ") listener exception: \n" + 660 " listener: " + listener, 661 e); 662 } 663 } 664 } 665 else 666 { 667 for (TransactionListener listener : iterableListeners) 668 { 669 try 670 { 671 listener.afterRollback(); 672 } 673 catch (RuntimeException e) 674 { 675 logger.error("After completion (" + statusStr + ") listener exception: \n" + 676 " listener: " + listener, 677 e); 678 } 679 } 680 } 681 682 AlfrescoTransactionSupport.clearSynchronization(); 684 } 685 } 686 } 687 | Popular Tags |