1 45 46 47 package org.openejb.core.stateful; 48 49 import java.rmi.RemoteException ; 50 import java.util.Hashtable ; 51 import java.util.Properties ; 52 53 import javax.ejb.EJBException ; 54 import javax.ejb.EnterpriseBean ; 55 import javax.ejb.SessionBean ; 56 import javax.transaction.Status ; 57 import javax.transaction.Transaction ; 58 import javax.transaction.TransactionManager ; 59 60 import org.openejb.ApplicationException; 61 import org.openejb.InvalidateReferenceException; 62 import org.openejb.OpenEJB; 63 import org.openejb.OpenEJBException; 64 import org.openejb.SystemException; 65 import org.openejb.core.EnvProps; 66 import org.openejb.core.Operations; 67 import org.openejb.core.ThreadContext; 68 import org.openejb.core.ivm.IntraVmCopyMonitor; 69 import org.openejb.util.Logger; 70 import org.openejb.util.OpenEJBErrorHandler; 71 import org.openejb.util.SafeProperties; 72 import org.openejb.util.SafeToolkit; 73 74 80 public class StatefulInstanceManager { 81 82 86 protected long timeOUT = 0; 87 88 93 protected Hashtable beanINDEX = new Hashtable (); 94 95 101 protected BeanEntryQue lruQUE; 103 111 protected PassivationStrategy passivator; 112 113 116 protected int BULK_PASSIVATION_SIZE = 100; 117 118 protected SafeToolkit toolkit = SafeToolkit.getToolkit("StatefulInstanceManager"); 119 120 123 public StatefulInstanceManager( ) { 124 } 125 126 129 138 public void init(Properties props) 139 throws OpenEJBException{ 140 141 SafeProperties safeProps = toolkit.getSafeProperties(props); 142 143 String passivatorClass=null; 144 try { 145 passivatorClass = safeProps.getProperty(EnvProps.IM_PASSIVATOR); 146 } catch ( org.openejb.OpenEJBException e ) { 147 try { 149 passivatorClass = safeProps.getProperty("org/openejb/core/InstanceManager/PASSIVATOR"); 150 } catch ( org.openejb.OpenEJBException e1 ) { 151 throw e; 153 } 154 } 155 156 try { 157 passivator = (PassivationStrategy)toolkit.newInstance(passivatorClass); 158 } catch ( Exception e ) { 159 OpenEJBErrorHandler.propertyValueIsIllegal(EnvProps.IM_PASSIVATOR, passivatorClass, e.getLocalizedMessage()); 160 } 161 passivator.init(props); 162 163 int poolSize = safeProps.getPropertyAsInt(EnvProps.IM_POOL_SIZE, 100); 164 int timeOutInMinutes = safeProps.getPropertyAsInt(EnvProps.IM_TIME_OUT,5); 165 int bulkPassivationSize = safeProps.getPropertyAsInt(EnvProps.IM_PASSIVATE_SIZE,(int)(poolSize*.25)); 166 167 168 lruQUE = new BeanEntryQue(poolSize); 169 BULK_PASSIVATION_SIZE = (bulkPassivationSize > poolSize)? poolSize: bulkPassivationSize; 170 timeOUT = timeOutInMinutes*60*1000; } 172 173 190 public Object getAncillaryState(Object primaryKey) 191 throws OpenEJBException{ 192 return this.getBeanEntry(primaryKey).ancillaryState; 193 } 194 195 207 public void setAncillaryState(Object primaryKey, Object ancillaryState) 208 throws OpenEJBException{ 209 BeanEntry entry = getBeanEntry(primaryKey); 210 entry.ancillaryState = ancillaryState; 211 if ( ancillaryState instanceof javax.transaction.Transaction ) 212 entry.transaction = (javax.transaction.Transaction )ancillaryState; 213 214 } 215 216 225 public EnterpriseBean newInstance(Object primaryKey, Class beanClass) 226 throws OpenEJBException{ 227 return this.newInstance(primaryKey,null, beanClass); 228 } 229 230 241 public EnterpriseBean newInstance(Object primaryKey, Object ancillaryState, Class beanClass) 242 throws OpenEJBException{ 243 244 SessionBean bean = null; 245 246 try { 247 bean = (SessionBean )toolkit.newInstance(beanClass); 248 } catch ( OpenEJBException oee ) { 249 logger.error("Can't instantiate new instance of class +"+beanClass.getName()+". Received exception "+oee, oee); 250 throw (SystemException)oee; 251 } 252 253 ThreadContext thrdCntx = ThreadContext.getThreadContext(); 254 byte currentOp = thrdCntx.getCurrentOperation(); 255 thrdCntx.setCurrentOperation(Operations.OP_SET_CONTEXT); 256 try { 257 bean.setSessionContext((javax.ejb.SessionContext )thrdCntx.getDeploymentInfo().getEJBContext()); 258 } catch ( Throwable callbackException ) { 259 267 handleCallbackException(callbackException, bean, thrdCntx, "setSessionContext"); 268 } finally { 269 thrdCntx.setCurrentOperation(currentOp); 270 } 271 272 273 BeanEntry entry = new BeanEntry(bean,primaryKey, ancillaryState, timeOUT); 274 275 beanINDEX.put(primaryKey, entry); 276 277 278 return entry.bean; 279 } 280 281 292 public SessionBean obtainInstance(Object primaryKey, ThreadContext callContext)throws OpenEJBException{ 293 if ( primaryKey == null ) { 294 throw new org.openejb.SystemException( new NullPointerException ("Cannot obtain an instance of the stateful session bean with a null session id")); 295 } 296 297 BeanEntry entry = (BeanEntry)beanINDEX.get(primaryKey); 298 if ( entry == null ) { 299 entry = activate(primaryKey); 301 if ( entry != null ) { 302 if ( entry.isTimedOut() ) { 304 309 throw new org.openejb.InvalidateReferenceException(new java.rmi.NoSuchObjectException ("Timed Out")); 310 } 311 byte currentOp = callContext.getCurrentOperation(); 313 callContext.setCurrentOperation(Operations.OP_ACTIVATE); 314 315 try { 316 entry.bean.ejbActivate( ); 317 } catch ( Throwable callbackException ) { 318 325 handleCallbackException(callbackException, entry.bean, callContext, "ejbActivate"); 326 } finally { 327 callContext.setCurrentOperation(currentOp); 328 } 329 330 beanINDEX.put(primaryKey, entry); 331 return entry.bean; 332 } else { 333 throw new org.openejb.InvalidateReferenceException(new java.rmi.NoSuchObjectException ("Not Found")); 334 } 335 } else { if ( entry.transaction != null ) { try { 340 if ( entry.transaction.getStatus() == Status.STATUS_ACTIVE ) { 341 return entry.bean; 347 } else { 350 entry.transaction = null; 353 return entry.bean; 354 } 355 } catch ( javax.transaction.SystemException se ) { 356 throw new org.openejb.SystemException(se); 357 } catch ( IllegalStateException ise ) { 358 throw new org.openejb.SystemException(ise); 359 } catch ( java.lang.SecurityException lse ) { 360 throw new org.openejb.SystemException(lse); 361 } 362 } else { BeanEntry queEntry = lruQUE.remove(entry); if ( queEntry != null ) { 366 if ( entry.isTimedOut() ) { 367 entry = (BeanEntry)beanINDEX.remove(entry.primaryKey ); handleTimeout(entry, callContext); 370 throw new org.openejb.InvalidateReferenceException(new java.rmi.NoSuchObjectException ("Stateful SessionBean has timed-out")); 371 } 372 return entry.bean; 373 } else { 374 byte currentOperation = callContext.getCurrentOperation(); 375 if ( currentOperation == Operations.OP_AFTER_COMPLETION || currentOperation == Operations.OP_BEFORE_COMPLETION ) { 376 return entry.bean; 377 } else { 378 throw new ApplicationException(new RemoteException ("Concurrent calls not allowed")); 380 } 381 } 382 } 383 } 384 } 385 386 protected void handleTimeout(BeanEntry entry, ThreadContext thrdCntx) { 387 388 byte currentOp = thrdCntx.getCurrentOperation(); 390 thrdCntx.setCurrentOperation(Operations.OP_REMOVE); 391 392 try { 395 entry.bean.ejbRemove(); 396 } catch ( Throwable callbackException ) { 397 403 String logMessage = "An unexpected exception occured while invoking the ejbRemove method on the timed-out Stateful SessionBean instance; "+callbackException.getClass().getName()+" "+callbackException.getMessage(); 404 405 406 logger.error( logMessage ); 407 408 } finally { 409 logger.info("Removing the timed-out stateful session bean instance "+entry.primaryKey ); 410 thrdCntx.setCurrentOperation(currentOp); 411 } 412 } 413 422 public void poolInstance(Object primaryKey, EnterpriseBean bean) 423 throws OpenEJBException{ 424 if ( primaryKey == null || bean == null ) 425 throw new SystemException("Invalid arguments"); 426 427 BeanEntry entry = (BeanEntry)beanINDEX.get(primaryKey); 428 if ( entry == null ) { 429 entry = activate(primaryKey); 430 if ( entry == null ) { 431 throw new SystemException("Invalid primaryKey:"+primaryKey); 432 } 433 } else if ( entry.bean != bean ) 434 throw new SystemException("Invalid ID for bean"); 435 436 437 if ( entry.transaction!=null && entry.transaction == entry.ancillaryState ) { 438 return; } else { 442 try { 443 entry.transaction = OpenEJB.getTransactionManager().getTransaction(); 444 } catch ( javax.transaction.SystemException se ) { 445 throw new org.openejb.SystemException("TransactionManager failure"); 446 } 447 448 if ( entry.transaction == null ) { lruQUE.add(entry); } 451 } 452 } 453 454 463 public EnterpriseBean freeInstance(Object primaryKey) 464 throws org.openejb.SystemException{ 465 BeanEntry entry = null; 466 entry = (BeanEntry)beanINDEX.remove(primaryKey); if ( entry == null ) { 468 entry = activate(primaryKey); 469 } else { 470 lruQUE.remove(entry); } 472 473 if ( entry == null ) 474 return null; 475 476 return entry.bean; 478 } 479 480 485 protected void passivate( ) throws SystemException { 486 final ThreadContext thrdCntx = ThreadContext.getThreadContext(); 487 Hashtable stateTable = new Hashtable (BULK_PASSIVATION_SIZE); 488 489 BeanEntry currentEntry; 490 final byte currentOp = thrdCntx.getCurrentOperation(); 491 try { 492 for ( int i=0; i<BULK_PASSIVATION_SIZE; ++i ) { 493 currentEntry=lruQUE.first(); 494 if ( currentEntry==null ) 495 break; 496 beanINDEX.remove(currentEntry.primaryKey); 497 if ( currentEntry.isTimedOut() ) { 498 handleTimeout(currentEntry, thrdCntx); 499 } else { 500 thrdCntx.setCurrentOperation(Operations.OP_PASSIVATE); 501 try { 502 currentEntry.bean.ejbPassivate(); 504 } catch ( Throwable e ) { 505 String logMessage = "An unexpected exception occured while invoking the ejbPassivate method on the Stateful SessionBean instance; "+e.getClass().getName()+" "+e.getMessage(); 508 509 510 logger.error( logMessage ); 511 } 512 stateTable.put(currentEntry.primaryKey, currentEntry); 513 } 514 } 515 } finally { 516 thrdCntx.setCurrentOperation(currentOp); 517 } 518 519 525 try { 526 IntraVmCopyMonitor.prePassivationOperation(); 527 passivator.passivate(stateTable); 529 } finally { 530 IntraVmCopyMonitor.postPassivationOperation(); 532 } 533 } 534 protected BeanEntry activate(Object primaryKey) throws SystemException { 535 return(BeanEntry)passivator.activate(primaryKey); 536 } 537 538 545 protected org.openejb.InvalidateReferenceException destroy(BeanEntry entry, Exception t) 546 throws org.openejb.SystemException{ 547 548 beanINDEX.remove(entry.primaryKey); lruQUE.remove(entry); if ( entry.transaction != null ) { 551 try { 552 entry.transaction.setRollbackOnly(); 553 } catch ( javax.transaction.SystemException se ) { 554 throw new org.openejb.SystemException(se); 555 } catch ( IllegalStateException ise ) { 556 throw new org.openejb.SystemException("Attempt to rollback a non-tx context",ise); 557 } catch ( java.lang.SecurityException lse ) { 558 throw new org.openejb.SystemException("Container not authorized to rollback tx",lse); 559 } 560 return new org.openejb.InvalidateReferenceException(new javax.transaction.TransactionRolledbackException (t.getMessage())); 561 } else if ( t instanceof RemoteException ) 562 return new org.openejb.InvalidateReferenceException(t); 563 else { 564 EJBException ejbE = (EJBException )t; 565 return new org.openejb.InvalidateReferenceException(new RemoteException (ejbE.getMessage(), ejbE.getCausedByException())); 566 } 567 568 569 571 } 572 579 protected BeanEntry getBeanEntry(Object primaryKey) 580 throws OpenEJBException{ 581 if ( primaryKey == null ) { 582 throw new SystemException(new NullPointerException ("The primary key is null. Cannot get the bean entry")); 583 } 584 BeanEntry entry = (BeanEntry)beanINDEX.get(primaryKey); 585 if ( entry == null ) { 586 EnterpriseBean bean = this.obtainInstance(primaryKey, ThreadContext.getThreadContext()); 587 this.poolInstance(primaryKey,bean); 588 entry = (BeanEntry)beanINDEX.get(primaryKey); 589 } 590 return entry; 591 } 592 593 594 597 class BeanEntryQue { 598 private final java.util.LinkedList list; 599 private final int capacity; 600 601 protected BeanEntryQue(int preferedCapacity) { 602 capacity = preferedCapacity; 603 list = new java.util.LinkedList (); 604 } 605 606 protected synchronized BeanEntry first( ) { 607 return(BeanEntry)list.removeFirst(); 608 } 609 610 protected synchronized void add(BeanEntry entry) throws org.openejb.SystemException { 611 if ( list.size() >= capacity ) { passivate(); 613 } 614 entry.resetTimeOut(); 615 list.addLast(entry); 617 entry.inQue = true; 618 } 619 protected synchronized BeanEntry remove(BeanEntry entry) { 620 if ( !entry.inQue ) 621 return null; 622 if ( list.remove(entry)==true ) { 623 entry.inQue = false; 624 return entry; 625 } else { 626 return null; 628 } 629 } 630 } 631 632 public Logger logger = Logger.getInstance( "OpenEJB", "org.openejb.util.resources" ); 633 634 635 protected void handleCallbackException(Throwable e, EnterpriseBean instance, ThreadContext callContext, String callBack) throws ApplicationException, org.openejb.SystemException{ 636 637 String remoteMessage = "An unexpected exception occured while invoking the "+callBack+" method on the Stateful SessionBean instance"; 639 String logMessage = remoteMessage +"; "+e.getClass().getName()+" "+e.getMessage(); 640 641 642 logger.error( logMessage ); 643 644 645 Transaction tx = null; 646 try { 647 tx = getTxMngr().getTransaction(); 648 } catch ( Throwable t ) { 649 logger.error("Could not retreive the current transaction from the transaction manager while handling a callback exception from the "+callBack+" method of bean "+callContext.getPrimaryKey()); 650 } 651 if ( tx != null ) markTxRollbackOnly( tx ); 652 653 654 655 freeInstance( callContext.getPrimaryKey() ); 656 657 658 if ( tx == null ) { 659 throw new InvalidateReferenceException( new RemoteException ( remoteMessage, e ) ); 660 } else { 661 throw new InvalidateReferenceException( new javax.transaction.TransactionRolledbackException ( logMessage ) ); 662 } 663 664 } 665 666 667 protected void markTxRollbackOnly( Transaction tx ) throws SystemException{ 668 try { 669 if ( tx != null ) tx.setRollbackOnly(); 670 } catch ( javax.transaction.SystemException se ) { 671 throw new org.openejb.SystemException(se); 672 } 673 } 674 675 protected TransactionManager getTxMngr( ) { 676 return OpenEJB.getTransactionManager(); 677 } 678 679 } 680 681 | Popular Tags |