KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > ejb > containers > EntityContainer


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.ejb.containers;
24
25 import java.lang.reflect.Method JavaDoc;
26 import java.io.*;
27 import java.rmi.RemoteException JavaDoc;
28
29 import java.util.*;
30 import java.util.logging.*;
31 import java.lang.reflect.Proxy JavaDoc;
32 import java.lang.reflect.InvocationHandler JavaDoc;
33
34 import javax.ejb.*;
35 import javax.transaction.*;
36
37 import com.sun.appserv.util.cache.Constants;
38 import com.sun.appserv.util.cache.Cache;
39 import com.sun.appserv.util.cache.BaseCache;
40 import com.sun.appserv.util.cache.LruCache;
41 import com.sun.appserv.util.cache.CacheListener;
42
43 import com.sun.ejb.*;
44 import com.sun.ejb.portable.ObjrefEnumeration;
45 import com.sun.ejb.containers.util.pool.*;
46 import com.sun.ejb.containers.util.cache.EJBObjectCache;
47 import com.sun.ejb.containers.util.cache.EJBObjectCacheListener;
48 import com.sun.ejb.containers.util.cache.FIFOEJBObjectCache;
49 import com.sun.ejb.containers.util.cache.UnboundedEJBObjectCache;
50 import com.sun.ejb.containers.util.ContainerWorkPool;
51
52 import com.sun.enterprise.*;
53 import com.sun.enterprise.deployment.*;
54 import com.sun.enterprise.deployment.runtime.IASEjbExtraDescriptors;
55 import com.sun.enterprise.deployment.runtime.BeanCacheDescriptor;
56 import com.sun.enterprise.deployment.runtime.BeanPoolDescriptor;
57 import com.sun.enterprise.util.LocalStringManagerImpl;
58 import com.sun.enterprise.log.Log;
59 import com.sun.enterprise.appverification.factory.AppVerification;
60 import com.sun.enterprise.admin.monitor.callflow.ComponentType;
61
62 import com.sun.logging.*;
63
64 import com.sun.enterprise.admin.monitor.*;
65 import com.sun.enterprise.config.ConfigException;
66 import com.sun.enterprise.config.serverbeans.*;
67 import com.sun.enterprise.server.ServerContext;
68 import com.sun.enterprise.server.ApplicationServer;
69 import com.sun.enterprise.util.io.FileUtils;
70
71 import com.sun.ejb.spi.stats.EntityBeanStatsProvider;
72
73 import com.sun.ejb.spi.container.BeanStateSynchronization;
74
75 import com.sun.enterprise.distributedtx.J2EETransaction;
76
77 /**
78  * This class implements the Container interface for EntityBeans.
79  * It is responsible for instance & lifecycle management for BMP & CMP
80  * EntityBeans.
81  * The EntityContainer implements option B of the commit-time options
82  * described in the EJB2.0 spec section 10.5.9
83  * It also implements optimistic concurrency (i.e. multiple non-exclusive
84  * bean instances per primary key) when there are multiple concurrent
85  * transactions on a EntityBean.
86  * <p>
87  * The following sequence of actions happens for the EntityContainer,
88  * for each EJB lifecycle stage (note: getEJBObject, getContext,
89  * releaseContext, preInvokeTx, postInvokeTx are called from BaseContainer).
90  * 1. EJB Creation
91  * homeImpl.create, container.getContext,
92  * container.preInvokeTx, ejb.ejbCreate, container.postCreate,
93  * ejb.ejbPostCreate, container.postInvokeTx, container.releaseContext
94  * 2. EJB Finding
95  * homeImpl.find---, container.getContext, container.preInvokeTx,
96  * ejb.ejbFind---, container.postFind, container.postInvokeTx,
97  * container.releaseContext
98  * 3. EJB Invocation
99  * container.getEJBObject, ejbObject.someMethod, container.getContext,
100  * container.preInvokeTx, ejb.someMethod, container.postInvokeTx,
101  * container.releaseContext
102  * <P>
103  * State Management: The EntityContainer manages collections of EJBs
104  * in different states.
105  * The 5 states of an EntityBean (an EJB can be in only 1 state at a time):
106  * <UL>
107  * <LI> 1. POOLED : does not have identity. EJBs in the POOLED state are
108  * all identical, hence are maintained in a java.util.Vector,
109  * whose size is maintained below a HIGH_WATER_MARK (currently 100).
110  * <LI> 2. READY : ready for invocations, no transaction in progress.
111  * EJBs in the READY state are associated with a primary key.
112  * To enhance reuse of EJB instances, only one READY
113  * EJB per primary key is stored. READY EJBs are managed by the
114  * ejbstore/EntityStore class. READY EJBs are looked up using a key consisting
115  * of the primary key and a null transaction context.
116  * <LI> 3. INVOKING : processing an invocation.
117  * EJBs in the INVOKING state are not stored anywhere. Before transitioning
118  * from READY or INCOMPLETE_TX to INVOKING, the EJB is removed from the
119  * EntityStore.
120  * <LI> 4. INCOMPLETE_TX : ready for invocations, transaction in progress.
121  * EJBs in the INCOMPLETE_TX state are associated with a primary key.
122  * INCOMPLETE_TX EJBs are managed by the ejbstore/EntityStore class.
123  * INCOMPLETE_TX EJBs are looked up using a composite key consisting
124  * of the primary key and the transaction context.
125  * <LI> 5. DESTROYED : does not exist.
126  * </UL>
127  * All READY bean instances are stored in the readyStore.
128  * All INCOMPLETE_TX bean instances are stored in the ActiveTxCache.
129  * Beans in the READY state are stored with key = ejbObject.
130  * Beans in the INCOMPLETE_TX state are stored with key = ejbObject+Tx.
131  * Instances in INVOKING state which have transactions associated
132  * with them are also in ActiveTxCache.
133  * All POOLED instances are stored in the pooledEJBs vector.
134  *
135  * Note on locking order: if both ready/ActiveTxCache and context are to be
136  * locked, always acquire the context lock first, then the Store lock.
137  * Note on locking order: if both ready/ActiveTxCache and ejbObject need
138  * locks, always acquire the ejbObject lock first, then the Store lock.
139  *
140  * @author Mahesh Kannan
141  * @author Shanker N
142  * @author Pramod Gopinath
143  */

144
145 public class EntityContainer
146     extends BaseContainer
147     implements CacheListener, EntityBeanStatsProvider
148 {
149     protected static Logger _logger;
150     static {
151         _logger=LogDomains.getLogger(LogDomains.EJB_LOGGER);
152         
153     }
154     
155     private ThreadLocal JavaDoc ejbServant = new ThreadLocal JavaDoc() {
156         protected Object JavaDoc initialValue() {
157             return null;
158         }
159     };
160     protected static LocalStringManagerImpl localStrings =
161     new LocalStringManagerImpl(EntityContainer.class);
162     
163     static final int POOLED=1, READY=2, INVOKING=3,
164     INCOMPLETE_TX=4, DESTROYED=5;
165     protected static final int HIGH_WATER_MARK=100;
166     
167     private static final int DEFAULT_TX_CACHE_BUCKETS = 16;
168
169     // table of EJBObjects, indexed by primary key.
170
// Note: Hashtable methods are synchronized.
171
protected EJBObjectCache ejbObjectStore;
172     
173     // table of EJBLocalObjectImpls, indexed by primary key.
174
// Note: Hashtable methods are synchronized.
175
protected EJBObjectCache ejbLocalObjectStore;
176     
177     //protected LIFOChannel channel = null;
178
protected Stack passivationCandidates = new Stack();
179     
180     // table of EJBs (Contexts) in READY state, key is primary key
181
protected Cache readyStore;
182     
183     //Pool of free EntityContexts
184
protected AbstractPool entityCtxPool;
185     
186     protected boolean isReentrant;
187     protected boolean isContainerManagedPers;
188     protected Hashtable txBeanTable;
189     
190     protected final float DEFAULT_LOAD_FACTOR = 0.75f;
191     protected final int DEFAULT_CACHE_SIZE = 8192;
192     protected int _maxBuckets = 8;
193     
194     protected IASEjbExtraDescriptors iased = null;
195     protected BeanCacheDescriptor beanCacheDes = null;
196     protected BeanPoolDescriptor beanPoolDes = null;
197     protected Server svr = null;
198     protected Config cfg = null;
199     protected EjbContainer ejbContainer = null;
200     boolean largeCache = false;
201     
202     CacheProperties cacheProp = null;
203     PoolProperties poolProp = null;
204     Object JavaDoc asyncTaskSemaphore = new Object JavaDoc();
205     boolean addedASyncTask = false;
206     
207     // a timer task to trim the beans idle in readyStore
208
protected IdleBeansPassivator idleEJBObjectPassivator;
209     protected IdleBeansPassivator idleLocalEJBObjectPassivator;
210     protected boolean defaultCacheEJBO = true;
211     
212     IdleBeansPassivator idleBeansPassivator;
213     boolean timerValid = true;
214     long idleTimeout;
215     
216     
217     protected int ejboRemoved;
218
219     protected int totalPassivations;
220     protected int totalPassivationErrors;
221
222     private EntityCacheStatsProvider cacheStatsProvider;
223
224     static {
225         _logger.log(Level.FINE," Loading Entitycontainer...");
226     }
227     
228     /**
229      * This constructor is called from the JarManager when a Jar is deployed.
230      * @exception Exception on error
231      */

232     protected EntityContainer(EjbDescriptor desc, ClassLoader JavaDoc loader)
233         throws Exception JavaDoc
234     {
235         super(desc, loader);
236         EjbEntityDescriptor ed = (EjbEntityDescriptor)desc;
237         isReentrant = ed.isReentrant();
238         if ( ed.getPersistenceType().equals(
239             EjbEntityDescriptor.BEAN_PERSISTENCE) ) {
240             isContainerManagedPers = false;
241         } else {
242             isContainerManagedPers = true;
243         }
244         
245         iased = ed.getIASEjbExtraDescriptors();
246         if( iased != null) {
247             beanCacheDes = iased.getBeanCache();
248             beanPoolDes = iased.getBeanPool();
249         }
250         try {
251             ServerContext sc = ApplicationServer.getServerContext();
252            //ROB: config changes
253
//svr = ServerBeansFactory.getServerBean(sc.getConfigContext());
254
cfg = ServerBeansFactory.getConfigBean(sc.getConfigContext());
255         } catch (ConfigException ex) {
256             _logger.log(Level.WARNING, "ejb.entitycontainer_exception", ex);
257         }
258         //ROB: config changes
259
//ejbContainer = svr.getEjbContainer();
260
ejbContainer = cfg.getEjbContainer();
261
262         super.setMonitorOn(ejbContainer.isMonitoringEnabled());
263         
264         createCaches();
265         
266         super.createCallFlowAgent(
267                 isContainerManagedPers ? ComponentType.CMP : ComponentType.BMP);
268         _logger.log(Level.FINE,"[EntityContainer] Created EntityContainer: "
269                 + logParams[0]);
270     }
271     
272     
273     /**
274      * setup a timer task to trim timed out entries in the cache.
275      * @param cache cache which is used to setup the timer task
276      * @return the passivator object
277      */

278     public IdleBeansPassivator setupIdleBeansPassivator(Cache cache)
279         throws Exception JavaDoc {
280         
281         IdleBeansPassivator idleBeansPassivator =
282             new IdleBeansPassivator(cache);
283
284         ContainerFactoryImpl.getTimer().
285             scheduleAtFixedRate(idleBeansPassivator, idleTimeout, idleTimeout);
286         
287         return idleBeansPassivator;
288     }
289     
290     /**
291      * cancel a timer task to trim timed out entries in the cache.
292      */

293     public void cancelTimerTasks() {
294         timerValid = false;
295         if (idleBeansPassivator != null) {
296             try {
297                 idleBeansPassivator.cancel();
298                 idleBeansPassivator.cache = null;
299             } catch (Exception JavaDoc e) {
300                 _logger.log(Level.FINE, "[EntityContainer] cancelTimerTask: " +
301                     e);
302             }
303         }
304         
305         if (idleEJBObjectPassivator != null) {
306             try {
307                 idleEJBObjectPassivator.cancel();
308                 idleEJBObjectPassivator.cache = null;
309             } catch (Exception JavaDoc e) {
310                 _logger.log(Level.FINE, "[EntityContainer] cancelTimerTask: " +
311                     e);
312             }
313         }
314         
315         if (idleLocalEJBObjectPassivator != null) {
316             try {
317                 idleLocalEJBObjectPassivator.cancel();
318                 idleLocalEJBObjectPassivator.cache = null;
319             } catch (Exception JavaDoc e) {
320                 _logger.log(Level.FINE, "[EntityContainer] cancelTimerTask: " +
321                     e);
322             }
323         }
324         
325         this.idleEJBObjectPassivator = null;
326         this.idleLocalEJBObjectPassivator = null;
327         this.idleBeansPassivator = null;
328     }
329     
330     void setTxBeanTable(Hashtable t) {
331         txBeanTable = t;
332     }
333     
334     /**
335      * Called from the ContainerFactory during initialization.
336      */

337     protected void initializeHome()
338         throws Exception JavaDoc
339     {
340         ObjectFactory entityCtxFactory = new EntityContextFactory(this);
341         
342         int steadyPoolSize = 0;
343         int resizeQuantity = 10;
344         int idleTimeoutInSeconds = Integer.MAX_VALUE-1;
345         poolProp = new PoolProperties();
346         
347         super.initializeHome();
348
349         entityCtxPool = new NonBlockingPool(ejbDescriptor.getName(),
350             entityCtxFactory, poolProp.steadyPoolSize,
351             poolProp.poolResizeQuantity, poolProp.maxPoolSize,
352             poolProp.poolIdleTimeoutInSeconds, loader);
353
354
355     registerMonitorableComponents();
356     }
357
358     protected void registerMonitorableComponents() {
359     registryMediator.registerProvider(this);
360     registryMediator.registerProvider(entityCtxPool);
361     if (readyStore != null) {
362         int confMaxCacheSize = cacheProp.maxCacheSize;
363         if (confMaxCacheSize <= 0) {
364         confMaxCacheSize = Integer.MAX_VALUE;
365         }
366         this.cacheStatsProvider = new EntityCacheStatsProvider(
367             (BaseCache) readyStore, confMaxCacheSize);
368         registryMediator.registerProvider(cacheStatsProvider);
369     }
370         super.registerMonitorableComponents();
371     super.populateMethodMonitorMap();
372         _logger.log(Level.FINE, "[Entity Container] registered monitorable");
373     }
374     
375     public void onReady() {
376     }
377     
378     public String JavaDoc getMonitorAttributeValues() {
379         StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc();
380     appendStats(sbuf);
381     return sbuf.toString();
382     }
383
384     public void appendStats(StringBuffer JavaDoc sbuf) {
385     sbuf.append("\nEntityContainer: ")
386         .append("CreateCount=").append(statCreateCount).append("; ")
387         .append("RemoveCount=").append(statRemoveCount).append("; ")
388         .append("PassQSize=")
389         .append(passivationCandidates.size()).append("]");
390         Map stats = null;
391         if (readyStore != null) {
392             stats = readyStore.getStats();
393         }
394         appendStat(sbuf, "ReadyStore", stats);
395         
396         appendStat(sbuf, "EJBObjectStore", ejbObjectStore.getStats());
397         appendStat(sbuf, "EJBLocalObjectStore",ejbLocalObjectStore.getStats());
398     }
399
400     /****************************/
401     //Methods of EntityBeanStatsProvider
402

403     public int getMaxCacheSize() {
404     int maxSize = 0;
405     if (readyStore != null) {
406         maxSize = (cacheProp.maxCacheSize <= 0)
407         ? Integer.MAX_VALUE
408         : cacheProp.maxCacheSize;
409     }
410
411     return maxSize;
412     }
413
414     public int getSteadyPoolSize() {
415     return entityCtxPool.getSteadyPoolSize();
416     }
417
418     public int getMaxPoolSize() {
419     return entityCtxPool.getMaxPoolSize();
420     }
421
422     public long getPooledCount() {
423     return entityCtxPool.getSize();
424     }
425
426     public long getReadyCount() {
427     return (readyStore == null)
428         ? 0
429         : readyStore.getEntryCount();
430     }
431
432     /****************************/
433     
434     private int getEjbObjectStoreSize() {
435         return ejbObjectStore.getEntryCount();
436     }
437     
438     /**
439      * Implementation of BaseContainer method. This is never called.
440      */

441     EJBObjectImpl createEJBObjectImpl()
442         throws CreateException, RemoteException JavaDoc
443     {
444         throw new EJBException(
445             "INTERNAL ERROR: EntityContainer.createEJBObject() called");
446     }
447     
448     EJBLocalObjectImpl createEJBLocalObjectImpl()
449         throws CreateException
450     {
451         throw new EJBException(
452           "INTERNAL ERROR: EntityContainer.createEJBLocalObjectImpl() called");
453     }
454     
455     
456     /**
457      * Called only from ContainerFactory when a remote invocation
458      * arrives for an EJB.
459      */

460     EJBObjectImpl getEJBObjectImpl(byte[] streamKey) {
461         // First get the primary key of the EJB
462
Object JavaDoc primaryKey;
463         try {
464             primaryKey = EJBUtils.deserializeObject(streamKey, loader, false);
465         } catch ( Exception JavaDoc ex ) {
466             throw new EJBException(ex);
467         }
468         
469         return internalGetEJBObjectImpl(primaryKey, streamKey);
470     }
471     
472     
473     /**
474      * Called from EJBLocalObjectImpl.getLocalObject() while deserializing
475      * a local object reference.
476      */

477     EJBLocalObjectImpl getEJBLocalObjectImpl(Object JavaDoc key) {
478         return internalGetEJBLocalObjectImpl(key);
479     }
480     
481     /**
482      * Called from BaseContainer.preInvoke which is called from the EJBObject
483      * for local and remote invocations, and from the EJBHome for create/find.
484      */

485     protected ComponentContext _getContext(Invocation inv) {
486         String JavaDoc name = inv.method.getName();
487         
488         if ( inv.invocationInfo.isCreateHomeFinder ) {
489             // create*, find*, home methods
490
// Note: even though CMP finders dont need an instance,
491
// we still return a pooled instance, so that the Tx demarcation
492
// in BaseContainer.pre+postInvoke can work.
493

494             // get any pooled EJB
495
EntityContextImpl context = getPooledEJB();
496             
497             // we're sure that no concurrent thread can be using this
498
// context, so no need to synchronize.
499
context.setState(INVOKING);
500             
501             if ( inv.invocationInfo.startsWithCreate )
502                 preCreate(inv, context);
503             else if ( inv.invocationInfo.startsWithFind )
504                 preFind(inv, context);
505
506
507             context.setLastTransactionStatus(-1);
508             context.incrementCalls();
509             
510             return context;
511         }
512         
513         // If we came here, it means this is a business method
514
// and there is an EJBObject/LocalObject.
515

516         // If we would invoke the EJB with the client's Tx,
517
// try to get an EJB with that incomplete Tx.
518
EntityContextImpl context = null;
519         if ( willInvokeWithClientTx(inv) )
520             context = getEJBWithIncompleteTx(inv);
521         if ( context == null )
522             context = getReadyEJB(inv);
523         
524         synchronized ( context ) {
525             if ( context.getState() == INVOKING && !isReentrant )
526                 throw new EJBException(
527                     "EJB is already executing another request");
528             if (context.getState() == POOLED ||
529                 context.getState() == DESTROYED) {
530                 // somehow a concurrent thread must have changed state.
531
// this is an internal error.
532
throw new EJBException("Internal error: unknown EJB state");
533             }
534             
535             context.setState(INVOKING);
536         }
537         
538         context.setLastTransactionStatus(-1);
539         context.incrementCalls();
540         // A business method may modify the bean's state
541
context.setDirty(true);
542         
543         return context;
544     }
545     
546     protected boolean willInvokeWithClientTx(Invocation inv) {
547         int status = Status.STATUS_UNKNOWN;
548         try {
549             status = transactionManager.getStatus();
550         } catch ( SystemException ex ) {
551             throw new EJBException(ex);
552         }
553         if ( status != Status.STATUS_NO_TRANSACTION ) {
554             int txAttr = inv.invocationInfo.txAttr;
555             switch (txAttr) {
556                 case TX_SUPPORTS:
557                 case TX_REQUIRED:
558                 case TX_MANDATORY:
559                     return true;
560             }
561         }
562         return false;
563     }
564     
565     
566     
567     /**
568      * This is called from BaseContainer.postInvoke after
569      * EntityContainer.preInvokeTx has been called.
570      */

571     public void releaseContext(Invocation inv) {
572         EntityContextImpl context = (EntityContextImpl)inv.context;
573         boolean decrementedCalls = false; // End of IAS 4661771
574

575         if ( context.getState()==DESTROYED )
576             return;
577         
578         try {
579             if ( context.hasReentrantCall() ) {
580                 // For biz->biz or postCreate->biz, the bean instance will
581
// remain in the incomplete-tx table.
582
if ( inv.ejbObject.isRemoved() ) {
583                     // biz -> remove case (biz method invoked reentrant remove)
584
// Remove from IncompleteTx table, to prevent further
585
// reentrant calls.
586
removeIncompleteTxEJB(context, true);
587                     
588                     // disconnect context from EJB(Local)Object so that
589
// context.getEJBObject() will throw exception.
590
if ( context.getEJBObjectImpl() != null ) {
591                         // reset flag in case EJBObject is used again
592
context.getEJBObjectImpl().setRemoved(false);
593                         context.setEJBObjectImpl(null);
594                         context.setEJBStub(null);
595                     }
596                     if ( context.getEJBLocalObjectImpl() != null ) {
597                         // reset flag in case EJBLocalObject is used again
598
context.getEJBLocalObjectImpl().setRemoved(false);
599                         context.setEJBLocalObjectImpl(null);
600                     }
601                 } else {
602                     if ( context.getState() == INVOKING ) {
603                         doFlush( inv );
604                     }
605                 }
606                 
607                 // Note: at this point context.getState() is INVOKING.
608
} else if ( context.getEJBObjectImpl()==null
609                 && context.getEJBLocalObjectImpl()==null ) {
610                 // This can only happen if the method was ejbFind
611
// OR if the method was ejbCreate which threw an application
612
// exception (so postCreate was not called)
613
// OR after a biz method which called a reentrant remove.
614
// So bean instance goes back into pool.
615
// We dont care if any Tx has completed or not.
616
//context.setTransaction(null);
617
decrementedCalls = true;
618                 context.decrementCalls();
619                 if (!(inv.invocationInfo.startsWithCreate)) {
620                     context.setTransaction(null);
621                     addPooledEJB(context);
622                 }else if(context.getTransaction() == null) {
623                     addPooledEJB(context);
624                 } else {
625                     // Set the state to incomplete as the transaction
626
// is not done still and afterCompletion will
627
// handle stuff
628
context.setState(INCOMPLETE_TX);
629                 }
630             } else if ( inv.ejbObject.isRemoved() ) {
631                 // EJBObject/LocalObject was removed, so bean instance
632
// goes back into pool.
633
// We dont care if any Tx has completed or not.
634
removeIncompleteTxEJB(context, true);
635                 // unset the removed flag, in case the EJB(Local)Object
636
// ref is held by the client and is used again
637
if ( context.getEJBObjectImpl() != null )
638                     context.getEJBObjectImpl().setRemoved(false);
639                 if ( context.getEJBLocalObjectImpl() != null )
640                     context.getEJBLocalObjectImpl().setRemoved(false);
641                 
642                 decrementedCalls = true;
643                 context.decrementCalls();
644                 if(context.getTransaction() == null) {
645                     addPooledEJB(context);
646                 } else {
647                     // Set the state to incomplete as the transaction
648
// is not done still and afterCompletion will
649
// handle stuff
650
context.setState(INCOMPLETE_TX);
651                 }
652                 
653             } else if ( context.getTransaction() == null ) {
654                 // biz methods and ejbCreate
655
// Either the EJB was called with no tx,
656
// or it was called with a tx which finished,
657
// so afterCompletion was already called.
658

659                 // If no tx or tx committed, then move the EJB to READY state
660
// else pool the bean
661
int status = context.getLastTransactionStatus();
662                 decrementedCalls = true;
663                 context.decrementCalls();
664                 context.setLastTransactionStatus(-1);
665                 if ( status == -1 || status == Status.STATUS_COMMITTED
666                 || status == Status.STATUS_NO_TRANSACTION )
667                     addReadyEJB(context);
668                 else
669                     passivateAndPoolEJB(context);
670             } else {
671                 // biz methods and ejbCreate
672
// The EJB is still associated with a Tx.
673
// It will already be in the INCOMPLETE_TX table.
674
context.setState(INCOMPLETE_TX);
675
676                 doFlush( inv );
677             }
678         } catch ( Exception JavaDoc ex ) {
679             _logger.log(Level.FINE, "ejb.release_context_exception",
680                         logParams);
681             _logger.log(Level.FINE, "",ex);
682             throw new EJBException(ex);
683         } finally {
684             if (decrementedCalls == false) {
685                 context.decrementCalls();
686             }
687             context.touch();
688         }
689     }
690     
691     
692     /**
693      * Called from getContext before the ejb.ejbCreate is called
694      */

695     protected void preCreate(Invocation inv, EntityContextImpl context) {
696     statCreateCount++;
697     }
698     
699     
700     /**
701      * This is called from the generated "HelloEJBHomeImpl" create* method,
702      * after ejb.ejbCreate() has been called and before ejb.ejbPostCreate()
703      * is called.
704      * Note: postCreate will not be called if ejbCreate throws an exception
705      */

706     public void postCreate(Invocation inv, Object JavaDoc primaryKey)
707         throws CreateException
708     {
709         if ( primaryKey == null )
710             throw new EJBException(
711                 "Null primary key returned by ejbCreate method");
712         
713         EntityContextImpl context = (EntityContextImpl)inv.context;
714         EJBObjectImpl ejbObjImpl = null;
715         EJBLocalObjectImpl localObjImpl = null;
716         
717         if ( (isRemote) && (!inv.isLocal) ) {
718             // remote invocation: create EJBObject
719
ejbObjImpl = internalGetEJBObjectImpl(primaryKey, null, true);
720             
721             // associate the context with the ejbObject
722
context.setEJBObjectImpl(ejbObjImpl);
723             context.setEJBStub((EJBObject)ejbObjImpl.getStub());
724         }
725         
726         if ( isLocal ) {
727             // create EJBLocalObject irrespective of local/remote invocation
728
// this is necessary to make EntityContext.getPrimaryKey and
729
// EntityContext.getEJBObject work.
730
localObjImpl = internalGetEJBLocalObjectImpl(primaryKey, true);
731             
732             // associate the context with the ejbLocalObject
733
context.setEJBLocalObjectImpl(localObjImpl);
734         }
735         
736         if ( inv.isLocal )
737             inv.ejbObject = localObjImpl;
738         else
739             inv.ejbObject = ejbObjImpl;
740         
741         if ( context.getTransaction() != null ) {
742             // Add EJB to INCOMPLETE_TX table so that concurrent/loopback
743
// invocations will be correctly handled
744
addIncompleteTxEJB(context);
745         }
746         
747         context.setDirty(true); // ejbPostCreate could modify state
748
}
749     
750     //Called from EJB(Local)HomeInvocationHandler
751
//Note: preFind is already called from getContext
752
protected Object JavaDoc invokeFindByPrimaryKey(Method JavaDoc method,
753         Invocation inv, Object JavaDoc[] args)
754     throws Throwable JavaDoc
755     {
756     Object JavaDoc pKeys = super.invokeTargetBeanMethod(method,
757         inv, inv.ejb, args, null);
758     return postFind(inv, pKeys, null);
759     }
760    
761     /**
762      * Called from getContext before the ejb.ejbFind* is called
763      */

764     protected void preFind(Invocation inv, EntityContextImpl context) {
765         // if the finder is being invoked with the client's transaction,
766
// call ejbStore on all dirty bean instances associated with that
767
// transaction. This ensures that the finder results will include
768
// all updates done previously in the client's tx.
769
if ( willInvokeWithClientTx(inv) &&
770         !inv.method.getName().equals("findByPrimaryKey") ) {
771             Transaction tx = null;
772             try {
773                 tx = transactionManager.getTransaction();
774             } catch ( SystemException ex ) {
775                 throw new EJBException(ex);
776             }
777             
778             storeAllBeansInTx( tx );
779         }
780         
781     }
782     
783     /**
784      * Called from CMP PersistentManager
785      */

786     public void preSelect()
787       throws javax.ejb.EJBException JavaDoc {
788     // if the ejbSelect is being invoked with the client's transaction,
789
// call ejbStore on all dirty bean instances associated with that
790
// transaction. This ensures that the select results will include
791
// all updates done previously in the client's tx.
792
_logger.fine(" inside preSelect...");
793     Transaction tx = null;
794     try {
795         _logger.fine("PRESELECT : getting transaction...");
796         tx = transactionManager.getTransaction();
797     } catch ( SystemException ex ) {
798         throw new EJBException(ex);
799     }
800     _logger.fine("PRESELECT : calling storeAllBeansInTx()...");
801     storeAllBeansInTx( tx );
802     }
803
804     /**
805      * Convert a collection of primary keys to a collection of EJBObjects.
806      * (special case: single primary key).
807      * Note: the order of input & output collections must be maintained.
808      * Null values are preserved in both the single primary key return
809      * and collection-valued return cases.
810      *
811      * This is called from the generated "HelloEJBHomeImpl" find* method,
812      * after ejb.ejbFind**() has been called.
813      * Note: postFind will not be called if ejbFindXXX throws an exception
814      */

815     public Object JavaDoc postFind(Invocation inv, Object JavaDoc primaryKeys,
816         Object JavaDoc[] findParams)
817         throws FinderException
818     {
819                 
820         if ( primaryKeys instanceof Enumeration ) {
821             // create Enumeration of objrefs from Enumeration of primaryKeys
822
Enumeration e = (Enumeration)primaryKeys;
823             // this is a portable Serializable Enumeration
824
ObjrefEnumeration objrefs = new ObjrefEnumeration();
825             while ( e.hasMoreElements() ) {
826                 Object JavaDoc primaryKey = e.nextElement();
827                 Object JavaDoc ref;
828                 if( primaryKey != null ) {
829                     if ( inv.isLocal )
830                         ref = getEJBLocalObjectForPrimaryKey(primaryKey);
831                     else
832                         ref = getEJBObjectStub(primaryKey, null);
833                     objrefs.add(ref);
834                 } else {
835                     objrefs.add(null);
836                 }
837             }
838             return objrefs;
839         } else if ( primaryKeys instanceof Collection ) {
840             // create Collection of objrefs from Collection of primaryKeys
841
Collection c = (Collection)primaryKeys;
842             Iterator it = c.iterator();
843             ArrayList objrefs = new ArrayList(); // a Serializable Collection
844
while ( it.hasNext() ) {
845                 Object JavaDoc primaryKey = it.next();
846                 Object JavaDoc ref;
847                 if( primaryKey != null ) {
848                     if ( inv.isLocal )
849                         ref = getEJBLocalObjectForPrimaryKey(primaryKey);
850                     else
851                         ref = getEJBObjectStub(primaryKey, null);
852                     objrefs.add(ref);
853                 } else {
854                     objrefs.add(null);
855                 }
856             }
857             return objrefs;
858         } else {
859             if( primaryKeys != null ) {
860                 if ( inv.isLocal )
861                     return getEJBLocalObjectForPrimaryKey(primaryKeys);
862                 else
863                     return getEJBObjectStub(primaryKeys, null);
864             } else {
865                 return null;
866             }
867         }
868     }
869     
870     /**
871      * Called only from the Persistence Manager for EJB2.0 CMP EntityBeans.
872      * This is a private API between the PM and Container because there
873      * is no standard API defined in EJB2.0 for the PM to get an EJBObject
874      * for a primary key (home.findByPrimaryKey cant be used because it may
875      * not run in the same tx).
876      */

877     public EJBObject getEJBObjectForPrimaryKey(Object JavaDoc pkey) {
878         // create stub without creating EJBObject
879
return getEJBObjectStub(pkey, null);
880     }
881
882     /**
883      * Called only from the Persistence Manager for EJB2.0 CMP EntityBeans.
884      * Called only during cascade delete......
885      * This is a private API between the PM and Container because there
886      * is no standard API defined in EJB2.0 for the PM to get an EJBLocalObject
887      * for a primary key (findByPrimaryKey cant be used because it may
888      * not run in the same tx).
889      *
890      * Example 1:
891      * A cascadeDeletes B and B calls getA() (expected return value: null)
892      *
893      * In the above case, getA() eventualy calls getEJBLocalObjectForPrimaryKey(PK_of_A, Ctx_of_B)
894      * We first check if B is in the process of being cascade deleted by checking the
895      * cascadeDeleteBeforeEJBRemove flag. If this flag is true, only then we bother to check if
896      * the Context associated with the PK_of_A in this transaction is marked for cascade delete
897      * which can be figured out by checking isCascadeDeleteAfterSuperEJBRemove() in A's context.
898      * If A is marked for cascade delete then we return null else the EJBLocalObject associated
899      * with A.
900      *
901      * Example 2:
902      * C cascadeDeletes B and B calls getA() (expected return value: EJBLocalObject for PK_of_A)
903      *
904      * In the above case, getA() eventualy calls getEJBLocalObjectForPrimaryKey(PK_of_A, Ctx_of_B)
905      * We first check if B is in the process of being cascade deleted by checking the
906      * cascadeDeleteBeforeEJBRemove flag. This flag will be true, and hence we check if
907      * the Context associated with the PK_of_A in this transaction is marked for cascade delete
908      * which can be figured out by checking isCascadeDeleteAfterSuperEJBRemove() in A's context.
909      * In this case this flag will be false and hcen we return the ejbLocalObject
910      * Example 2:
911      * B is *NOT* cascade deleted and B calls getA() (expected return value: EJBLocalObject for PK_of_A)
912      *
913      * In the above case, getA() eventualy calls getEJBLocalObjectForPrimaryKey(PK_of_A, Ctx_of_B)
914      * We first check if B is in the process of being cascade deleted by checking the
915      * cascadeDeleteBeforeEJBRemove flag. This flag will be FALSE, and hence we do not make
916      * any further check and return the EJBLocalObject associated with A
917      *
918      * @param pkey The primary key for which the EJBLocalObject is required
919      * @param ctx The context associated with the bean from which the accessor method is invoked
920      * @return The EJBLocalObject associated with the PK or null if it is cascade deleted.
921      *
922      */

923     public EJBLocalObject getEJBLocalObjectForPrimaryKey
924         (Object JavaDoc pkey, EJBContext ctx) {
925
926         EntityContextImpl context = (EntityContextImpl) ctx;
927         EJBLocalObjectImpl ejbLocalObjectImpl =
928             internalGetEJBLocalObjectImpl(pkey);
929
930         if (context.isCascadeDeleteBeforeEJBRemove()) {
931             J2EETransaction current = null;
932             try {
933                 current = (J2EETransaction) transactionManager.getTransaction();
934             } catch ( SystemException ex ) {
935                 throw new EJBException(ex);
936             }
937         ActiveTxCache activeTxCache = (current == null) ? null :
938         (ActiveTxCache) (current.getActiveTxCache());
939             if (activeTxCache != null) {
940         EntityContextImpl ctx2 = (EntityContextImpl)
941             activeTxCache.get(this, pkey);
942         if ((ctx2 != null) &&
943             (ctx2.isCascadeDeleteAfterSuperEJBRemove())) {
944             return null;
945         }
946         }
947         return (EJBLocalObject) ejbLocalObjectImpl.getClientObject();
948         }
949
950         return (EJBLocalObject) ejbLocalObjectImpl.getClientObject();
951     }
952
953     /**
954      * Called only from the Persistence Manager for EJB2.0 CMP EntityBeans.
955      * This is a private API between the PM and Container because there
956      * is no standard API defined in EJB2.0 for the PM to get an EJBLocalObject
957      * for a primary key (findByPrimaryKey cant be used because it may
958      * not run in the same tx).
959      */

960     public EJBLocalObject getEJBLocalObjectForPrimaryKey(Object JavaDoc pkey) {
961         EJBLocalObjectImpl localObjectImpl =
962             internalGetEJBLocalObjectImpl(pkey);
963     return (localObjectImpl != null) ?
964             (EJBLocalObject) localObjectImpl.getClientObject() : null;
965     }
966     
967     // Called from EJBHomeImpl.remove(primaryKey),
968
// EJBLocalHomeImpl.remove(primaryKey)
969
protected void removeBean(Object JavaDoc primaryKey, Method JavaDoc removeMethod,
970         boolean local)
971         throws RemoveException, EJBException, RemoteException JavaDoc
972     {
973         EJBLocalRemoteObject ejbo;
974         if ( local ) {
975             ejbo = internalGetEJBLocalObjectImpl(primaryKey, false, true);
976         }
977         else { // may be remote-only bean
978
ejbo = internalGetEJBObjectImpl(primaryKey, null, false, true);
979         }
980         removeBean(ejbo, removeMethod, local);
981     }
982     
983     // Called from EJBObjectImpl.remove, EJBLocalObjectImpl.remove,
984
// and removeBean above.
985
protected void removeBean(EJBLocalRemoteObject ejbo, Method JavaDoc removeMethod,
986             boolean local)
987         throws RemoveException, EJBException, RemoteException JavaDoc
988     {
989         Invocation i = new Invocation();
990         i.ejbObject = ejbo;
991         i.isLocal = local;
992         i.method = removeMethod;
993         
994         // Method must be a remove method defined on one of :
995
// javax.ejb.EJBHome, javax.ejb.EJBObject, javax.ejb.EJBLocalHome,
996
// javax.ejb.EJBLocalObject
997
Class JavaDoc declaringClass = removeMethod.getDeclaringClass();
998         i.isHome = ( (declaringClass == javax.ejb.EJBHome JavaDoc.class) ||
999                      (declaringClass == javax.ejb.EJBLocalHome JavaDoc.class) );
1000
1001        try {
1002            preInvoke(i);
1003            removeBean(i);
1004        } catch(Exception JavaDoc e) {
1005            _logger.log(Level.SEVERE,"ejb.preinvoke_exception",logParams);
1006            _logger.log(Level.SEVERE,"",e);
1007            i.exception = e;
1008        } finally {
1009            postInvoke(i);
1010        }
1011        
1012        if(i.exception != null) {
1013            if(i.exception instanceof RemoveException) {
1014                throw (RemoveException)i.exception;
1015            }
1016            else if(i.exception instanceof RuntimeException JavaDoc) {
1017                throw (RuntimeException JavaDoc)i.exception;
1018            }
1019            else if(i.exception instanceof Exception JavaDoc) {
1020                throw new EJBException((Exception JavaDoc)i.exception);
1021            }
1022            else {
1023                EJBException ejbEx = new EJBException();
1024                ejbEx.initCause(i.exception);
1025                throw ejbEx;
1026            }
1027        }
1028    }
1029    
1030    
1031    /**
1032     * container.preInvoke() must already be done.
1033     * So this will be called with the proper Tx context.
1034     * @exception RemoveException if an error occurs while removing the bean
1035     */

1036    protected void removeBean(Invocation inv)
1037        throws RemoveException
1038    {
1039        try {
1040        statRemoveCount++;
1041            // Note: if there are concurrent invocations/transactions in
1042
// progress for this ejbObject, they will be serialized along with
1043
// this remove by the database. So we optimistically do ejbRemove.
1044

1045            // call ejbRemove on the EJB
1046
// the EJB is allowed to veto the remove by throwing RemoveException
1047
EntityBean ejb = (EntityBean)inv.ejb;
1048            EntityContextImpl context = (EntityContextImpl)inv.context;
1049            callEJBRemove(ejb, context);
1050            
1051            // inv.ejbObject could be a EJBObject or a EJBLocalObject
1052
Object JavaDoc primaryKey = inv.ejbObject.getKey();
1053            if ( isRemote ) {
1054                removeEJBObjectFromStore(primaryKey);
1055                
1056                // Mark EJB as removed. Now releaseContext will add bean to pool
1057
if ( context.getEJBObjectImpl() != null ) {
1058                    context.getEJBObjectImpl().setRemoved(true);
1059                }
1060            }
1061            
1062            if ( isLocal ) {
1063                // Remove the EJBLocalObject from ejbLocalObjectStore
1064
ejbLocalObjectStore.remove(primaryKey);
1065                // Mark EJB as removed. Now releaseContext will add bean to pool
1066
if ( context.getEJBLocalObjectImpl() != null ) {
1067                    context.getEJBLocalObjectImpl().setRemoved(true);
1068                }
1069            }
1070            
1071            // Remove any timers for this entity bean identity.
1072
EJBTimerService ejbTimerService =
1073                containerFactory.getEJBTimerService();
1074            if( (ejbTimerService != null) && isTimedObject() ) {
1075                ejbTimerService.cancelEntityBeanTimers(getContainerId(),
1076                                                       primaryKey);
1077            }
1078
1079        } catch ( RemoveException ex ) {
1080            if(_logger.isLoggable(Level.FINE)) {
1081                _logger.log(Level.FINE,"ejb.local_remove_exception",logParams);
1082                _logger.log(Level.FINE,"",ex);
1083            }
1084            throw ex;
1085        }
1086        catch ( Exception JavaDoc ex ) {
1087            if(_logger.isLoggable(Level.FINE)) {
1088                _logger.log(Level.FINE,"ejb.remove_bean_exception",logParams);
1089                _logger.log(Level.FINE,"",ex);
1090            }
1091            throw new EJBException(ex);
1092        }
1093    }
1094    
1095    private void removeEJBObjectFromStore(Object JavaDoc primaryKey) {
1096        removeEJBObjectFromStore(primaryKey, true);
1097    }
1098    
1099    private void removeEJBObjectFromStore(Object JavaDoc primaryKey, boolean decrementRefCount) {
1100        // Remove the EJBObject from ejbObjectStore so future lookups
1101
// in internalGetEJBObject will not get it.
1102
EJBObjectImpl ejbObjImpl =
1103            (EJBObjectImpl)ejbObjectStore.remove(primaryKey, decrementRefCount);
1104                                                 
1105        if ( ejbObjImpl != null ) {
1106            synchronized ( ejbObjImpl ) {
1107                // disconnect the EJBObject from the ProtocolManager
1108
// so that no remote invocations can reach the EJBObject
1109
remoteHomeRefFactory.destroyReference(ejbObjImpl.getStub(),
1110                                                  ejbObjImpl.getEJBObject());
1111            }
1112        }
1113    }
1114    
1115    /**
1116     * Remove a bean. Used by the PersistenceManager.
1117     * This is needed because the PM's remove must bypass tx/security checks.
1118     */

1119    public void removeBeanUnchecked(EJBLocalObject localObj) {
1120        // First convert client EJBLocalObject to EJBLocalObjectImpl
1121
EJBLocalObjectImpl localObjectImpl =
1122            EJBLocalObjectImpl.toEJBLocalObjectImpl(localObj);
1123        internalRemoveBeanUnchecked(localObjectImpl, true);
1124    }
1125    
1126    
1127    /**
1128     * Remove a bean. Used by the PersistenceManager.
1129     * This is needed because the PM's remove must bypass tx/security checks.
1130     */

1131    public void removeBeanUnchecked(Object JavaDoc primaryKey) {
1132        EJBLocalRemoteObject ejbo;
1133        if ( isLocal ) {
1134            ejbo = internalGetEJBLocalObjectImpl(primaryKey);
1135            internalRemoveBeanUnchecked(ejbo, true);
1136        }
1137        else { // remote-only bean
1138
ejbo = internalGetEJBObjectImpl(primaryKey, null);
1139            internalRemoveBeanUnchecked(ejbo, false);
1140        }
1141    }
1142    
1143    /**
1144     * Remove a bean. Used by the PersistenceManager.
1145     * This is needed because the PM's remove must bypass tx/security checks.
1146     */

1147    private void internalRemoveBeanUnchecked(
1148    EJBLocalRemoteObject localRemoteObj, boolean local) {
1149        Invocation inv = new Invocation();
1150        inv.ejbObject = localRemoteObj;
1151        inv.isLocal = local;
1152        Method JavaDoc method=null;
1153        try {
1154            method = EJBLocalObject.class.getMethod("remove", NO_PARAMS);
1155        } catch ( NoSuchMethodException JavaDoc e ) {
1156            _logger.log(Level.FINE,
1157                "Exception in internalRemoveBeanUnchecked()", e);
1158        }
1159        inv.method = method;
1160        
1161        inv.invocationInfo = (InvocationInfo) invocationInfoMap.get(method);
1162        
1163        try {
1164            // First get a bean instance on which ejbRemove can be invoked.
1165
// This code must be in sync with getContext().
1166
// Can't call getContext() directly because it does stuff
1167
// based on remove's txAttr.
1168
// Assume there is a tx on the current thread.
1169
EntityContextImpl context = getEJBWithIncompleteTx(inv);
1170            if ( context == null ) {
1171                context = getReadyEJB(inv);
1172            }
1173            
1174            synchronized ( context ) {
1175                if ( context.getState() == INVOKING && !isReentrant ) {
1176                    throw new EJBException(
1177                        "EJB is already executing another request");
1178                }
1179                if (context.getState() == POOLED ||
1180                    context.getState() == DESTROYED) {
1181                    // somehow a concurrent thread must have changed state.
1182
// this is an internal error.
1183
throw new EJBException("Internal error: unknown EJB state");
1184                }
1185                
1186                context.setState(INVOKING);
1187            }
1188            inv.context = context;
1189            context.setLastTransactionStatus(-1);
1190            context.incrementCalls();
1191            
1192            inv.instance = inv.ejb = context.getEJB();
1193            inv.container = this;
1194            invocationManager.preInvoke(inv);
1195            
1196            // call ejbLoad if necessary
1197
useClientTx(context.getTransaction(), inv);
1198            
1199            try {
1200                context.setCascadeDeleteBeforeEJBRemove(true);
1201                removeBean(inv);
1202            } catch ( Exception JavaDoc ex ) {
1203                _logger.log(Level.FINE,
1204                    "Exception in internalRemoveBeanUnchecked()", ex);
1205                // if system exception mark the tx for rollback
1206
inv.exception = checkExceptionClientTx(context, ex);
1207            }
1208            if ( inv.exception != null ) {
1209                throw inv.exception;
1210            }
1211        }
1212        catch ( RuntimeException JavaDoc ex ) {
1213            throw ex;
1214        }
1215        catch ( Exception JavaDoc ex ) {
1216            throw new EJBException(ex);
1217        }
1218        catch ( Throwable JavaDoc ex ) {
1219            EJBException ejbEx = new EJBException();
1220            ejbEx.initCause(ex);
1221            throw ejbEx;
1222        }
1223        finally {
1224            invocationManager.postInvoke(inv);
1225            releaseContext(inv);
1226        }
1227    }
1228    
1229    /**
1230     * Discard the bean instance. The bean's persistent state is not removed.
1231     * This is usually called when the bean instance throws a system exception,
1232     * from BaseContainer.postInvokeTx, getReadyEJB,
1233     * afterBegin, beforeCompletion, passivateEJB.
1234     */

1235    void forceDestroyBean(EJBContextImpl ctx) {
1236        // Something bad happened (such as a RuntimeException),
1237
// so kill the bean and let it be GC'ed
1238
// Note: EJB2.0 section 18.3.1 says that discarding an EJB
1239
// means that no methods other than finalize() should be invoked on it.
1240

1241        if ( ctx.getState() == DESTROYED ) {
1242            entityCtxPool.destroyObject(null);
1243            return;
1244        }
1245        
1246        EntityContextImpl context = (EntityContextImpl)ctx;
1247        EntityBean ejb = (EntityBean)context.getEJB();
1248        // Start of IAS 4661771
1249
synchronized ( context ) {
1250            try {
1251                Object JavaDoc primaryKey = context.getPrimaryKey();
1252                if ( primaryKey != null ) {
1253                    if ( context.getTransaction() != null ) {
1254                        Transaction txCurrent = context.getTransaction();
1255            ActiveTxCache activeTxCache = (ActiveTxCache)
1256                (((J2EETransaction) txCurrent).getActiveTxCache());
1257                        if (activeTxCache != null) {
1258                // remove the context from the store
1259
activeTxCache.remove(this, primaryKey);
1260            }
1261                    }
1262                    
1263                    // remove the context from readyStore as well
1264
removeContextFromReadyStore(primaryKey, context);
1265                    
1266                    if (context.getEJBObjectImpl() != null) {
1267                        removeEJBObjectFromStore(primaryKey);
1268                    }
1269                    if (context.getEJBLocalObjectImpl() != null) {
1270                        ejbLocalObjectStore.remove(primaryKey);
1271                    }
1272                    
1273                }
1274            } catch ( Exception JavaDoc ex ) {
1275                _logger.log(Level.FINE, "Exception in forceDestroyBean()", ex);
1276            } finally {
1277                try {
1278                    //Very importatnt to set the state as destroyed otherwise
1279
// the pool.destroy might wrongly call unsetEntityContext
1280
context.setState(DESTROYED);
1281                    entityCtxPool.destroyObject(context);
1282                } catch (Exception JavaDoc ex) {
1283                    _logger.log(Level.FINE, "Exception in forceDestroyBean()",
1284                        ex);
1285                }
1286            }
1287        }
1288        // End of IAS 4661771
1289
}
1290    
1291    
1292    // Called before invoking a bean with no Tx or with a new Tx.
1293
// Check if the bean is associated with an unfinished tx.
1294
protected void checkUnfinishedTx(Transaction prevTx, Invocation inv) {
1295                                     
1296        try {
1297            if ( (prevTx != null) &&
1298                 prevTx.getStatus() != Status.STATUS_NO_TRANSACTION ) {
1299                // An unfinished tx exists for the bean.
1300
// so we cannot invoke the bean with no Tx or a new Tx.
1301
throw new IllegalStateException JavaDoc(
1302                  "Bean is associated with a different unfinished transaction");
1303            }
1304        } catch (SystemException ex) {
1305            throw new EJBException(ex);
1306        }
1307    }
1308    
1309    
1310    /**
1311     * Check if the given EJBObject/LocalObject has been removed.
1312     * Called before executing non-business methods of EJBLocalObject.
1313     * @exception NoSuchObjectLocalException if the object has been removed.
1314     */

1315    void checkExists(EJBLocalRemoteObject ejbObj) {
1316        // Need to call ejbLoad to see if persistent state is removed.
1317
// However, the non-business methods dont have a transaction attribute.
1318
// So do nothing for now.
1319
}
1320    
1321    // Called from BaseContainer.SyncImpl
1322
void afterBegin(EJBContextImpl ctx) {
1323        // Note: EntityBeans are not allowed to be TX_BEAN_MANAGED
1324
if ( ctx.getState() == DESTROYED )
1325            return;
1326        
1327        EntityContextImpl context = (EntityContextImpl)ctx;
1328        
1329        if ( context.getEJBObjectImpl() != null
1330             || context.getEJBLocalObjectImpl() != null ) {
1331            // ejbLoad needed only for business methods and removes
1332

1333            // Add EJB to INCOMPLETE_TX table so that concurrent/loopback
1334
// invocations will be correctly handled
1335
if ( context.getTransaction() != null ) {
1336                addIncompleteTxEJB(context);
1337            }
1338            
1339            // need to call ejbLoad since there can be more than
1340
// one active EJB instance per primaryKey. (Option B in 9.11.5).
1341
EntityBean e = (EntityBean)context.getEJB();
1342            try {
1343                callEJBLoad(e, context);
1344            } catch ( NoSuchEntityException ex ) {
1345                _logger.log(Level.FINE, "Exception in afterBegin()", ex);
1346                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
1347
forceDestroyBean(context);
1348                
1349                throw new NoSuchObjectLocalException(
1350         "NoSuchEntityException thrown by ejbLoad, EJB instance discarded", ex);
1351            } catch ( Exception JavaDoc ex ) {
1352                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
1353
forceDestroyBean(context);
1354                
1355                throw new EJBException(ex);
1356            }
1357            
1358            context.setNewlyActivated(false);
1359        }
1360    }
1361    
1362    // Called from BaseContainer.SyncImpl.beforeCompletion, postInvokeNoTx
1363
void beforeCompletion(EJBContextImpl ctx) {
1364        if ( ctx.getState() == DESTROYED ) {
1365            return;
1366        }
1367        
1368        EntityContextImpl context = (EntityContextImpl)ctx;
1369        EJBLocalRemoteObject ejbObjImpl = context.getEJBObjectImpl();
1370        EJBLocalRemoteObject ejbLocalObjImpl = context.getEJBLocalObjectImpl();
1371        
1372        // Call ejbStore as required by diagram in EJB2.0 section 10.9.4
1373
// home methods, finders and remove dont need ejbStore
1374
if ( ((ejbObjImpl != null) && !ejbObjImpl.isRemoved())
1375             || ((ejbLocalObjImpl != null) && !ejbLocalObjImpl.isRemoved()) ) {
1376            if ( context.isDirty() ) {
1377                enlistResourcesAndStore(context);
1378            }
1379        }
1380    }
1381    
1382    
1383    // Called from beforeCompletion and preFind
1384
private void enlistResourcesAndStore(EntityContextImpl context) {
1385        EntityBean e = (EntityBean)context.getEJB();
1386        // NOTE : Use invocation instead of ComponentInvocation since
1387
// the context is available. It is needed in case ejbStore/ejbLoad
1388
// makes use of EJB timer service in order to perform operations allowed
1389
// checks
1390
Invocation inv = new Invocation(e, this);
1391        inv.context = context;
1392        invocationManager.preInvoke(inv);
1393        
1394        try {
1395            transactionManager.enlistComponentResources();
1396            
1397            callEJBStore(e, context);
1398            
1399        } catch ( NoSuchEntityException ex ) {
1400            // Error during ejbStore, so discard bean: EJB2.0 18.3.3
1401
forceDestroyBean(context);
1402            
1403            throw new NoSuchObjectLocalException(
1404        "NoSuchEntityException thrown by ejbStore, EJB instance discarded", ex);
1405        } catch ( Exception JavaDoc ex ) {
1406            // Error during ejbStore, so discard bean: EJB2.0 18.3.3
1407
forceDestroyBean(context);
1408            throw new EJBException(ex);
1409        } finally {
1410            invocationManager.postInvoke(inv);
1411        }
1412    }
1413    
1414    
1415    // Called from BaseContainer.SyncImpl.afterCompletion
1416
// at the end of a transaction.
1417
// Note: this can be called possibly asynchronously because
1418
// of transaction timeout
1419
// Note: this can be called before releaseContext (if container
1420
// completed the tx in BaseContainer.postInvokeTx), or it can
1421
// be called after releaseContext (if client completed the tx after
1422
// getting reply from bean). So whatever is done here *MUST* be
1423
// consistent with releaseContext, and the bean should end up in
1424
// the correct state.
1425
void afterCompletion(EJBContextImpl ctx, int status) {
1426        if ( ctx.getState() == DESTROYED ) {
1427            return;
1428        }
1429
1430        if (super.isUndeployed()) {
1431        transactionManager.ejbDestroyed(ctx);
1432        return;
1433    }
1434
1435        EntityContextImpl context = (EntityContextImpl)ctx;
1436        EJBLocalRemoteObject ejbObjImpl = context.getEJBObjectImpl();
1437        EJBLocalRemoteObject ejbLocalObjImpl = context.getEJBLocalObjectImpl();
1438
1439        // home methods, finders and remove dont need this
1440
if ( ((ejbObjImpl != null) && !ejbObjImpl.isRemoved())
1441             || ((ejbLocalObjImpl != null) && !ejbLocalObjImpl.isRemoved()) ) {
1442            // Remove bean from ActiveTxCache table if its there.
1443
// No need to remove it from txBeanTable because the table
1444
// gets updated in ContainerFactoryImpl.removeContainerSync.
1445

1446        //removeIncompleteTxEJB(context, false);
1447

1448            context.setTransaction(null);
1449            context.setLastTransactionStatus(status);
1450
1451            context.setCascadeDeleteAfterSuperEJBRemove(false);
1452            context.setCascadeDeleteBeforeEJBRemove(false);
1453            
1454            // Move context to ready state if tx commited, else to pooled state
1455
if ( context.getState() != INVOKING ) {
1456                if ( (status == Status.STATUS_COMMITTED)
1457                     || (status == Status.STATUS_NO_TRANSACTION) ) {
1458                    addReadyEJB(context);
1459                } else {
1460                    passivateAndPoolEJB(context);
1461                }
1462            }
1463        } else if ((ejbObjImpl == null) && (ejbLocalObjImpl == null)) {
1464            // This happens if an ejbcreate has an exception, in that case
1465
// we remove bean from ActiveTxCache table if its there.
1466
// and return it to the pool
1467
//removeIncompleteTxEJB(context, false);
1468

1469            context.setTransaction(null);
1470            context.setLastTransactionStatus(status);
1471
1472            context.setCascadeDeleteAfterSuperEJBRemove(false);
1473            context.setCascadeDeleteBeforeEJBRemove(false);
1474            
1475            if ( context.getState() != INVOKING ) {
1476                addPooledEJB(context);
1477            }
1478    } else if ( ((ejbObjImpl != null) && ejbObjImpl.isRemoved())
1479        || ((ejbLocalObjImpl != null) && ejbLocalObjImpl.isRemoved()) )
1480    {
1481        //removeIncompleteTxEJB(context, false);
1482
context.setTransaction(null);
1483        context.setLastTransactionStatus(status);
1484
1485        if (context.getState() == INCOMPLETE_TX) {
1486        addPooledEJB(context);
1487        }
1488    }
1489    }
1490    
1491    
1492    // Called from BaseContainer just before invoking a business method
1493
// whose tx attribute is TX_NEVER / TX_NOT_SUPPORTED / TX_SUPPORTS without
1494
// a client tx.
1495
void preInvokeNoTx(Invocation inv) {
1496        EntityContextImpl context = (EntityContextImpl)inv.context;
1497        
1498        if ( context.getState() == DESTROYED ) {
1499            return;
1500        }
1501        
1502        if ( context.isNewlyActivated() &&
1503            !inv.invocationInfo.isCreateHomeFinder ) {
1504            // follow EJB2.0 section 12.1.6.1
1505
EntityBean e = (EntityBean)context.getEJB();
1506            try {
1507                callEJBLoad(e, context);
1508            } catch ( NoSuchEntityException ex ) {
1509                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
1510
forceDestroyBean(context);
1511                
1512                throw new NoSuchObjectLocalException(
1513         "NoSuchEntityException thrown by ejbLoad, EJB instance discarded", ex);
1514            } catch ( Exception JavaDoc ex ) {
1515                // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
1516
forceDestroyBean(context);
1517                
1518                throw new EJBException(ex);
1519            }
1520            
1521            context.setNewlyActivated(false);
1522        }
1523    }
1524    
1525    // Called from BaseContainer after invoking a method with tx attribute
1526
// NotSupported or Never or Supports without client tx.
1527
void postInvokeNoTx(Invocation inv) {
1528        // This calls ejbStore to allow bean to flush any state to database.
1529
// This is also sufficient for compliance with EJB2.0 section 12.1.6.1
1530
// (ejbStore must be called between biz method and ejbPassivate).
1531
beforeCompletion((EJBContextImpl)inv.context);
1532    }
1533    
1534    
1535    
1536    // CacheListener interface
1537
public void trimEvent(Object JavaDoc primaryKey, Object JavaDoc context) {
1538        boolean addTask = false;
1539        synchronized (asyncTaskSemaphore) {
1540            passivationCandidates.add(context);
1541            if (addedASyncTask == true) {
1542                return;
1543            }
1544            addTask = addedASyncTask = true;
1545        }
1546        
1547        try {
1548            ASyncPassivator work = new ASyncPassivator();
1549            ContainerWorkPool.addLast(work);
1550        } catch (Exception JavaDoc ex) {
1551            addedASyncTask = false;
1552            _logger.log(Level.WARNING, "ejb.add_cleanup_task_error",ex);
1553        }
1554    }
1555    
1556    private class ASyncPassivator
1557        implements com.sun.enterprise.util.threadpool.Servicable
1558    {
1559        
1560        public void prolog() { }
1561        
1562        public void epilog() { }
1563        
1564        public void service() { run(); }
1565        
1566        public void run() {
1567            final Thread JavaDoc currentThread = Thread.currentThread();
1568            final ClassLoader JavaDoc previousClassLoader =
1569                currentThread.getContextClassLoader();
1570            final ClassLoader JavaDoc myClassLoader = loader;
1571            
1572            try {
1573                //We need to set the context class loader for this
1574
//(deamon) thread!!
1575
java.security.AccessController.doPrivileged(
1576                    new java.security.PrivilegedAction JavaDoc() {
1577                        public java.lang.Object JavaDoc run() {
1578                            currentThread.setContextClassLoader(loader);
1579                            return null;
1580                        }
1581                    }
1582                );
1583                
1584                ComponentContext ctx = null;
1585                do {
1586                    synchronized (asyncTaskSemaphore) {
1587                        int sz = passivationCandidates.size() - 1;
1588                        if (sz > 0) {
1589                            ctx =
1590                          (ComponentContext) passivationCandidates.remove(sz-1);
1591                        } else {
1592                            return;
1593                        }
1594                    }
1595                    
1596                    if (ctx != null) {
1597                        passivateEJB(ctx);
1598            totalPassivations++;
1599                    }
1600                } while (ctx != null);
1601            } catch (Throwable JavaDoc th) {
1602        totalPassivationErrors++;
1603                th.printStackTrace();
1604            } finally {
1605                synchronized (asyncTaskSemaphore) {
1606                    addedASyncTask = false;
1607                }
1608                java.security.AccessController.doPrivileged(
1609                    new java.security.PrivilegedAction JavaDoc() {
1610                        public java.lang.Object JavaDoc run() {
1611                            currentThread.setContextClassLoader(previousClassLoader);
1612                            return null;
1613                        }
1614                    }
1615                );
1616            }
1617        }
1618    }
1619    
1620    // Called from AbstractCache
1621
boolean passivateEJB(ComponentContext ctx) {
1622        if (containerState != CONTAINER_STARTED) {
1623            return false;
1624        }
1625        
1626        EntityContextImpl context = (EntityContextImpl)ctx;
1627        
1628        if (context.getState() != READY) {
1629            return false;
1630        }
1631        
1632        _logger.log(Level.FINEST,"EntityContainer.passivateEJB(): context = (" +
1633            ctx + ")");
1634        EntityBean ejb = (EntityBean)context.getEJB();
1635        
1636        Invocation inv = new Invocation(ejb, this, context);
1637        inv.method = ejbPassivateMethod;
1638        
1639        Object JavaDoc pkey = context.getPrimaryKey();
1640        boolean wasPassivated = false;
1641        
1642        // check state after locking ctx
1643
if ( context.getState() != READY )
1644            return false;
1645        try {
1646            invocationManager.preInvoke(inv);
1647            
1648            // remove EJB from readyStore
1649
removeContextFromReadyStore(pkey, context);
1650            
1651            // no Tx needed for ejbPassivate
1652
ejb.ejbPassivate();
1653            
1654            wasPassivated = true;
1655        } catch ( Exception JavaDoc ex ) {
1656            _logger.log(Level.FINE, "Exception in passivateEJB()", ex);
1657            // Error during ejbStore/Passivate, discard bean: EJB2.0 18.3.3
1658
forceDestroyBean(context);
1659            return false;
1660        } finally {
1661            invocationManager.postInvoke(inv);
1662        }
1663        
1664        // Remove the ejbObject/LocalObject from ejbObject/LocalObjectStore
1665
// If a future invocation arrives for them, they'll get recreated.
1666
if ( isRemote ) {
1667            removeEJBObjectFromStore(pkey);
1668        }
1669        if ( isLocal ) {
1670            ejbLocalObjectStore.remove(pkey);
1671        }
1672        
1673        // Note: ejbStore and ejbPassivate need the primarykey
1674
// so we should dissociate the context from EJBObject only
1675
// after calling ejbStore and ejbPassivate.
1676
synchronized (context) {
1677            addPooledEJB(context);
1678        }
1679        
1680        return wasPassivated;
1681    }
1682    
1683    
1684    
1685    /***************************************************************************
1686     * The following are private methods for implementing internal logic
1687     * for lifecyle and state management, in a reusable way.
1688     **************************************************************************/

1689    
1690    
1691    // called from postCreate, postFind,
1692
// getEJBLocalObjectForPrimaryKey, removeBean
1693
protected EJBLocalObjectImpl internalGetEJBLocalObjectImpl
1694        (Object JavaDoc primaryKey) {
1695        return internalGetEJBLocalObjectImpl(primaryKey, false,
1696                                             defaultCacheEJBO);
1697    }
1698    
1699    protected EJBLocalObjectImpl internalGetEJBLocalObjectImpl
1700        (Object JavaDoc primaryKey, boolean incrementRefCount)
1701    {
1702        return internalGetEJBLocalObjectImpl(primaryKey, incrementRefCount,
1703            defaultCacheEJBO);
1704    }
1705    
1706    protected EJBLocalObjectImpl internalGetEJBLocalObjectImpl
1707        (Object JavaDoc primaryKey, boolean incrementRefCount, boolean cacheEJBO) {
1708        // check if the EJBLocalObject exists in the store.
1709
try {
1710            EJBLocalObjectImpl localObjImpl = (EJBLocalObjectImpl)
1711                ejbLocalObjectStore.get(primaryKey, incrementRefCount);
1712            if ( localObjImpl == null ) {
1713
1714                localObjImpl = instantiateEJBLocalObjectImpl();
1715                
1716                // associate the EJBLocalObjectImpl with the primary key
1717
localObjImpl.setKey(primaryKey);
1718                
1719                // add the EJBLocalObjectImpl to ejbLocalObjectStore
1720
if (incrementRefCount || cacheEJBO) {
1721                    ejbLocalObjectStore.put(primaryKey, localObjImpl,
1722                        incrementRefCount);
1723                }
1724            }
1725            return localObjImpl;
1726        } catch ( Exception JavaDoc ex ) {
1727            _logger.log(Level.SEVERE,"ejb.get_ejb_local_object_exception",
1728                        logParams);
1729            _logger.log(Level.SEVERE,"",ex);
1730            throw new EJBException(ex);
1731        }
1732    }
1733    
1734    // called from postFind, getEJBObjectForPrimaryKey,
1735
// EntityContextImpl.getEJBObject()
1736
EJBObject getEJBObjectStub(Object JavaDoc primaryKey, byte[] streamKey) {
1737        // primary key cant be null, streamkey may be null
1738

1739        // check if the EJBObject exists in the store.
1740
try {
1741            EJBObjectImpl ejbObjImpl =
1742                (EJBObjectImpl) ejbObjectStore.get(primaryKey);
1743            if ( (ejbObjImpl != null) && (ejbObjImpl.getStub() != null) ) {
1744                return (EJBObject) ejbObjImpl.getStub();
1745            }
1746
1747            // create a new stub without creating the EJBObject itself
1748
if ( streamKey == null ) {
1749                streamKey = EJBUtils.serializeObject(primaryKey, false);
1750            }
1751            EJBObject ejbStub = (EJBObject)
1752                remoteHomeRefFactory.createRemoteReference(streamKey);
1753                                                           
1754            return ejbStub;
1755        } catch ( Exception JavaDoc ex ) {
1756            _logger.log(Level.FINE,"", ex);
1757            throw new EJBException(ex);
1758        }
1759    }
1760
1761    // called from getEJBObject, postCreate, removeBean,
1762
// postFind, getEJBObjectForPrimaryKey
1763
protected EJBObjectImpl internalGetEJBObjectImpl(Object JavaDoc primaryKey,
1764                                                     byte[] streamKey) {
1765        return internalGetEJBObjectImpl(primaryKey, streamKey, false,
1766                                        defaultCacheEJBO);
1767    }
1768    
1769    protected EJBObjectImpl internalGetEJBObjectImpl(Object JavaDoc primaryKey,
1770            byte[] streamKey, boolean incrementRefCount)
1771    {
1772        return internalGetEJBObjectImpl
1773            (primaryKey, streamKey, incrementRefCount, defaultCacheEJBO);
1774    }
1775    
1776    
1777    // called from getEJBObject, postCreate, postFind,
1778
// getEJBObjectForPrimaryKey, removeBean
1779
protected EJBObjectImpl internalGetEJBObjectImpl(Object JavaDoc primaryKey,
1780        byte[] streamKey, boolean incrementRefCount, boolean cacheEJBO) {
1781        // primary key cant be null, streamkey may be null
1782

1783        // check if the EJBContext/EJBObject exists in the store.
1784
try {
1785            
1786            EJBObjectImpl ejbObjImpl = (EJBObjectImpl)
1787                ejbObjectStore.get(primaryKey, incrementRefCount);
1788
1789            if ( (ejbObjImpl != null) && (ejbObjImpl.getStub() != null) ) {
1790                return ejbObjImpl;
1791            }
1792            
1793            // check if the EJBContext/EJBObject exists in threadlocal
1794
// This happens if ejbo is in the process of being created.
1795
// This is necessary to prevent infinite recursion
1796
// because PRO.narrow calls is_a which calls the
1797
// ProtocolMgr which calls getEJBObject.
1798
ejbObjImpl = (EJBObjectImpl) ejbServant.get();
1799            if ( ejbObjImpl != null ) {
1800                return ejbObjImpl;
1801            }
1802            
1803            // create the EJBObject.
1804
ejbObjImpl = instantiateEJBObjectImpl();
1805            
1806            // associate the EJBObject with the primary key
1807
ejbObjImpl.setKey(primaryKey);
1808            
1809            // set ejbo in thread local to help recursive calls find the ejbo
1810
ejbServant.set(ejbObjImpl);
1811            
1812            // "Connect" the EJBObject to the Protocol Manager
1813

1814            if ( streamKey == null ) {
1815                streamKey = EJBUtils.serializeObject(primaryKey, false);
1816            }
1817            EJBObject ejbStub = (EJBObject)
1818                remoteHomeRefFactory.createRemoteReference(streamKey);
1819                                                           
1820            ejbObjImpl.setStub(ejbStub);
1821            ejbServant.set(null);
1822            
1823            if ((incrementRefCount || cacheEJBO)) {
1824                EJBObjectImpl ejbo1 =
1825                    (EJBObjectImpl) ejbObjectStore.put(primaryKey, ejbObjImpl,
1826                        incrementRefCount);
1827                if ((ejbo1 != null) && (ejbo1 != ejbObjImpl)) {
1828                    remoteHomeRefFactory.destroyReference(ejbObjImpl.getStub(),
1829                                                      ejbObjImpl);
1830                    ejbObjImpl = ejbo1;
1831                }
1832            }
1833            
1834            return ejbObjImpl;
1835        }
1836        catch ( Exception JavaDoc ex ) {
1837            _logger.log(Level.FINE, "ejb.get_ejb_context_exception", logParams);
1838            _logger.log(Level.FINE,"",ex);
1839            throw new EJBException(ex);
1840        }
1841    } //internalGetEJBObject(..)
1842

1843    
1844    // called from getContext and getReadyEJB
1845
protected EntityContextImpl getPooledEJB() {
1846        try {
1847            return (EntityContextImpl) entityCtxPool.getObject(true, null);
1848        } catch (com.sun.ejb.containers.util.pool.PoolException inEx) {
1849            throw new EJBException(inEx);
1850        }
1851    }
1852    
1853    // called from passivateAndPoolEJB, releaseContext, passivateEJB
1854
// Note: addPooledEJB is idempotent: i.e. even if it is called multiple
1855
// times with the same context, the context is added only once.
1856
protected void addPooledEJB(EntityContextImpl context) {
1857        if ( context.getState() == POOLED ) {
1858            return;
1859        }
1860        // we're sure that no concurrent thread can be using this
1861
// context, so no need to synchronize.
1862
context.setEJBLocalObjectImpl(null);
1863        context.setEJBObjectImpl(null);
1864        context.setEJBStub(null);
1865        context.setState(POOLED);
1866    context.clearCachedPrimaryKey();
1867        
1868        //context.cacheEntry = null;
1869
entityCtxPool.returnObject(context);
1870        
1871    }
1872    
1873    // called from addReadyEJB and afterCompletion
1874
protected void passivateAndPoolEJB(EntityContextImpl context) {
1875        if ( context.getState() == DESTROYED || context.getState() == POOLED )
1876            return;
1877        
1878        // if ( context.isPooled() ) {
1879
// context.isPooled(false);
1880
// return;
1881
// }
1882
EntityBean ejb = (EntityBean) context.getEJB();
1883        synchronized ( context ) {
1884            Invocation inv = new Invocation(ejb, this, context);
1885            inv.method = ejbPassivateMethod;
1886            invocationManager.preInvoke(inv);
1887            
1888            try {
1889                ejb.ejbPassivate();
1890            } catch ( Exception JavaDoc ex ) {
1891                _logger.log(Level.FINE,"Exception in passivateAndPoolEJB()",ex);
1892                forceDestroyBean(context);
1893                return;
1894            } finally {
1895                invocationManager.postInvoke(inv);
1896            }
1897            
1898            // remove EJB(Local)Object from ejb(Local)ObjectStore
1899

1900            
1901            Object JavaDoc primaryKey = context.getPrimaryKey();
1902            if ( isRemote ) {
1903                removeEJBObjectFromStore(primaryKey);
1904            }
1905            if ( isLocal ) {
1906                ejbLocalObjectStore.remove(primaryKey);
1907            }
1908            
1909            addPooledEJB(context);
1910        }
1911    }
1912    
1913    
1914    /**
1915     * Called from getContext and getEJBWithIncompleteTx
1916     * Get an EJB in the ready state (i.e. which is not doing any
1917     * invocations and doesnt have any incomplete Tx), for the
1918     * ejbObject provided in the Invocation.
1919     * Concurrent invocations should get *different* instances.
1920     */

1921    protected EntityContextImpl activateEJBFromPool(Object JavaDoc primaryKey,
1922        Invocation inv) {
1923        EntityContextImpl context = null;
1924        // get a pooled EJB and activate it.
1925
context = getPooledEJB();
1926        
1927        // we're sure that no concurrent thread can be using this
1928
// context, so no need to synchronize.
1929

1930        // set EJBObject/LocalObject for the context
1931
if ( inv.isLocal ) {
1932            EJBLocalObjectImpl localObjImpl =
1933                internalGetEJBLocalObjectImpl(primaryKey, true);
1934            inv.ejbObject = localObjImpl;
1935            context.setEJBLocalObjectImpl(localObjImpl);
1936            // No need to create/set EJBObject if this EJB isRemote too.
1937
// This saves remote object creation overhead.
1938
// The EJBObject and stub will get created lazily if needed
1939
// when EntityContext.getEJBObjectImpl is called.
1940
} else { // remote invocation
1941
EJBObjectImpl ejbObjImpl =
1942                internalGetEJBObjectImpl(primaryKey, null, true);
1943            inv.ejbObject = ejbObjImpl;
1944            context.setEJBObjectImpl(ejbObjImpl);
1945            context.setEJBStub((EJBObject)ejbObjImpl.getStub());
1946            
1947            if ( isLocal ) {
1948                // Create EJBLocalObject so EntityContext methods work
1949
context.setEJBLocalObjectImpl(
1950                    internalGetEJBLocalObjectImpl(primaryKey, true));
1951            }
1952        }
1953        
1954        context.setState(READY);
1955        
1956        EntityBean ejb = (EntityBean)context.getEJB();
1957        
1958        Invocation inv2 = new Invocation(ejb, this, context);
1959        inv2.method = ejbActivateMethod;
1960        invocationManager.preInvoke(inv2);
1961        
1962        try {
1963            ejb.ejbActivate();
1964            
1965            // Note: ejbLoad will be called during preInvokeTx
1966
// since this EJB instance is being associated with
1967
// a Tx for the first time.
1968

1969        } catch ( Exception JavaDoc ex ) {
1970            // Error during ejbActivate, discard bean: EJB2.0 18.3.3
1971
forceDestroyBean(context);
1972            throw new EJBException(ex);
1973        } finally {
1974            invocationManager.postInvoke(inv2);
1975        }
1976        
1977        context.setNewlyActivated(true);
1978        //recycler.initSoftRef(context);
1979

1980        afterNewlyActivated(context);
1981        
1982        return context;
1983    } //getReadyEJB(inv)
1984

1985    
1986    // called from releaseContext, afterCompletion
1987

1988    
1989    /**
1990     * Get an EJB instance for this EJBObject and current client Tx
1991     * Called only from getContext.
1992     * Return null if there no INCOMPLETE_TX bean for the pkey & tx.
1993     */

1994    private EntityContextImpl getEJBWithIncompleteTx(Invocation inv) {
1995        // We need to make sure that two concurrent client
1996
// invocations with same primary key and same client tx
1997
// get the SAME EJB instance.
1998
// So we need to maintain exactly one copy of an EJB's state
1999
// per transaction.
2000

2001        J2EETransaction current = null;
2002        try {
2003            current = (J2EETransaction) transactionManager.getTransaction();
2004        } catch ( SystemException ex ) {
2005            throw new EJBException(ex);
2006        }
2007        
2008        EntityContextImpl ctx = null;
2009    if (current != null) {
2010        ActiveTxCache activeTxCache = (ActiveTxCache)
2011        current.getActiveTxCache();
2012        ctx = (activeTxCache == null)
2013        ? null
2014        : activeTxCache.get(this, inv.ejbObject.getKey());
2015    }
2016    
2017    return ctx;
2018    }
2019    
2020    
2021    /**
2022     * Called only from afterBegin.
2023     * This EJB is invoked either with client's tx (in which case
2024     * it would already be in table), or with new tx (in which case
2025     * it would not be in table).
2026     */

2027    private void addIncompleteTxEJB(EntityContextImpl context) {
2028        J2EETransaction current = (J2EETransaction) context.getTransaction();
2029        if ( current == null ) {
2030            return;
2031        }
2032        if ( (context.getEJBObjectImpl() == null) &&
2033             (context.getEJBLocalObjectImpl() == null) ) {
2034            return;
2035        }
2036
2037        // Its ok to add this context without checking if its already there.
2038
ActiveTxCache activeTxCache = (ActiveTxCache) current.getActiveTxCache();
2039    if (activeTxCache == null) {
2040        activeTxCache = new ActiveTxCache(DEFAULT_TX_CACHE_BUCKETS);
2041        current.setActiveTxCache(activeTxCache);
2042    }
2043
2044    activeTxCache.add(context);
2045        
2046        Vector beans;
2047        synchronized ( txBeanTable ) {
2048            beans = (Vector)txBeanTable.get(current);
2049            if ( beans == null ) {
2050                beans = new Vector();
2051                txBeanTable.put(current, beans);
2052            }
2053        }
2054        beans.add(context);
2055    }
2056    
2057    /**
2058     * Called from releaseContext if ejb is removed, from afterCompletion,
2059     * and from passivateEJB.
2060     */

2061    protected void removeIncompleteTxEJB(EntityContextImpl context,
2062                                         boolean updateTxBeanTable)
2063    {
2064        J2EETransaction current = (J2EETransaction) context.getTransaction();
2065
2066        if (current == null) {
2067            return;
2068        }
2069        if ( (context.getEJBObjectImpl() == null) &&
2070             (context.getEJBLocalObjectImpl() == null) ) {
2071            return;
2072        }
2073        
2074    ActiveTxCache activeTxCache = (ActiveTxCache)
2075        (((J2EETransaction) current).getActiveTxCache());
2076    if (activeTxCache != null) {
2077        activeTxCache.remove(this, context.getPrimaryKey());
2078    }
2079
2080        if ( updateTxBeanTable ) {
2081            Vector beans = (Vector)txBeanTable.get(current);
2082            if ( beans != null ) {
2083                beans.remove(context); // this is a little expensive...
2084
}
2085        }
2086    }
2087    
2088    /**
2089     * a TimerTask class to trim a given cache of timedout entries
2090     */

2091    private class IdleBeansPassivator
2092        extends java.util.TimerTask JavaDoc
2093    {
2094        Cache cache;
2095        
2096        IdleBeansPassivator(Cache cache) {
2097            this.cache = cache;
2098        }
2099        
2100        public void run() {
2101            if (timerValid) {
2102                cache.trimExpiredEntries(Integer.MAX_VALUE);
2103            }
2104        }
2105
2106    public boolean cancel() {
2107        cache = null;
2108        return super.cancel();
2109    }
2110    }
2111    
2112    
2113    // Key for INCOMPLETE_TX beans which contains ejbObject + Tx
2114
private class EJBTxKey {
2115        
2116        Transaction tx; // may be null
2117
Object JavaDoc primaryKey;
2118        int pkHashCode;
2119        
2120        EJBTxKey(Object JavaDoc primaryKey, Transaction tx) {
2121            this.tx = tx;
2122            this.primaryKey = primaryKey;
2123            this.pkHashCode = primaryKey.hashCode();
2124        }
2125        
2126        public final int hashCode() {
2127            // Note: this hashcode need not be persistent across
2128
// activations of this process.
2129
// Return the primaryKey's hashCode. The Hashtable will
2130
// then search for the Tx through the bucket for
2131
// the primaryKey's hashCode.
2132

2133            //return primaryKey.hashCode();
2134
return pkHashCode;
2135        }
2136        
2137        public final boolean equals(Object JavaDoc obj) {
2138            if ( !(obj instanceof EJBTxKey) ) {
2139                return false;
2140            }
2141            EJBTxKey other = (EJBTxKey) obj;
2142            try {
2143                // Note: tx may be null if the EJB is not associated with
2144
// an incomplete Tx.
2145
if ( primaryKey.equals(other.primaryKey) ) {
2146                    if ( (tx == null) && (other.tx == null) ) {
2147                        return true;
2148                    } else if ( (tx != null) && (other.tx != null)
2149                                && tx.equals(other.tx) ) {
2150                        return true;
2151                    } else {
2152                        return false;
2153                    }
2154                } else {
2155                    return false;
2156                }
2157            } catch ( Exception JavaDoc ex ) {
2158                _logger.log(Level.FINE, "Exception in equals()", ex);
2159                return false;
2160            }
2161        }
2162        
2163    }
2164    
2165    protected class CacheProperties {
2166        
2167        int maxCacheSize ;
2168        int numberOfVictimsToSelect ;
2169        int cacheIdleTimeoutInSeconds ;
2170        String JavaDoc victimSelectionPolicy;
2171        int removalTimeoutInSeconds;
2172        
2173        public CacheProperties() {
2174            numberOfVictimsToSelect =
2175                new Integer JavaDoc(ejbContainer.getCacheResizeQuantity()).intValue();
2176            maxCacheSize=new Integer JavaDoc(ejbContainer.getMaxCacheSize()).intValue();
2177            cacheIdleTimeoutInSeconds =
2178            new Integer JavaDoc(ejbContainer.getCacheIdleTimeoutInSeconds()).intValue();
2179            victimSelectionPolicy = ejbContainer.getVictimSelectionPolicy();
2180            removalTimeoutInSeconds =
2181            new Integer JavaDoc(ejbContainer.getRemovalTimeoutInSeconds()).intValue();
2182            
2183            if(beanCacheDes != null) {
2184                int temp = 0;
2185                if((temp = beanCacheDes.getResizeQuantity()) != -1) {
2186                    numberOfVictimsToSelect = temp;
2187                }
2188                
2189                if((temp = beanCacheDes.getMaxCacheSize()) != -1) {
2190                    maxCacheSize = temp;
2191                }
2192                
2193                if ((temp = beanCacheDes.getCacheIdleTimeoutInSeconds()) != -1)
2194                {
2195                    cacheIdleTimeoutInSeconds = temp;
2196                }
2197                
2198                if (( beanCacheDes.getVictimSelectionPolicy()) != null) {
2199                    victimSelectionPolicy =
2200                        beanCacheDes.getVictimSelectionPolicy();
2201                }
2202                if ((temp = beanCacheDes.getRemovalTimeoutInSeconds()) != -1) {
2203                    removalTimeoutInSeconds = temp;
2204                }
2205            }
2206        }
2207    } //CacheProperties
2208

2209    private class PoolProperties {
2210        int maxPoolSize;
2211        int poolIdleTimeoutInSeconds;
2212        // int maxWaitTimeInMillis;
2213
int poolResizeQuantity;
2214        int steadyPoolSize;
2215        
2216        public PoolProperties() {
2217            
2218            maxPoolSize = new Integer JavaDoc(ejbContainer.getMaxPoolSize()).intValue();
2219            poolIdleTimeoutInSeconds = new Integer JavaDoc(
2220                ejbContainer.getPoolIdleTimeoutInSeconds()).intValue();
2221            poolResizeQuantity = new Integer JavaDoc(
2222                ejbContainer.getPoolResizeQuantity()).intValue();
2223            steadyPoolSize = new Integer JavaDoc(
2224                ejbContainer.getSteadyPoolSize()).intValue();
2225            if(beanPoolDes != null) {
2226                int temp = 0;
2227                if (( temp = beanPoolDes.getMaxPoolSize()) != -1) {
2228                    maxPoolSize = temp;
2229                }
2230                if (( temp = beanPoolDes.getPoolIdleTimeoutInSeconds()) != -1) {
2231                    poolIdleTimeoutInSeconds = temp;
2232                }
2233                if (( temp = beanPoolDes.getPoolResizeQuantity()) != -1) {
2234                    poolResizeQuantity = temp;
2235                }
2236                if (( temp = beanPoolDes.getSteadyPoolSize()) != -1) {
2237                    steadyPoolSize = temp;
2238                }
2239            }
2240        }
2241    } //PoolProperties
2242

2243    
2244    boolean isIdentical(EJBObjectImpl ejbObjImpl, EJBObject other)
2245        throws RemoteException JavaDoc
2246    {
2247        if ( other == ejbObjImpl.getStub() ) {
2248            return true;
2249        } else {
2250            try {
2251                // EJBObject may be a remote object.
2252
// Compare homes. See EJB2.0 spec section 9.8.
2253
if ( !protocolMgr.isIdentical(ejbHomeStub,
2254                                              other.getEJBHome()))
2255                    return false;
2256                
2257                // Compare primary keys.
2258
if (!ejbObjImpl.getPrimaryKey().equals(other.getPrimaryKey())) {
2259                    return false;
2260                }
2261                
2262                return true;
2263            } catch ( Exception JavaDoc ex ) {
2264                _logger.log(Level.INFO, "ejb.ejb_comparison_exception",
2265                            logParams);
2266                _logger.log(Level.INFO, "", ex);
2267                throw new RemoteException JavaDoc("Exception in isIdentical()", ex);
2268            }
2269        }
2270    }
2271    
2272    
2273    protected void callEJBLoad(EntityBean ejb, EntityContextImpl context)
2274        throws Exception JavaDoc
2275    {
2276        try {
2277            context.setInEjbLoad(true);
2278            ejb.ejbLoad();
2279            // Note: no need to do context.setDirty(false) because ejbLoad is
2280
// called immediately before a business method.
2281
} catch(Exception JavaDoc e) {
2282            throw e;
2283        } finally {
2284            context.setInEjbLoad(false);
2285        }
2286    }
2287    
2288    protected void callEJBStore(EntityBean ejb, EntityContextImpl context)
2289        throws Exception JavaDoc
2290    {
2291        try {
2292            context.setInEjbStore(true);
2293            ejb.ejbStore();
2294        } finally {
2295            context.setInEjbStore(false);
2296            context.setDirty(false); // bean's state is in sync with DB
2297
}
2298    }
2299    
2300    protected void callEJBRemove(EntityBean ejb, EntityContextImpl context)
2301        throws Exception JavaDoc
2302    {
2303        Exception JavaDoc exc = null;
2304        try {
2305            context.setInEjbRemove(true);
2306            ejb.ejbRemove();
2307        } catch ( Exception JavaDoc ex ) {
2308            exc = ex;
2309            throw ex;
2310        } finally {
2311            context.setInEjbRemove(false);
2312            context.setDirty(false); // bean is removed so doesnt need ejbStore
2313
if ( AppVerification.doInstrument() ) {
2314                AppVerification.getInstrumentLogger().doInstrumentForEjb(
2315                ejbDescriptor, ejbRemoveMethod, exc);
2316            }
2317        }
2318    }
2319     
2320    void doTimerInvocationInit(Invocation inv, RuntimeTimerState timerState)
2321        throws Exception JavaDoc
2322    {
2323        Object JavaDoc primaryKey = timerState.getTimedObjectPrimaryKey();
2324        if( isRemote ) {
2325            inv.ejbObject = internalGetEJBObjectImpl(primaryKey, null);
2326            inv.isLocal = false;
2327        } else {
2328            inv.ejbObject = internalGetEJBLocalObjectImpl(primaryKey);
2329            inv.isLocal = true;
2330        }
2331        if( inv.ejbObject == null ) {
2332            throw new Exception JavaDoc("Timed object identity (" + primaryKey +
2333                " ) no longer exists " );
2334        }
2335    }
2336    
2337    public void undeploy() {
2338        
2339        //Change the container state to ensure that all new invocations will be rejected
2340
super.setUndeployedState();
2341        
2342        String JavaDoc ejbName = ejbDescriptor.getName();
2343        
2344        _logger.log(Level.FINE,"[EntityContainer]: Undeploying " + ejbName +
2345            " ...");
2346        
2347        // destroy all EJBObject refs
2348

2349        try {
2350            Iterator elements = ejbObjectStore.values();
2351            while ( elements.hasNext() ) {
2352                EJBObjectImpl ejbObjImpl = (EJBObjectImpl) elements.next();
2353                try {
2354                    if ( isRemote ) {
2355                        remoteHomeRefFactory.destroyReference
2356                            (ejbObjImpl.getStub(), ejbObjImpl.getEJBObject());
2357                                                    
2358                    }
2359                } catch ( Exception JavaDoc ex ) {
2360                    _logger.log(Level.FINE, "Exception in undeploy()", ex);
2361                }
2362            }
2363            
2364            ejbObjectStore.destroy(); //store must set the listern to null
2365
ejbObjectStore = null;
2366            
2367            ejbLocalObjectStore.destroy(); //store must set the listern to null
2368
ejbLocalObjectStore = null;
2369            
2370            // destroy all EJB instances in readyStore
2371
destroyReadyStoreOnUndeploy(); //cache must set the listern to null
2372

2373            // destroy all EJB instances in ActiveTxCache
2374
/*
2375            synchronized ( incompleteTxStore ) {
2376                Iterator beans = incompleteTxStore.values();
2377                while ( beans.hasNext() ) {
2378                    EJBContextImpl ctx = (EJBContextImpl)beans.next();
2379                    transactionManager.ejbDestroyed(ctx);
2380                }
2381            }
2382        */

2383            
2384            entityCtxPool.close();
2385            
2386            // stops the idle bean passivator and also removes the link
2387
// to the cache; note that cancel() method of timertask
2388
// does not remove the task from the timer's queue
2389
if (idleBeansPassivator != null) {
2390                try {
2391                    idleBeansPassivator.cancel();
2392                } catch (Exception JavaDoc e) {
2393                    _logger.log(Level.FINE,
2394                                "[EntityContainer] cancelTimerTask: ", e);
2395                }
2396                this.idleBeansPassivator.cache = null;
2397            }
2398        cancelTimerTasks();
2399        }
2400        finally {
2401            
2402            super.undeploy();
2403            
2404            // helps garbage collection
2405
this.ejbObjectStore = null;
2406            this.ejbLocalObjectStore = null;
2407            this.passivationCandidates = null;
2408            this.readyStore = null;
2409            this.entityCtxPool = null;
2410            this.txBeanTable = null;
2411            this.iased = null;
2412            this.beanCacheDes = null;
2413            this.beanPoolDes = null;
2414            this.svr = null;
2415            this.cfg = null;
2416            this.ejbContainer = null;
2417            this.cacheProp = null;
2418            this.poolProp = null;
2419            this.asyncTaskSemaphore = null;
2420            this.idleBeansPassivator = null;
2421            
2422        }
2423        
2424        _logger.log(Level.FINE," [EntityContainer]: Successfully Undeployed " +
2425            ejbName);
2426    }
2427    
2428    protected void afterNewlyActivated(EntityContextImpl context) {
2429        //Noop for EntityContainer
2430
}
2431    
2432    protected EntityContextImpl createEntityContextInstance(EntityBean ejb,
2433            EntityContainer entityContainer)
2434    {
2435        return new EntityContextImpl(ejb, entityContainer);
2436    }
2437    
2438    private class EntityContextFactory
2439        implements ObjectFactory
2440    {
2441        private EntityContainer entityContainer;
2442        
2443        public EntityContextFactory(EntityContainer entityContainer) {
2444            this.entityContainer = entityContainer;
2445        }
2446        
2447        public Object JavaDoc create(Object JavaDoc param) {
2448            EntityContextImpl entityCtx = null;
2449            ComponentInvocation ci = null;
2450            try {
2451                // Create new bean. The constructor is not allowed
2452
// to do a JNDI access (see EJB2.0 section 10.5.5),
2453
// so no need to call invocationMgr before instantiation.
2454
EntityBean ejb = (EntityBean) ejbClass.newInstance();
2455                
2456                // create EntityContext
2457
entityCtx = createEntityContextInstance(ejb, entityContainer);
2458
2459                ci = new ComponentInvocation(ejb, entityContainer,entityCtx);
2460                invocationManager.preInvoke(ci);
2461                
2462                // setEntityContext may be called with or without a Tx
2463
// spec 9.4.2
2464
ejb.setEntityContext(entityCtx);
2465
2466                // NOTE : Annotations are *not* supported for entity beans
2467
// so we do not invoke the injection manager for this instance.
2468

2469            } catch (Exception JavaDoc ex ) {
2470                throw new EJBException("Could not create Entity EJB", ex);
2471            } finally {
2472                if ( ci != null ) {
2473                    invocationManager.postInvoke(ci);
2474                }
2475            }
2476            
2477            entityCtx.touch();
2478            return entityCtx;
2479        }
2480        
2481        
2482        public void destroy(Object JavaDoc object) {
2483            if (object == null) {
2484                //means that this is called through forceDestroyBean
2485
//So no need to anything, as we cannot call unsetEntityCtx etc..
2486
return;
2487            }
2488            
2489            EntityContextImpl context = (EntityContextImpl) object;
2490            EntityBean ejb = (EntityBean)context.getEJB();
2491            if (context.getState() != DESTROYED) {
2492                ComponentInvocation ci = new ComponentInvocation(ejb, entityContainer, context);
2493                invocationManager.preInvoke(ci);
2494                
2495                // kill the bean and let it be GC'ed
2496
try {
2497                    synchronized ( context ) {
2498                        context.setEJBLocalObjectImpl(null);
2499                        context.setEJBObjectImpl(null);
2500                        context.setEJBStub(null);
2501                        context.setState(DESTROYED);
2502                        //context.cacheEntry = null;
2503
context.setInUnsetEntityContext(true);
2504                        
2505                        try {
2506                            ejb.unsetEntityContext();
2507                        } catch ( Exception JavaDoc ex ) {
2508                            _logger.log(Level.FINE,
2509                                "Exception in ejb.unsetEntityContext()", ex);
2510                        }
2511                        
2512                        // tell the TM to release resources held by the bean
2513
transactionManager.ejbDestroyed(context);
2514                    }
2515                } finally {
2516                    invocationManager.postInvoke(ci);
2517                }
2518            } else {
2519                //Called from forceDestroyBean
2520
try {
2521                    synchronized ( context ) {
2522                        context.setEJBLocalObjectImpl(null);
2523                        context.setEJBObjectImpl(null);
2524                        context.setEJBStub(null);
2525                        context.setState(DESTROYED);
2526                        //context.cacheEntry = null;
2527

2528                        // mark the context's transaction for rollback
2529
Transaction tx = context.getTransaction();
2530                        if ( tx != null && tx.getStatus() !=
2531                            Status.STATUS_NO_TRANSACTION ) {
2532                            context.getTransaction().setRollbackOnly();
2533                        }
2534                        
2535                        // tell the TM to release resources held by the bean
2536
transactionManager.ejbDestroyed(context);
2537                        
2538                    }
2539                } catch (Exception JavaDoc ex) {
2540                    _logger.log(Level.FINE, "Exception in destroy()", ex);
2541                }
2542            }
2543        }
2544        
2545    } //class EntityContextFactory
2546

2547    private static void appendStat(StringBuffer JavaDoc sbuf, String JavaDoc header, Map map) {
2548        
2549        sbuf.append("\n\t[").append(header).append(": ");
2550        if (map != null) {
2551            Iterator iter = map.keySet().iterator();
2552            
2553            while (iter.hasNext()) {
2554                String JavaDoc name = (String JavaDoc)iter.next();
2555                sbuf.append(name).append("=").append(map.get(name))
2556            .append("; ");
2557            }
2558        } else {
2559            sbuf.append("NONE");
2560        }
2561        sbuf.append("]");
2562    }
2563    
2564    private void createCaches() throws Exception JavaDoc {
2565
2566        cacheProp = new CacheProperties();
2567        
2568        int cacheSize = cacheProp.maxCacheSize;
2569        int numberOfVictimsToSelect = cacheProp.numberOfVictimsToSelect;
2570        float loadFactor = DEFAULT_LOAD_FACTOR;
2571        idleTimeout = cacheProp.cacheIdleTimeoutInSeconds * 1000;
2572        
2573        createReadyStore(cacheSize, numberOfVictimsToSelect, loadFactor,
2574                         idleTimeout);
2575        
2576        createEJBObjectStores(cacheSize, numberOfVictimsToSelect,
2577                              idleTimeout);
2578        
2579    }
2580    
2581    protected void createReadyStore(int cacheSize, int numberOfVictimsToSelect,
2582            float loadFactor, long idleTimeout) throws Exception JavaDoc
2583    {
2584        idleTimeout = (idleTimeout <= 0) ? -1 : idleTimeout;
2585        if (cacheSize <= 0 && idleTimeout <= 0) {
2586            readyStore = new BaseCache();
2587            cacheSize = DEFAULT_CACHE_SIZE;
2588            readyStore.init(cacheSize, loadFactor, null);
2589        } else {
2590            cacheSize = (cacheSize <= 0) ? DEFAULT_CACHE_SIZE : cacheSize;
2591            LruCache lru = new LruCache(DEFAULT_CACHE_SIZE);
2592            if (numberOfVictimsToSelect >= 0) {
2593                loadFactor = (float) (1.0 - (1.0 *
2594                                             numberOfVictimsToSelect/cacheSize));
2595            }
2596            lru.init(cacheSize, idleTimeout, loadFactor, null);
2597            readyStore = lru;
2598            readyStore.addCacheListener(this);
2599        }
2600        
2601        if (idleTimeout > 0) {
2602            idleBeansPassivator = setupIdleBeansPassivator(readyStore);
2603        }
2604    }
2605    
2606    protected void createEJBObjectStores(int cacheSize,
2607        int numberOfVictimsToSelect, long idleTimeout) throws Exception JavaDoc {
2608        
2609        EJBObjectCache lru = null;
2610        String JavaDoc ejbName = ejbDescriptor.getName();
2611        idleTimeout = (idleTimeout <= 0) ? -1 : idleTimeout;
2612        
2613        if (cacheSize <= 0 && idleTimeout <= 0) {
2614            ejbObjectStore = new UnboundedEJBObjectCache(ejbName);
2615            ejbObjectStore.init(DEFAULT_CACHE_SIZE, numberOfVictimsToSelect, 0L,
2616                (float)1.0, null);
2617            
2618            ejbLocalObjectStore = new UnboundedEJBObjectCache(ejbName);
2619            ejbLocalObjectStore.init(DEFAULT_CACHE_SIZE,
2620                numberOfVictimsToSelect, 0L, (float)1.0, null);
2621        } else {
2622            cacheSize = (cacheSize <= 0) ? DEFAULT_CACHE_SIZE : cacheSize;
2623            ejbObjectStore = new FIFOEJBObjectCache(ejbName);
2624            ejbObjectStore.init(cacheSize, numberOfVictimsToSelect, idleTimeout,
2625                (float)1.0, null);
2626            ejbObjectStore.setEJBObjectCacheListener(
2627                new EJBObjectCacheVictimHandler());
2628            
2629            ejbLocalObjectStore = new FIFOEJBObjectCache(ejbName);
2630            ejbLocalObjectStore.init(cacheSize, numberOfVictimsToSelect,
2631                idleTimeout, (float)1.0, null);
2632            ejbLocalObjectStore.setEJBObjectCacheListener(
2633                new LocalEJBObjectCacheVictimHandler());
2634        }
2635        
2636        if (idleTimeout > 0) {
2637            idleEJBObjectPassivator = setupIdleBeansPassivator(ejbObjectStore);
2638            idleLocalEJBObjectPassivator =
2639                setupIdleBeansPassivator(ejbLocalObjectStore);
2640        }
2641    }
2642    
2643    protected EntityContextImpl getReadyEJB(Invocation inv) {
2644        Object JavaDoc primaryKey = inv.ejbObject.getKey();
2645        EntityContextImpl context = null;
2646        // Try and get an EJB instance for this primaryKey from the
2647
// readyStore
2648
context = (EntityContextImpl)readyStore.remove(primaryKey);
2649        if (context == null || context.getState() != READY) {
2650            context = activateEJBFromPool(primaryKey, inv);
2651        }
2652        return context;
2653    } //getReadyEJB(inv)
2654

2655    protected void addReadyEJB(EntityContextImpl context) {
2656        // add to the cache (can have multiple instances of beans per key)
2657
Object JavaDoc primaryKey = context.getPrimaryKey();
2658        context.setState(READY);
2659        readyStore.add(primaryKey, context);
2660    }
2661    
2662    protected void destroyReadyStoreOnUndeploy() {
2663        if (readyStore == null) {
2664            return;
2665        }
2666        
2667        // destroy all EJB instances in readyStore
2668
synchronized ( readyStore ) {
2669            
2670            Iterator beans = readyStore.values();
2671            while ( beans.hasNext() ) {
2672                EJBContextImpl ctx = (EJBContextImpl)beans.next();
2673                transactionManager.ejbDestroyed(ctx);
2674            }
2675        }
2676        readyStore.destroy();
2677        readyStore = null;
2678    }
2679    
2680    protected void removeContextFromReadyStore(Object JavaDoc primaryKey,
2681        EntityContextImpl context) {
2682        readyStore.remove(primaryKey, context);
2683    }
2684    
2685    protected void doFlush( Invocation inv ) {
2686        if( !inv.invocationInfo.flushEnabled ||
2687            inv.exception != null ) {
2688            return;
2689        }
2690
2691        if( !isContainerManagedPers ) {
2692            //NEED TO INTERNATIONALIZE THIS WARNING MESSAGE
2693
_logger.log(Level.WARNING,
2694                "Cannot turn on flush-enabled-at-end-of-method for a bean with Bean Managed Persistence");
2695            return;
2696        }
2697
2698        InvocationInfo invInfo = inv.invocationInfo;
2699        Throwable JavaDoc exception = inv.exception;
2700        EntityContextImpl context = (EntityContextImpl)inv.context;
2701        Transaction tx = context.getTransaction();
2702
2703        //Since postInvoke(Tx) has been called before the releaseContext, the transaction
2704
//could be committed or rolledback. In that case there is no point to call flush
2705
if( tx == null) {
2706            return;
2707        }
2708
2709        //return w/o doing anything if the transaction is marked for rollback
2710
try {
2711            if( context.getRollbackOnly() ) {
2712                return;
2713            }
2714        } catch( Throwable JavaDoc ex ) {
2715            _logger.log(Level.WARNING, "Exception when calling getRollbackOnly", ex);
2716            return;
2717        }
2718
2719        if ( invInfo.isBusinessMethod ) {
2720            try {
2721                //Store the state of all the beans that are part of this transaction
2722
storeAllBeansInTx( tx );
2723            } catch( Throwable JavaDoc ex ) {
2724                inv.exception = ex;
2725                return;
2726            }
2727        }
2728
2729        try {
2730            BeanStateSynchronization pmcontract = (BeanStateSynchronization)inv.ejb;
2731            pmcontract.ejb__flush();
2732        } catch( Throwable JavaDoc ex ) {
2733            //check the type of the method and create the corresponding exception
2734
if( invInfo.startsWithCreate ) {
2735                CreateException ejbEx = new CreateException();
2736                ejbEx.initCause(ex);
2737                inv.exception = ejbEx;
2738            } else if( invInfo.startsWithRemove ) {
2739                RemoveException ejbEx = new RemoveException();
2740                ejbEx.initCause(ex);
2741                inv.exception = ejbEx;
2742            } else {
2743                EJBException ejbEx = new EJBException();
2744                ejbEx.initCause(ex);
2745                inv.exception = ejbEx;
2746            }
2747
2748            return;
2749        }
2750
2751    } //doFlush(...)
2752

2753    private void storeAllBeansInTx(Transaction tx) {
2754        // Call ejbStore on all entitybeans in tx for all EntityContainers
2755
Vector beans = (Vector)txBeanTable.get(tx);
2756        if ( beans == null ) {
2757            // No beans associated with the current transaction
2758
return;
2759        }
2760
2761        Iterator itr = beans.iterator();
2762        while ( itr.hasNext() ) {
2763            EntityContextImpl ctx = (EntityContextImpl)itr.next();
2764            if ( ctx.getState() == INCOMPLETE_TX && ctx.isDirty() ) {
2765                // Call ejbStore on the bean
2766
// Note: the bean may be in a different container instance
2767
EntityContainer cont = (EntityContainer)ctx.getContainer();
2768                cont.enlistResourcesAndStore(ctx);
2769            }
2770        }
2771    }
2772
2773
2774    protected class LocalEJBObjectCacheVictimHandler
2775        implements EJBObjectCacheListener,
2776            com.sun.enterprise.util.threadpool.Servicable
2777    {
2778        
2779        protected Object JavaDoc lock = new Object JavaDoc();
2780        protected boolean addedTask = false;
2781        protected ArrayList keys = new ArrayList(16);
2782        
2783        protected LocalEJBObjectCacheVictimHandler() {
2784        }
2785        
2786        //EJBObjectCacheListener interface
2787
public void handleOverflow(Object JavaDoc key) {
2788            doCleanup(key);
2789        }
2790        
2791        public void handleBatchOverflow(ArrayList paramKeys) {
2792            int size = paramKeys.size();
2793            synchronized (lock) {
2794                for (int i=0; i<size; i++) {
2795                    keys.add(paramKeys.get(i));
2796                }
2797                if (addedTask == true) {
2798                    return;
2799                }
2800                addedTask = true;
2801            }
2802            
2803            try {
2804                ContainerWorkPool.addLast(this);
2805            } catch (Exception JavaDoc ex) {
2806                _logger.log(Level.WARNING, "ejb.entity_add_async_task", ex);
2807                synchronized (lock) {
2808                    addedTask = false;
2809                }
2810            }
2811        }
2812        
2813        
2814        public void prolog() { }
2815        
2816        public void epilog() { }
2817        
2818        public void service() { run(); }
2819        
2820        public void run() {
2821            final Thread JavaDoc currentThread = Thread.currentThread();
2822            final ClassLoader JavaDoc previousClassLoader =
2823                currentThread.getContextClassLoader();
2824            final ClassLoader JavaDoc myClassLoader = loader;
2825            
2826            try {
2827            //We need to set the context class loader for this (deamon) thread!!
2828

2829                java.security.AccessController.doPrivileged(
2830                new java.security.PrivilegedAction JavaDoc() {
2831                    public java.lang.Object JavaDoc run() {
2832                        currentThread.setContextClassLoader(loader);
2833                        return null;
2834                    }
2835                }
2836                );
2837                
2838                ArrayList localKeys = null;
2839                do {
2840                    synchronized (lock) {
2841                        int size = keys.size();
2842                        if (size == 0) {
2843                            return;
2844                        }
2845                        
2846                        localKeys = keys;
2847                        keys = new ArrayList(16);
2848                    }
2849                    
2850                    int maxIndex = localKeys.size();
2851                    for (int i=0; i<maxIndex; i++) {
2852                        doCleanup(localKeys.get(i));
2853                    }
2854                } while (true);
2855                
2856            } catch (Throwable JavaDoc th) {
2857                th.printStackTrace();
2858            } finally {
2859                synchronized (lock) {
2860                    addedTask = false;
2861                }
2862                java.security.AccessController.doPrivileged(
2863                new java.security.PrivilegedAction JavaDoc() {
2864                    public java.lang.Object JavaDoc run() {
2865                       currentThread.setContextClassLoader(previousClassLoader);
2866                        return null;
2867                    }
2868                }
2869                );
2870            }
2871        }
2872        
2873        protected void doCleanup(Object JavaDoc key) {
2874            ejbLocalObjectStore.remove(key, false);
2875        }
2876    } //LocalEJBObjectCacheVictimHandler{}
2877

2878    protected class EJBObjectCacheVictimHandler
2879        extends LocalEJBObjectCacheVictimHandler
2880    {
2881        
2882        protected EJBObjectCacheVictimHandler() {
2883        }
2884        
2885        protected void doCleanup(Object JavaDoc key) {
2886            removeEJBObjectFromStore(key, false);
2887        }
2888    } //EJBObjectCacheVictimHandler{}
2889

2890
2891
2892    class EntityCacheStatsProvider
2893    implements com.sun.ejb.spi.stats.EJBCacheStatsProvider
2894    {
2895    private BaseCache cache;
2896    private String JavaDoc configData;
2897    private int confMaxCacheSize;
2898
2899    EntityCacheStatsProvider(BaseCache cache, int maxCacheSize) {
2900        this.cache = cache;
2901        this.confMaxCacheSize = maxCacheSize;
2902    }
2903    
2904    public int getCacheHits() {
2905        return ((Integer JavaDoc) cache.getStatByName(
2906            Constants.STAT_BASECACHE_HIT_COUNT)).intValue();
2907    }
2908    
2909    public int getCacheMisses() {
2910        return ((Integer JavaDoc) cache.getStatByName(
2911            Constants.STAT_BASECACHE_MISS_COUNT)).intValue();
2912    }
2913    
2914    public int getNumBeansInCache() {
2915        return cache.getEntryCount();
2916    }
2917    
2918    public int getNumExpiredSessionsRemoved() {
2919        return 0;
2920    }
2921    
2922    public int getNumPassivationErrors() {
2923        return totalPassivationErrors;
2924    }
2925    
2926    public int getNumPassivations() {
2927        return totalPassivations;
2928    }
2929    
2930    public int getNumPassivationSuccess() {
2931        return totalPassivations - totalPassivationErrors;
2932    }
2933
2934    public int getMaxCacheSize() {
2935        return this.confMaxCacheSize;
2936    }
2937
2938        public void appendStats(StringBuffer JavaDoc sbuf) {
2939        sbuf.append("[Cache: ")
2940        .append("Size=").append(getNumBeansInCache()).append("; ")
2941        .append("HitCount=").append(getCacheHits()).append("; ")
2942        .append("MissCount=").append(getCacheMisses()).append("; ")
2943        .append("Passivations=").append(getNumPassivations()).append("; ");
2944        if (configData != null) {
2945        sbuf.append(configData);
2946        }
2947        sbuf.append("]");
2948    }
2949
2950    }//End of class EntityCacheStatsProvider
2951

2952}
2953
2954//No need to sync...
2955
class ActiveTxCache {
2956
2957    private static Logger _logger =
2958        LogDomains.getLogger(LogDomains.EJB_LOGGER);
2959
2960    private EntityContextImpl[] buckets;
2961    private int bucketMask;
2962
2963    ActiveTxCache(int numBuckets) {
2964    this.bucketMask = numBuckets - 1;
2965    initialize();
2966    }
2967
2968    EntityContextImpl get(BaseContainer container, Object JavaDoc pk) {
2969    int pkHashCode = pk.hashCode();
2970    int index = getIndex(pkHashCode);
2971
2972    EntityContextImpl ctx = buckets[index];
2973    while (ctx != null) {
2974        if (ctx.doesMatch(container, pkHashCode, pk)) {
2975        return ctx;
2976        }
2977        ctx = ctx._getNext();
2978    }
2979
2980    return null;
2981    }
2982
2983    void add(EntityContextImpl ctx) {
2984    ctx.cachePrimaryKey();
2985    int index = getIndex(ctx._getPKHashCode());
2986    ctx._setNext(buckets[index]);
2987    buckets[index] = ctx;
2988    }
2989
2990    EntityContextImpl remove(BaseContainer container, Object JavaDoc pk) {
2991    int pkHashCode = pk.hashCode();
2992    int index = getIndex(pkHashCode);
2993
2994    EntityContextImpl ctx = buckets[index];
2995    for (EntityContextImpl prev = null; ctx != null; ctx = ctx._getNext()) {
2996        if (ctx.doesMatch(container, pkHashCode, pk)) {
2997        if (prev == null) {
2998            buckets[index] = ctx._getNext();
2999        } else {
3000            prev._setNext(ctx._getNext());
3001        }
3002        ctx._setNext(null);
3003        break;
3004        }
3005        prev = ctx;
3006    }
3007
3008    return ctx;
3009    }
3010
3011    //One remove method is enough
3012
EntityContextImpl remove(Object JavaDoc pk, EntityContextImpl existingCtx) {
3013    int pkHashCode = pk.hashCode();
3014    int index = getIndex(pkHashCode);
3015
3016    EntityContextImpl ctx = buckets[index];
3017    for (EntityContextImpl prev = null; ctx != null; ctx = ctx._getNext()) {
3018        if (ctx == existingCtx) {
3019        if (prev == null) {
3020            buckets[index] = ctx._getNext();
3021        } else {
3022            prev._setNext(ctx._getNext());
3023        }
3024        ctx._setNext(null);
3025        break;
3026        }
3027        prev = ctx;
3028    }
3029
3030    return ctx;
3031    }
3032
3033    private void initialize() {
3034    buckets = new EntityContextImpl[bucketMask+1];
3035    }
3036
3037    private final int getIndex(int hashCode) {
3038    return (hashCode & bucketMask);
3039    }
3040
3041}
3042
Popular Tags