1 17 package org.alfresco.util.transaction; 18 19 import java.lang.reflect.Method ; 20 21 import javax.transaction.HeuristicMixedException ; 22 import javax.transaction.HeuristicRollbackException ; 23 import javax.transaction.NotSupportedException ; 24 import javax.transaction.RollbackException ; 25 import javax.transaction.Status ; 26 import javax.transaction.SystemException ; 27 import javax.transaction.UserTransaction ; 28 29 import org.alfresco.error.StackTraceUtil; 30 import org.apache.commons.logging.Log; 31 import org.apache.commons.logging.LogFactory; 32 import org.springframework.transaction.NoTransactionException; 33 import org.springframework.transaction.PlatformTransactionManager; 34 import org.springframework.transaction.TransactionDefinition; 35 import org.springframework.transaction.TransactionStatus; 36 import org.springframework.transaction.interceptor.TransactionAspectSupport; 37 import org.springframework.transaction.interceptor.TransactionAttribute; 38 import org.springframework.transaction.interceptor.TransactionAttributeSource; 39 40 57 public class SpringAwareUserTransaction 58 extends TransactionAspectSupport 59 implements UserTransaction , TransactionAttributeSource, TransactionAttribute 60 { 61 67 68 private static final long serialVersionUID = 3762538897183224373L; 69 70 private static final String NAME = "UserTransaction"; 71 72 private static final Log logger = LogFactory.getLog(SpringAwareUserTransaction.class); 73 private static final Log traceLogger = LogFactory.getLog(SpringAwareUserTransaction.class.getName() + ".trace"); 74 static 75 { 76 if (traceLogger.isDebugEnabled()) 77 { 78 traceLogger.warn("Trace logging is enabled and will affect performance"); 79 } 80 } 81 82 83 private StackTraceElement [] traceDebugBeginTrace; 84 85 private boolean readOnly; 86 private int isolationLevel; 87 private int propagationBehaviour; 88 private int timeout; 89 90 91 private int internalStatus = Status.STATUS_NO_TRANSACTION; 92 93 private TransactionInfo internalTxnInfo; 94 95 private long threadId = Long.MIN_VALUE; 96 97 private boolean finalized = false; 98 99 114 public SpringAwareUserTransaction( 115 PlatformTransactionManager transactionManager, 116 boolean readOnly, 117 int isolationLevel, 118 int propagationBehaviour, 119 int timeout) 120 { 121 super(); 122 setTransactionManager(transactionManager); 123 setTransactionAttributeSource(this); 124 this.readOnly = readOnly; 125 this.isolationLevel = isolationLevel; 126 this.propagationBehaviour = propagationBehaviour; 127 this.timeout = timeout; 128 } 129 130 136 public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) 137 { 138 return this; 139 } 140 141 149 public boolean rollbackOn(Throwable ex) 150 { 151 return true; 152 } 153 154 157 public String getName() 158 { 159 return NAME; 160 } 161 162 public boolean isReadOnly() 163 { 164 return readOnly; 165 } 166 167 public int getIsolationLevel() 168 { 169 return isolationLevel; 170 } 171 172 public int getPropagationBehavior() 173 { 174 return propagationBehaviour; 175 } 176 177 public int getTimeout() 178 { 179 return timeout; 180 } 181 182 185 public void setTransactionTimeout(int timeout) throws SystemException 186 { 187 if (internalStatus != Status.STATUS_NO_TRANSACTION) 188 { 189 throw new RuntimeException ("Can only set the timeout before begin"); 190 } 191 this.timeout = timeout; 192 } 193 194 205 private TransactionInfo getTransactionInfo() 206 { 207 if (threadId < 0 && internalStatus != Status.STATUS_NO_TRANSACTION) 209 { 210 throw new RuntimeException ("Transaction has been started but there is no thread ID"); 211 } 212 else if (threadId >= 0 && internalStatus == Status.STATUS_NO_TRANSACTION) 213 { 214 throw new RuntimeException ("Transaction has not been started but a thread ID has been recorded"); 215 } 216 217 TransactionInfo txnInfo = null; 218 try 219 { 220 txnInfo = TransactionAspectSupport.currentTransactionInfo(); 221 } 223 catch (NoTransactionException e) 224 { 225 } 227 if (internalStatus == Status.STATUS_ACTIVE) 229 { 230 if (Thread.currentThread().getId() != threadId) 231 { 232 throw new RuntimeException ("UserTransaction may not be accessed by multiple threads"); 235 } 236 else if (txnInfo == null) 237 { 238 throw new RuntimeException ("Transaction boundaries have been made to overlap in the stack"); 240 } 241 else if (txnInfo != internalTxnInfo) 242 { 243 throw new RuntimeException ("UserTransaction begin/commit mismatch"); 245 } 246 } 247 return txnInfo; 248 } 249 250 256 public synchronized int getStatus() throws SystemException 257 { 258 TransactionInfo txnInfo = getTransactionInfo(); 259 260 if (txnInfo == null) 262 { 263 return internalStatus; } 265 266 TransactionStatus txnStatus = txnInfo.getTransactionStatus(); 270 if (internalStatus == Status.STATUS_ROLLEDBACK) 271 { 272 return internalStatus; 274 } 275 else if (txnStatus.isRollbackOnly()) 276 { 277 return Status.STATUS_MARKED_ROLLBACK; 279 } 280 else 281 { 282 return internalStatus; 284 } 285 } 286 287 public synchronized void setRollbackOnly() throws IllegalStateException , SystemException 288 { 289 TransactionInfo txnInfo = getTransactionInfo(); 291 292 int status = getStatus(); 293 if (status == Status.STATUS_MARKED_ROLLBACK) 295 { 296 } 298 else if (status == Status.STATUS_NO_TRANSACTION) 299 { 300 throw new IllegalStateException ("The transaction has not been started yet"); 301 } 302 else if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) 303 { 304 throw new IllegalStateException ("The transaction has already been rolled back"); 305 } 306 else if (status == Status.STATUS_COMMITTING || status == Status.STATUS_COMMITTED) 307 { 308 throw new IllegalStateException ("The transaction has already been committed"); 309 } 310 else if (status != Status.STATUS_ACTIVE) 311 { 312 throw new IllegalStateException ("The transaction is not active: " + status); 313 } 314 315 txnInfo.getTransactionStatus().setRollbackOnly(); 317 internalStatus = Status.STATUS_MARKED_ROLLBACK; 319 if (logger.isDebugEnabled()) 321 { 322 logger.debug("Set transaction status to rollback only: " + this); 323 } 324 } 325 326 329 public synchronized void begin() throws NotSupportedException , SystemException 330 { 331 if (traceLogger.isDebugEnabled()) 332 { 333 Exception e = new Exception (); 335 e.fillInStackTrace(); 336 traceDebugBeginTrace = e.getStackTrace(); 337 } 338 339 TransactionInfo txnInfo = getTransactionInfo(); 341 if (internalStatus != Status.STATUS_NO_TRANSACTION) 342 { 343 throw new NotSupportedException ("The UserTransaction may not be reused"); 344 } 345 346 internalTxnInfo = createTransactionIfNecessary(null, null); internalStatus = Status.STATUS_ACTIVE; 349 threadId = Thread.currentThread().getId(); 350 351 if (logger.isDebugEnabled()) 353 { 354 logger.debug("Began user transaction: " + this); 355 } 356 } 357 358 361 public synchronized void commit() 362 throws RollbackException , HeuristicMixedException , HeuristicRollbackException , 363 SecurityException , IllegalStateException , SystemException 364 { 365 TransactionInfo txnInfo = getTransactionInfo(); 367 368 int status = getStatus(); 369 if (status == Status.STATUS_NO_TRANSACTION) 371 { 372 throw new IllegalStateException ("The transaction has not yet begun"); 373 } 374 else if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) 375 { 376 throw new RollbackException ("The transaction has already been rolled back"); 377 } 378 else if (status == Status.STATUS_MARKED_ROLLBACK) 379 { 380 throw new RollbackException ("The transaction has already been marked for rollback"); 381 } 382 else if (status == Status.STATUS_COMMITTING || status == Status.STATUS_COMMITTED) 383 { 384 throw new IllegalStateException ("The transaction has already been committed"); 385 } 386 else if (status != Status.STATUS_ACTIVE || txnInfo == null) 387 { 388 throw new IllegalStateException ("No user transaction is active"); 389 } 390 391 if (!finalized) 392 { 393 try 394 { 395 doCommitTransactionAfterReturning(txnInfo); 397 } 398 catch (Throwable e) 399 { 400 logger.error("Transaction didn't commit", e); 401 internalStatus = Status.STATUS_ROLLEDBACK; 403 throw new RollbackException ("Transaction didn't commit: " + e.getMessage()); 404 } 405 finally 406 { 407 doFinally(txnInfo); 409 finalized = true; 410 if (traceLogger.isDebugEnabled()) 412 { 413 traceDebugBeginTrace = null; 414 } 415 } 416 } 417 418 421 internalStatus = Status.STATUS_COMMITTED; 423 424 if (logger.isDebugEnabled()) 426 { 427 logger.debug("Committed user transaction: " + this); 428 } 429 } 430 431 public synchronized void rollback() 432 throws IllegalStateException , SecurityException , SystemException 433 { 434 TransactionInfo txnInfo = getTransactionInfo(); 436 437 int status = getStatus(); 438 if (status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK) 440 { 441 throw new IllegalStateException ("The transaction has already been rolled back"); 442 } 443 else if (status == Status.STATUS_COMMITTING || status == Status.STATUS_COMMITTED) 444 { 445 throw new IllegalStateException ("The transaction has already been committed"); 446 } 447 else if (txnInfo == null) 448 { 449 throw new IllegalStateException ("No user transaction is active"); 450 } 451 452 if (!finalized) 453 { 454 try 455 { 456 doCloseTransactionAfterThrowing(txnInfo, new Exception ()); 458 } 459 finally 460 { 461 doFinally(txnInfo); 463 finalized = true; 464 if (traceLogger.isDebugEnabled()) 466 { 467 traceDebugBeginTrace = null; 468 } 469 } 470 } 471 472 internalStatus = Status.STATUS_ROLLEDBACK; 474 475 if (logger.isDebugEnabled()) 477 { 478 logger.debug("Rolled back user transaction: " + this); 479 } 480 } 481 482 @Override 483 protected String methodIdentification(Method method) 484 { 485 return NAME; 487 } 488 489 @Override 490 protected void finalize() throws Throwable 491 { 492 if (traceLogger.isDebugEnabled() && traceDebugBeginTrace != null) 493 { 494 StringBuilder sb = new StringBuilder (1024); 495 StackTraceUtil.buildStackTrace( 496 "UserTransaction being garbage collected without a commit() or rollback().", 497 traceDebugBeginTrace, 498 sb, 499 -1); 500 traceLogger.error(sb); 501 } 502 } 503 } 504 | Popular Tags |