1 22 package org.jboss.ejb.plugins; 23 24 import org.jboss.invocation.Invocation; 25 import org.jboss.invocation.InvocationType; 26 import org.jboss.metadata.BeanMetaData; 27 import org.jboss.metadata.MetaData; 28 import org.jboss.metadata.XmlLoadable; 29 import org.jboss.tm.JBossTransactionRolledbackException; 30 import org.jboss.tm.JBossTransactionRolledbackLocalException; 31 import org.jboss.tm.TransactionTimeoutConfiguration; 32 import org.jboss.util.NestedException; 33 import org.jboss.util.deadlock.ApplicationDeadlockException; 34 import org.w3c.dom.Element ; 35 36 import javax.ejb.EJBException ; 37 import javax.ejb.TransactionRequiredLocalException ; 38 import javax.transaction.HeuristicMixedException ; 39 import javax.transaction.HeuristicRollbackException ; 40 import javax.transaction.RollbackException ; 41 import javax.transaction.Status ; 42 import javax.transaction.SystemException ; 43 import javax.transaction.Transaction ; 44 import javax.transaction.TransactionRequiredException ; 45 import javax.transaction.TransactionRolledbackException ; 46 import java.lang.reflect.Method ; 47 import java.rmi.RemoteException ; 48 import java.util.HashMap ; 49 import java.util.Map ; 50 import java.util.Random ; 51 import java.util.Iterator ; 52 import java.util.ArrayList ; 53 54 65 public class TxInterceptorCMT extends AbstractTxInterceptor implements XmlLoadable 66 { 67 68 70 71 public static int MAX_RETRIES = 5; 72 public static Random random = new Random (); 73 74 76 80 private boolean exceptionRollback = true; 81 82 private TxRetryExceptionHandler[] retryHandlers = null; 83 84 86 87 90 public static ApplicationDeadlockException isADE(Throwable t) 91 { 92 while (t!=null) 93 { 94 if (t instanceof ApplicationDeadlockException) 95 { 96 return (ApplicationDeadlockException)t; 97 } 98 else if (t instanceof RemoteException ) 99 { 100 t = ((RemoteException )t).detail; 101 } 102 else if (t instanceof EJBException ) 103 { 104 t = ((EJBException )t).getCausedByException(); 105 } 106 else 107 { 108 return null; 109 } 110 } 111 return null; 112 } 113 114 116 118 120 public void importXml(Element ielement) 121 { 122 try 123 { 124 Element element = MetaData.getOptionalChild(ielement, "retry-handlers"); 125 if (element == null) return; 126 ArrayList list = new ArrayList (); 127 Iterator handlers = MetaData.getChildrenByTagName(element, "handler"); 128 while (handlers.hasNext()) 129 { 130 Element handler = (Element )handlers.next(); 131 String className = MetaData.getElementContent(handler).trim(); 132 Class clazz = SecurityActions.getContextClassLoader().loadClass(className); 133 list.add(clazz.newInstance()); 134 } 135 retryHandlers = (TxRetryExceptionHandler[])list.toArray(new TxRetryExceptionHandler[list.size()]); 136 } 137 catch (Exception ex) 138 { 139 log.warn("Unable to importXml for the TxInterceptorCMT", ex); 140 } 141 } 142 143 145 public void create() throws Exception 146 { 147 super.create(); 148 BeanMetaData bmd = getContainer().getBeanMetaData(); 149 exceptionRollback = bmd.getExceptionRollback(); 150 if (exceptionRollback == false) 151 exceptionRollback = bmd.getApplicationMetaData().getExceptionRollback(); 152 } 153 154 public Object invokeHome(Invocation invocation) throws Exception 155 { 156 Transaction oldTransaction = invocation.getTransaction(); 157 for (int i = 0; i < MAX_RETRIES; i++) 158 { 159 try 160 { 161 return runWithTransactions(invocation); 162 } 163 catch (Exception ex) 164 { 165 checkRetryable(i, ex, oldTransaction); 166 } 167 } 168 throw new RuntimeException ("Unreachable"); 169 } 170 171 174 public Object invoke(Invocation invocation) throws Exception 175 { 176 Transaction oldTransaction = invocation.getTransaction(); 177 for (int i = 0; i < MAX_RETRIES; i++) 178 { 179 try 180 { 181 return runWithTransactions(invocation); 182 } 183 catch (Exception ex) 184 { 185 checkRetryable(i, ex, oldTransaction); 186 } 187 } 188 throw new RuntimeException ("Unreachable"); 189 } 190 191 private void checkRetryable(int i, Exception ex, Transaction oldTransaction) throws Exception 192 { 193 if (i + 1 >= MAX_RETRIES || oldTransaction != null) throw ex; 196 ApplicationDeadlockException deadlock = isADE(ex); 198 if (deadlock != null) 199 { 200 if (!deadlock.retryable()) throw deadlock; 201 log.debug(deadlock.getMessage() + " retrying tx " + (i + 1)); 202 } 203 else if (retryHandlers != null) 204 { 205 boolean retryable = false; 206 for (int j = 0; j < retryHandlers.length; j++) 207 { 208 retryable = retryHandlers[j].retry(ex); 209 if (retryable) break; 210 } 211 if (!retryable) throw ex; 212 log.debug(ex.getMessage() + " retrying tx " + (i + 1)); 213 } 214 else 215 { 216 throw ex; 217 } 218 Thread.sleep(random.nextInt(1 + i), random.nextInt(1000)); 219 } 220 221 223 private void printMethod(Method m, byte type) 224 { 225 String txName; 226 switch(type) 227 { 228 case MetaData.TX_MANDATORY: 229 txName = "TX_MANDATORY"; 230 break; 231 case MetaData.TX_NEVER: 232 txName = "TX_NEVER"; 233 break; 234 case MetaData.TX_NOT_SUPPORTED: 235 txName = "TX_NOT_SUPPORTED"; 236 break; 237 case MetaData.TX_REQUIRED: 238 txName = "TX_REQUIRED"; 239 break; 240 case MetaData.TX_REQUIRES_NEW: 241 txName = "TX_REQUIRES_NEW"; 242 break; 243 case MetaData.TX_SUPPORTS: 244 txName = "TX_SUPPORTS"; 245 break; 246 default: 247 txName = "TX_UNKNOWN"; 248 } 249 250 String methodName; 251 if(m != null) 252 methodName = m.getName(); 253 else 254 methodName ="<no method>"; 255 256 if (log.isTraceEnabled()) 257 { 258 if (m != null && (type == MetaData.TX_REQUIRED || type == MetaData.TX_REQUIRES_NEW)) 259 log.trace(txName + " for " + methodName + " timeout=" + container.getBeanMetaData().getTransactionTimeout(methodName)); 260 else 261 log.trace(txName + " for " + methodName); 262 } 263 } 264 265 281 private Object runWithTransactions(Invocation invocation) throws Exception 282 { 283 Transaction oldTransaction = invocation.getTransaction(); 285 Transaction newTransaction = null; 287 288 boolean trace = log.isTraceEnabled(); 289 if( trace ) 290 log.trace("Current transaction in MI is " + oldTransaction); 291 292 InvocationType type = invocation.getType(); 293 byte transType = container.getBeanMetaData().getTransactionMethod(invocation.getMethod(), type); 294 295 if ( trace ) 296 printMethod(invocation.getMethod(), transType); 297 298 Transaction threadTx = tm.suspend(); 303 if( trace ) 304 log.trace("Thread came in with tx " + threadTx); 305 try 306 { 307 switch (transType) 308 { 309 case MetaData.TX_NOT_SUPPORTED: 310 { 311 try 313 { 314 invocation.setTransaction(null); 315 return invokeNext(invocation, false); 316 } 317 finally 318 { 319 invocation.setTransaction(oldTransaction); 320 } 321 } 322 case MetaData.TX_REQUIRED: 323 { 324 int oldTimeout = 0; 325 Transaction theTransaction = oldTransaction; 326 if (oldTransaction == null) 327 { oldTimeout = startTransaction(invocation); 330 331 newTransaction = tm.getTransaction(); 333 if( trace ) 334 log.trace("Starting new tx " + newTransaction); 335 336 invocation.setTransaction(newTransaction); 338 theTransaction = newTransaction; 339 } 340 else 341 { 342 tm.resume(oldTransaction); 345 } 346 347 try 349 { 350 Object result = invokeNext(invocation, oldTransaction != null); 351 checkTransactionStatus(theTransaction, type); 352 return result; 353 } 354 finally 355 { 356 if( trace ) 357 log.trace("TxInterceptorCMT: In finally"); 358 359 if (newTransaction != null) 361 endTransaction(invocation, newTransaction, oldTransaction, oldTimeout); 362 else 363 tm.suspend(); 364 } 365 } 366 case MetaData.TX_SUPPORTS: 367 { 368 if (oldTransaction != null) 372 { 373 tm.resume(oldTransaction); 374 } 375 376 try 377 { 378 Object result = invokeNext(invocation, oldTransaction != null); 379 if (oldTransaction != null) 380 checkTransactionStatus(oldTransaction, type); 381 return result; 382 } 383 finally 384 { 385 tm.suspend(); 386 } 387 388 } 391 case MetaData.TX_REQUIRES_NEW: 392 { 393 int oldTimeout = startTransaction(invocation); 395 396 newTransaction = tm.getTransaction(); 398 399 invocation.setTransaction(newTransaction); 401 try 403 { 404 Object result = invokeNext(invocation, false); 405 checkTransactionStatus(newTransaction, type); 406 return result; 407 } 408 finally 409 { 410 endTransaction(invocation, newTransaction, oldTransaction, oldTimeout); 412 } 413 } 414 case MetaData.TX_MANDATORY: 415 { 416 if (oldTransaction == null) 417 { 418 if (type == InvocationType.LOCAL || 419 type == InvocationType.LOCALHOME) 420 { 421 throw new TransactionRequiredLocalException ( 422 "Transaction Required"); 423 } 424 else 425 { 426 throw new TransactionRequiredException ( 427 "Transaction Required"); 428 } 429 } 430 431 tm.resume(oldTransaction); 433 try 434 { 435 Object result = invokeNext(invocation, true); 436 checkTransactionStatus(oldTransaction, type); 437 return result; 438 } 439 finally 440 { 441 tm.suspend(); 442 } 443 } 444 case MetaData.TX_NEVER: 445 { 446 if (oldTransaction != null) 447 { 448 throw new EJBException ("Transaction not allowed"); 449 } 450 return invokeNext(invocation, false); 451 } 452 default: 453 log.error("Unknown TX attribute "+transType+" for method"+invocation.getMethod()); 454 } 455 } 456 finally 457 { 458 if (threadTx != null) 460 tm.resume(threadTx); 461 } 462 463 return null; 464 } 465 466 private int startTransaction(final Invocation invocation) throws Exception 467 { 468 int oldTimeout = -1; 470 if (tm instanceof TransactionTimeoutConfiguration) 471 { 472 oldTimeout = ((TransactionTimeoutConfiguration) tm).getTransactionTimeout(); 473 int newTimeout = container.getBeanMetaData().getTransactionTimeout(invocation.getMethod()); 474 tm.setTransactionTimeout(newTimeout); 475 } 476 tm.begin(); 477 return oldTimeout; 478 } 479 480 private void endTransaction(final Invocation invocation, final Transaction tx, final Transaction oldTx, final int oldTimeout) 481 throws TransactionRolledbackException, SystemException 482 { 483 Transaction current = tm.getTransaction(); 485 if ((tx == null && current != null) || tx.equals(current) == false) 486 throw new IllegalStateException ("Wrong transaction association: expected " + tx + " was " + current); 487 488 try 489 { 490 if (tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) 492 { 493 tx.rollback(); 494 } 495 else 496 { 497 tx.commit(); 502 } 503 } 504 catch (RollbackException e) 505 { 506 throwJBossException(e, invocation.getType()); 507 } 508 catch (HeuristicMixedException e) 509 { 510 throwJBossException(e, invocation.getType()); 511 } 512 catch (HeuristicRollbackException e) 513 { 514 throwJBossException(e, invocation.getType()); 515 } 516 catch (SystemException e) 517 { 518 throwJBossException(e, invocation.getType()); 519 } 520 finally 521 { 522 invocation.setTransaction(oldTx); 524 tm.suspend(); 533 if (oldTimeout != -1) 535 tm.setTransactionTimeout(oldTimeout); 536 } 537 } 538 539 540 542 548 protected void throwJBossException(Exception e, InvocationType type) 549 throws TransactionRolledbackException 550 { 551 if (e instanceof NestedException) 555 { 556 NestedException rollback = (NestedException) e; 557 if(rollback.getCause() instanceof Exception ) 558 { 559 e = (Exception ) rollback.getCause(); 560 } 561 } 562 if (type == InvocationType.LOCAL 563 || type == InvocationType.LOCALHOME) 564 { 565 throw new JBossTransactionRolledbackLocalException(e); 566 } 567 else 568 { 569 throw new JBossTransactionRolledbackException(e); 570 } 571 } 572 573 583 protected void checkTransactionStatus(Transaction tx, InvocationType type) 584 throws TransactionRolledbackException 585 { 586 if (exceptionRollback) 587 { 588 if (log.isTraceEnabled()) 589 log.trace("No exception from ejb, checking transaction status: " + tx); 590 int status = Status.STATUS_UNKNOWN; 591 try 592 { 593 status = tx.getStatus(); 594 } 595 catch (Throwable t) 596 { 597 log.debug("Ignored error trying to retrieve transaction status", t); 598 } 599 if (status != Status.STATUS_ACTIVE) 600 { 601 Exception e = new Exception ("Transaction cannot be committed (probably transaction timeout): " + tx); 602 throwJBossException(e, type); 603 } 604 } 605 } 606 607 609 public void sample(Object s) 611 { 612 } 614 public Map retrieveStatistic() 615 { 616 return null; 617 } 618 public void resetStatistic() 619 { 620 } 621 } 622 | Popular Tags |