001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.core.entity;
017:
018: import org.apache.openejb.ApplicationException;
019: import org.apache.openejb.OpenEJBException;
020: import org.apache.openejb.SystemException;
021: import org.apache.openejb.InvalidateReferenceException;
022: import org.apache.openejb.DeploymentInfo;
023: import org.apache.openejb.spi.SecurityService;
024: import org.apache.openejb.core.BaseContext;
025: import org.apache.openejb.core.CoreDeploymentInfo;
026: import org.apache.openejb.core.Operation;
027: import org.apache.openejb.core.ThreadContext;
028: import org.apache.openejb.core.NoSuchObjectException;
029: import org.apache.openejb.core.transaction.TransactionRolledbackException;
030: import org.apache.openejb.util.LinkedListStack;
031: import org.apache.openejb.util.LogCategory;
032: import org.apache.openejb.util.Logger;
033: import org.apache.openejb.util.SafeToolkit;
034: import org.apache.openejb.util.Stack;
035:
036: import javax.ejb.EntityBean;
037: import javax.ejb.NoSuchEntityException;
038: import javax.transaction.Transaction;
039: import javax.transaction.TransactionManager;
040: import javax.transaction.Synchronization;
041: import javax.transaction.RollbackException;
042: import java.util.HashMap;
043: import java.util.Hashtable;
044: import java.util.Map;
045: import java.rmi.RemoteException;
046:
047: public class EntityInstanceManager {
048:
049: /* The default size of the bean pools. Every bean class gets its own pool of this size */
050: protected int poolsize = 0;
051: /* The container that owns this InstanceManager. Its needed to use the invoke() method
052: in the obtainInstance( ), which is needed for transactionally safe invokation.
053: */
054: protected EntityContainer container;
055: /*
056: * Every entity that is registered with a transaciton is kept in this pool until the tx
057: * completes. The pool contains SyncronizationWrappers (each holding a reference) keyed
058: * by using an instance of the inner Key class. The Key class is a compound key composed
059: * of the tx, deployment, and primary key identifiers.
060: */
061: protected Hashtable<Object, SyncronizationWrapper> txReadyPool = new Hashtable<Object, SyncronizationWrapper>();
062: /*
063: * contains a collection of LinkListStacks indexed by deployment id. Each indexed stack
064: * represents the method ready pool of for that class.
065: */
066: protected Map<Object, LinkedListStack> poolMap = null;
067:
068: public Logger logger = Logger.getInstance(LogCategory.OPENEJB,
069: "org.apache.openejb.util.resources");
070:
071: protected SafeToolkit toolkit = SafeToolkit
072: .getToolkit("EntityInstanceManager");
073: private TransactionManager transactionManager;
074: private SecurityService securityService;
075:
076: public EntityInstanceManager(EntityContainer container,
077: TransactionManager transactionManager,
078: SecurityService securityService, int poolSize) {
079: this .transactionManager = transactionManager;
080: this .securityService = securityService;
081: this .poolsize = poolSize;
082: this .container = container;
083: poolMap = new HashMap<Object, LinkedListStack>();// put size in later
084:
085: DeploymentInfo[] deploymentInfos = this .container.deployments();
086: for (int i = 0; i < deploymentInfos.length; i++) {
087: DeploymentInfo deploymentInfo = deploymentInfos[i];
088: deploy(deploymentInfo);
089: }
090: }
091:
092: public void deploy(DeploymentInfo deploymentInfo) {
093: poolMap.put(deploymentInfo.getDeploymentID(),
094: new LinkedListStack(poolsize / 2));
095: }
096:
097: public void undeploy(DeploymentInfo deploymentInfo) {
098: poolMap.remove(deploymentInfo.getDeploymentID());
099: }
100:
101: public EntityBean obtainInstance(ThreadContext callContext)
102: throws OpenEJBException {
103: Transaction currentTx;
104: try {
105: currentTx = getTransactionManager().getTransaction();
106: } catch (javax.transaction.SystemException se) {
107: logger.error(
108: "Transaction Manager getTransaction() failed.", se);
109: throw new SystemException("TransactionManager failure", se);
110: }
111:
112: Object primaryKey = callContext.getPrimaryKey();// null if its a servicing a home methods (create, find, ejbHome)
113: if (currentTx != null && primaryKey != null) {// primkey is null if create operation is called
114: CoreDeploymentInfo deploymentInfo = callContext
115: .getDeploymentInfo();
116: Key key = new Key(currentTx, deploymentInfo
117: .getDeploymentID(), primaryKey);
118: SyncronizationWrapper wrapper = txReadyPool.get(key);
119:
120: if (wrapper != null) {// if true, the requested bean instance is already enrolled in a transaction
121:
122: if (!wrapper.isAssociated()) {// is NOT associated
123: /*
124: * If the bean identity was removed (via ejbRemove()) within the same transaction,
125: * then it's SynchronizationWrapper will be in the txReady pool but marked as disassociated.
126: * This allows us to prevent a condition where the caller removes the bean and then attempts to
127: * call a business method on that bean within the same transaction. After a bean is removed any
128: * subsequent invocations on that bean with the same transaction should throw a NoSuchEntityException.
129: * its likely that the application server would have already made the reference invalid, but this bit of
130: * code is an extra precaution.
131: */
132: throw new InvalidateReferenceException(
133: new NoSuchObjectException(
134: "Entity not found: " + primaryKey));
135: } else if (callContext.getCurrentOperation() == Operation.REMOVE) {
136: /*
137: * To avoid calling ejbStore( ) on a bean that after its removed, we can not delegate
138: * the wrapper is marked as disassociated from the transaction to avoid processing the
139: * beforeCompletion( ) method on the SynchronizationWrapper object.
140: */
141: wrapper.disassociate();
142: }
143:
144: if (wrapper.isAvailable()
145: || wrapper.primaryKey.equals(primaryKey)) {
146: return wrapper.getEntityBean();
147: } else {
148:
149: // If the bean is declared as reentrant then the instance may be accessed
150: // by more then one thread at a time. This is one of the reasons that reentrancy
151: // is bad. In this case beans must be programmed to be multi threaded. The other reason
152: // reentrancy is bad has to do with transaction isolation. Multiple instances writing to
153: // the same database records will inevitably cancel out previous writes within the same tx.
154: //
155: // In the future we may change this to return a new instance of the bean and to
156: // link it and its wrapper to the original wrapper, but for now we choose this strategy because
157: // its simpler to implement.
158: return wrapper.getEntityBean();
159: }
160: } else {
161: /*
162: * If no synchronized wrapper for the key exists
163: * Then the bean entity is being access by this transaction for the first time,
164: * so it needs to be enrolled in the transaction.
165: */
166: EntityBean bean = getPooledInstance(callContext);
167: wrapper = new SyncronizationWrapper(callContext
168: .getDeploymentInfo(), primaryKey, bean, false,
169: key);
170:
171: if (callContext.getCurrentOperation() == Operation.REMOVE) {
172: /*
173: * To avoid calling ejbStore( ) on a bean that after its removed, we can not delegate
174: * the wrapper is marked as disassociated from the transaction to avoid processing the
175: * beforeCompletion( ) method on the SynchronizationWrapper object.
176: *
177: * We have to still use a wrapper so we can detect when a business method is called after
178: * a ejbRemove() and act to prevent it from being processed.
179: */
180: wrapper.disassociate();
181: }
182:
183: try {
184: currentTx.registerSynchronization(wrapper);
185: } catch (javax.transaction.SystemException e) {
186: logger
187: .error(
188: "Transaction Manager registerSynchronization() failed.",
189: e);
190: throw new SystemException(e);
191: } catch (RollbackException e) {
192: throw new ApplicationException(
193: new TransactionRolledbackException(e));
194: }
195: loadingBean(bean, callContext);
196: Operation orginalOperation = callContext
197: .getCurrentOperation();
198: callContext.setCurrentOperation(Operation.LOAD);
199: try {
200: bean.ejbLoad();
201: } catch (NoSuchEntityException e) {
202: throw new InvalidateReferenceException(
203: new NoSuchObjectException(
204: "Entity not found: " + primaryKey)
205: .initCause(e));
206: } catch (Exception e) {
207: logger.error(
208: "Exception encountered during ejbLoad():",
209: e);
210:
211: throw new OpenEJBException(e);
212: } finally {
213: callContext.setCurrentOperation(orginalOperation);
214: callContext.setCurrentAllowedStates(EntityContext
215: .getStates());
216: }
217: txReadyPool.put(key, wrapper);
218:
219: return bean;
220: }
221: } else {
222: // If no transaction is associated with the thread or if its a create, find or home method
223: // (primaryKey == null), then no synchronized wrapper is needed. if bean instance is used
224: // for a create method then a syncrhonziation wrapper may be assigned when the bean is
225: // returned to the pool -- depending on if the tx is a client initiated or container initiated.
226: return getPooledInstance(callContext);
227: }
228: }
229:
230: protected void loadingBean(EntityBean bean,
231: ThreadContext callContext) throws OpenEJBException {
232: }
233:
234: protected void reusingBean(EntityBean bean,
235: ThreadContext callContext) throws OpenEJBException {
236: }
237:
238: protected EntityBean getPooledInstance(ThreadContext callContext)
239: throws OpenEJBException {
240: CoreDeploymentInfo deploymentInfo = callContext
241: .getDeploymentInfo();
242: Stack methodReadyPool = poolMap.get(deploymentInfo
243: .getDeploymentID());
244: if (methodReadyPool == null)
245: throw new SystemException("Invalid deployment id "
246: + deploymentInfo.getDeploymentID()
247: + " for this container");
248:
249: EntityBean bean = (EntityBean) methodReadyPool.pop();
250: if (bean == null) {
251: try {
252: bean = (EntityBean) deploymentInfo.getBeanClass()
253: .newInstance();
254: } catch (Exception e) {
255: logger.error("Bean instantiation failed for class "
256: + deploymentInfo.getBeanClass(), e);
257: throw new SystemException(e);
258: }
259:
260: Operation currentOp = callContext.getCurrentOperation();
261: callContext.setCurrentOperation(Operation.SET_CONTEXT);
262: BaseContext.State[] originalStates = callContext
263: .setCurrentAllowedStates(EntityContext.getStates());
264:
265: try {
266: /*
267: * setEntityContext executes in an unspecified transactional context. In this case we choose to
268: * allow it to have what every transaction context is current. Better then suspending it
269: * unnecessarily.
270: *
271: * We also chose not to invoke EntityContainer.invoke( ) method, which duplicate the exception handling
272: * logic but also attempt to manage the begining and end of a transaction. It its a container managed transaciton
273: * we don't want the TransactionScopeHandler commiting the transaction in afterInvoke() which is what it would attempt
274: * to do.
275: */
276: bean.setEntityContext(createEntityContext());
277: } catch (Exception e) {
278: /*
279: * The EJB 1.1 specification does not specify how exceptions thrown by setEntityContext impact the
280: * transaction, if there is one. In this case we choose the least disruptive operation, throwing an
281: * application exception and NOT automatically marking the transaciton for rollback.
282: */
283: logger.error("Bean callback method failed ", e);
284: throw new ApplicationException(e);
285: } finally {
286: callContext.setCurrentOperation(currentOp);
287: callContext.setCurrentAllowedStates(originalStates);
288: }
289: } else {
290: reusingBean(bean, callContext);
291: }
292:
293: if ((callContext.getCurrentOperation() == Operation.BUSINESS)
294: || (callContext.getCurrentOperation() == Operation.REMOVE)) {
295: /*
296: * When a bean is retrieved from the bean pool to service a client's business method request it must be
297: * notified that its about to enter service by invoking its ejbActivate( ) method. A bean instance
298: * does not have its ejbActivate() invoked when:
299: * 1. Its being retreived to service an ejbCreate()/ejbPostCreate().
300: * 2. Its being retrieved to service an ejbFind method.
301: * 3. Its being retrieved to service an ejbRemove() method.
302: * See section 9.1.4 of the EJB 1.1 specification.
303: */
304: Operation currentOp = callContext.getCurrentOperation();
305:
306: callContext.setCurrentOperation(Operation.ACTIVATE);
307: BaseContext.State[] originalStates = callContext
308: .setCurrentAllowedStates(EntityContext.getStates());
309: try {
310: /*
311: In the event of an exception, OpenEJB is required to log the exception, evict the instance,
312: and mark the transaction for rollback. If there is a transaction to rollback, then the a
313: javax.transaction.TransactionRolledbackException must be throw to the client.
314: See EJB 1.1 specification, section 12.3.2
315: */
316: bean.ejbActivate();
317: } catch (Throwable e) {
318: logger
319: .error(
320: "Encountered exception during call to ejbActivate()",
321: e);
322: try {
323: Transaction tx = getTransactionManager()
324: .getTransaction();
325: if (tx != null) {
326: tx.setRollbackOnly();
327: throw new ApplicationException(
328: new TransactionRolledbackException(
329: "Reflection exception thrown while attempting to call ejbActivate() on the instance",
330: e));
331: }
332: } catch (javax.transaction.SystemException se) {
333: logger
334: .error(
335: "Transaction Manager getTransaction() failed.",
336: se);
337: throw new SystemException(se);
338: }
339: throw new ApplicationException(
340: new RemoteException(
341: "Exception thrown while attempting to call ejbActivate() on the instance. Exception message = "
342: + e.getMessage(), e));
343: } finally {
344: callContext.setCurrentOperation(currentOp);
345: callContext.setCurrentAllowedStates(originalStates);
346: }
347:
348: }
349: return bean;
350: }
351:
352: private EntityContext createEntityContext() {
353: return new EntityContext(transactionManager, securityService);
354: }
355:
356: public void poolInstance(ThreadContext callContext,
357: EntityBean bean, Object primaryKey) throws OpenEJBException {
358: if (bean == null) {
359: return;
360: }
361: Transaction currentTx = null;
362: try {
363: currentTx = getTransactionManager().getTransaction();
364: } catch (javax.transaction.SystemException se) {
365: logger.error(
366: "Transaction Manager getTransaction() failed.", se);
367: throw new SystemException("TransactionManager failure", se);
368: }
369: if (currentTx != null && primaryKey != null) {// primary key is null for find and home methods
370: Key key = new Key(currentTx, callContext
371: .getDeploymentInfo().getDeploymentID(), primaryKey);
372: SyncronizationWrapper wrapper = txReadyPool.get(key);
373: if (wrapper != null) {
374: if (callContext.getCurrentOperation() == Operation.REMOVE) {
375: /*
376: * The bean is being returned to the pool after it has been removed. Its
377: * important at this point to mark the bean as disassociated to prevent
378: * it's ejbStore method from bean called (see SynchronizationWrapper.beforeCompletion() method)
379: * and that subsequent methods can not be invoked on the bean identity (see obtainInstance() method).
380: */
381: wrapper.disassociate();
382: /*
383: * If the bean has been removed then the bean instance is no longer needed and can return to the methodReadyPool
384: * to service another identity.
385: */
386: Stack methodReadyPool = poolMap.get(callContext
387: .getDeploymentInfo().getDeploymentID());
388: methodReadyPool.push(bean);
389: } else {
390: if (callContext.getCurrentOperation() == Operation.CREATE) {
391: // Bean is being recreated (new-delete-new) so we need to reassociate it
392: wrapper.associate();
393: }
394: wrapper.setEntityBean(bean);
395: }
396: } else {
397: /*
398: A wrapper will not exist if the bean is being returned after a create operation.
399: In this case the transaction scope is broader then the create method itself; its a client
400: initiated transaction, so the bean must be registered with the tranaction and moved to the
401: tx ready pool
402: */
403:
404: wrapper = new SyncronizationWrapper(callContext
405: .getDeploymentInfo(), primaryKey, bean, true,
406: key);
407:
408: try {
409: currentTx.registerSynchronization(wrapper);
410: } catch (javax.transaction.SystemException se) {
411: logger
412: .error(
413: "Transaction Manager registerSynchronization() failed.",
414: se);
415: throw new SystemException(se);
416: } catch (RollbackException re) {
417: throw new ApplicationException(
418: new TransactionRolledbackException(re));
419: }
420:
421: txReadyPool.put(key, wrapper);
422: }
423: } else {
424: /*
425: If there is no transaction associated with the thread OR if the operation was a find or home method (PrimaryKey == null)
426: Then the bean instance is simply returned to the methodReady pool
427: */
428:
429: if (primaryKey != null
430: && callContext.getCurrentOperation() != Operation.REMOVE) {
431: /*
432: * If the bean has a primary key; And its not being returned following a remove operation;
433: * then the bean is being returned to the method ready pool after successfully executing a business method or create
434: * method. In this case we need to call the bean instance's ejbPassivate before returning it to the pool per EJB 1.1
435: * Section 9.1.
436: */
437: Operation currentOp = callContext.getCurrentOperation();
438:
439: callContext.setCurrentOperation(Operation.PASSIVATE);
440: BaseContext.State[] originalStates = callContext
441: .setCurrentAllowedStates(EntityContext
442: .getStates());
443:
444: try {
445: /*
446: In the event of an exception, OpenEJB is required to log the exception, evict the instance,
447: and mark the transaction for rollback. If there is a transaction to rollback, then the a
448: javax.transaction.TransactionRolledbackException must be throw to the client.
449: See EJB 1.1 specification, section 12.3.2
450: */
451: bean.ejbPassivate();
452: } catch (Throwable e) {
453: try {
454: Transaction tx = getTransactionManager()
455: .getTransaction();
456: if (tx != null) {
457: tx.setRollbackOnly();
458: throw new ApplicationException(
459: new TransactionRolledbackException(
460: "Reflection exception thrown while attempting to call ejbPassivate() on the instance",
461: e));
462: }
463: } catch (javax.transaction.SystemException se) {
464: logger
465: .error(
466: "Transaction Manager getTransaction() failed.",
467: se);
468: throw new SystemException(se);
469: }
470: throw new ApplicationException(
471: new RemoteException(
472: "Reflection exception thrown while attempting to call ejbPassivate() on the instance. Exception message = "
473: + e.getMessage(), e));
474: } finally {
475: callContext.setCurrentOperation(currentOp);
476: callContext.setCurrentAllowedStates(originalStates);
477: }
478: }
479:
480: /*
481: * The bean is returned to the method ready pool if its returned after servicing a find, ejbHome, business or create
482: * method and is not still part of a tx. While in the method ready pool the bean instance is not associated with a
483: * primary key and may be used to service a request for any bean of the same class.
484: */
485: Stack methodReadyPool = poolMap.get(callContext
486: .getDeploymentInfo().getDeploymentID());
487: methodReadyPool.push(bean);
488: }
489:
490: }
491:
492: public void freeInstance(ThreadContext callContext, EntityBean bean)
493: throws SystemException {
494:
495: discardInstance(callContext, bean);
496:
497: Operation currentOp = callContext.getCurrentOperation();
498: callContext.setCurrentOperation(Operation.UNSET_CONTEXT);
499: BaseContext.State[] originalStates = callContext
500: .setCurrentAllowedStates(EntityContext.getStates());
501:
502: try {
503: /*
504: * unsetEntityContext executes in an unspecified transactional context. In this case we choose to
505: * allow it to have what every transaction context is current. Better then suspending it
506: * unnecessarily.
507: *
508: * We also chose not to invoke EntityContainer.invoke( ) method, which duplicate the exception handling
509: * logic but also attempt to manage the begining and end of a transaction. It its a container managed transaciton
510: * we don't want the TransactionScopeHandler commiting the transaction in afterInvoke() which is what it would attempt
511: * to do.
512: */
513: bean.unsetEntityContext();
514: } catch (Exception e) {
515: /*
516: * The EJB 1.1 specification does not specify how exceptions thrown by unsetEntityContext impact the
517: * transaction, if there is one. In this case we choose to do nothing since the instance is being disposed
518: * of anyway.
519: */
520:
521: logger.info(getClass().getName()
522: + ".freeInstance: ignoring exception " + e
523: + " on bean instance " + bean);
524: } finally {
525: callContext.setCurrentOperation(currentOp);
526: callContext.setCurrentAllowedStates(originalStates);
527: }
528:
529: }
530:
531: public void discardInstance(ThreadContext callContext,
532: EntityBean bean) throws SystemException {
533: Transaction currentTx = null;
534: try {
535: currentTx = getTransactionManager().getTransaction();
536: } catch (javax.transaction.SystemException se) {
537: logger.error(
538: "Transaction Manager getTransaction() failed.", se);
539: throw new SystemException("TransactionManager failure", se);
540: }
541: if (currentTx != null) {
542: if (callContext.getPrimaryKey() == null)
543: return;
544:
545: Key key = new Key(currentTx, callContext
546: .getDeploymentInfo().getDeploymentID(), callContext
547: .getPrimaryKey());
548:
549: /*
550: The wrapper is removed (if pooled) so that it can not be accessed again. This is
551: especially important in the obtainInstance( ) method where a disassociated wrapper
552: in the txReadyPool is indicative of an entity bean that has been removed via
553: ejbRemove() rather than freed because of an error condition as is the case here.
554: */
555: SyncronizationWrapper wrapper = txReadyPool.remove(key);
556:
557: if (wrapper != null) {
558: /*
559: It's not possible to deregister a wrapper with the transaction,
560: but it can be removed from the tx pool and made inoperative by
561: calling its disassociate method. The wrapper will be returned to the
562: wrapper pool after the transaction completes
563: (see SynchronizationWrapper.afterCompletion( ) method). The wrapper must
564: be returned after the transaction completes so that it is not in the service
565: of another bean when the TransactionManager calls its Synchronization methods.
566:
567: In addition, the bean instance is dereferenced so it can be garbage
568: collected.
569: */
570: wrapper.disassociate();
571: }
572: }
573: }
574:
575: private TransactionManager getTransactionManager() {
576: return transactionManager;
577: }
578:
579: /*
580: * Instances of this class are used as keys for storing bean instances in the tx method
581: * ready pool. A compound key composed of the transaction, primary key, and deployment id
582: * identifiers is required to uniquely identify a bean in the tx method ready pool.
583: */
584: public static class Key {
585: private final Object deploymentID;
586: private final Object primaryKey;
587: private final Transaction transaction;
588:
589: public Key(Transaction tx, Object depID, Object prKey) {
590: if (tx == null || depID == null || prKey == null) {
591: throw new IllegalArgumentException();
592: }
593: transaction = tx;
594: deploymentID = depID;
595: primaryKey = prKey;
596: }
597:
598: public Object getPK() {
599: return primaryKey;
600: }
601:
602: public int hashCode() {
603: return transaction.hashCode() ^ deploymentID.hashCode()
604: ^ primaryKey.hashCode();
605: }
606:
607: public boolean equals(Object other) {
608: if (other != null
609: && other.getClass() == EntityInstanceManager.Key.class) {
610: Key otherKey = (Key) other;
611: if (otherKey.transaction.equals(transaction)
612: && otherKey.deploymentID.equals(deploymentID)
613: && otherKey.primaryKey.equals(primaryKey))
614: return true;
615: }
616: return false;
617: }
618: }
619:
620: /*
621: * Instances of this class are used to wrap entity instances so that they can be registered
622: * with a tx. When the Synchronization.beforeCompletion is called, the bean's ejbStore method
623: * is invoked. When the Synchroniztion.afterCompletion is called, the bean instance is returned
624: * to the method ready pool. Instances of this class are not recycled anymore, because modern VMs
625: * (JDK1.3 and above) perform better for objects that are short lived.
626: */
627: protected class SyncronizationWrapper implements Synchronization {
628: private EntityBean bean;
629: /*
630: * <tt>isAvailable<tt> determines if the wrapper is still associated with a bean. If the bean identity is removed (ejbRemove)
631: * or if the bean instance is discarded, the wrapper will not longer be associated with a bean instances
632: * and therefore its beforeCompletion method will not process the ejbStore method.
633: */
634: private boolean available;
635: private boolean associated;
636: private final Key readyPoolIndex;
637: private final CoreDeploymentInfo deploymentInfo;
638: private final Object primaryKey;
639:
640: public SyncronizationWrapper(CoreDeploymentInfo deploymentInfo,
641: Object primaryKey, EntityBean bean, boolean available,
642: Key readyPoolIndex) {
643: if (bean == null)
644: throw new IllegalArgumentException("bean is null");
645: if (readyPoolIndex == null)
646: throw new IllegalArgumentException("key is null");
647: if (deploymentInfo == null)
648: throw new IllegalArgumentException(
649: "deploymentInfo is null");
650: if (primaryKey == null)
651: throw new IllegalArgumentException("primaryKey is null");
652:
653: this .deploymentInfo = deploymentInfo;
654: this .bean = bean;
655: this .primaryKey = primaryKey;
656: this .available = available;
657: this .readyPoolIndex = readyPoolIndex;
658: associated = true;
659: }
660:
661: public void associate() {
662: associated = true;
663: }
664:
665: public void disassociate() {
666: associated = false;
667: }
668:
669: public boolean isAssociated() {
670: return associated;
671: }
672:
673: public synchronized boolean isAvailable() {
674: return available;
675: }
676:
677: public synchronized void setEntityBean(EntityBean ebean) {
678: available = true;
679: bean = ebean;
680: }
681:
682: public synchronized EntityBean getEntityBean() {
683: available = false;
684: return bean;
685: }
686:
687: public void beforeCompletion() {
688: if (associated) {
689: EntityBean bean;
690: synchronized (this ) {
691: bean = this .bean;
692: }
693:
694: ThreadContext callContext = new ThreadContext(
695: deploymentInfo, primaryKey);
696: callContext.setCurrentOperation(Operation.STORE);
697: callContext.setCurrentAllowedStates(EntityContext
698: .getStates());
699:
700: ThreadContext oldCallContext = ThreadContext
701: .enter(callContext);
702:
703: try {
704: bean.ejbStore();
705: } catch (Exception re) {
706: logger.error("Exception occured during ejbStore()",
707: re);
708: TransactionManager transactionManager = getTransactionManager();
709: try {
710: transactionManager.setRollbackOnly();
711: } catch (javax.transaction.SystemException se) {
712: logger
713: .error(
714: "Transaction manager reported error during setRollbackOnly()",
715: se);
716: }
717:
718: } finally {
719: ThreadContext.exit(oldCallContext);
720: }
721: }
722: }
723:
724: public void afterCompletion(int status) {
725: txReadyPool.remove(readyPoolIndex);
726: }
727: }
728: }
|