KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > jonas_ejb > container > JEntitySwitch


1 /**
2  * JOnAS: Java(TM) Open Application Server
3  * Copyright (C) 1999 Bull S.A.
4  * Contact: jonas-team@objectweb.org
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19  * USA
20  *
21  * --------------------------------------------------------------------------
22  * $Id: JEntitySwitch.java,v 1.80 2005/07/13 06:29:45 durieuxp Exp $
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.jonas_ejb.container;
27
28 import java.rmi.NoSuchObjectException JavaDoc;
29 import java.rmi.RemoteException JavaDoc;
30
31 import javax.ejb.EJBException JavaDoc;
32 import javax.ejb.EntityBean JavaDoc;
33 import javax.ejb.NoSuchObjectLocalException JavaDoc;
34 import javax.ejb.ObjectNotFoundException JavaDoc;
35 import javax.ejb.TimedObject JavaDoc;
36 import javax.ejb.Timer JavaDoc;
37 import javax.ejb.TimerService JavaDoc;
38 import javax.transaction.Status JavaDoc;
39 import javax.transaction.SystemException JavaDoc;
40 import javax.transaction.Transaction JavaDoc;
41
42 import org.objectweb.jonas_ejb.deployment.api.EntityDesc;
43 import org.objectweb.jonas_ejb.deployment.api.MethodDesc;
44 import org.objectweb.jonas_timer.TraceTimer;
45
46 import org.objectweb.util.monolog.api.BasicLevel;
47
48 /**
49  * JEntitySwitch is used internally to synchronize accesses to the entity
50  * context and thus to the entity bean instance. All parts common to EJBObject
51  * and EJBLocalObject should be here. Different policies can be applied to
52  * manage context/instance pairs: - only 1 pair (container manages the
53  * transaction isolation) - 1 pair for each transaction (transaction isolation
54  * managed by DataBase) - 2 pairs (1 for transactional accesses, 1 for non
55  * transaction accesses)
56  * @author Philippe Durieux
57  * @author Philippe Coq
58  */

59 public abstract class JEntitySwitch {
60
61     /**
62      * The Factory for this bean
63      */

64     protected JEntityFactory bf;
65
66     /**
67      * The Primary Key for this bean instance.
68      */

69     protected Object JavaDoc pk = null;
70
71     /**
72      * The EJBLocalObject, or null if bean has no local interface.
73      */

74     protected JEntityLocal local = null;
75
76     /**
77      * The EJBObject, or null if bean has no remote interface.
78      */

79     protected JEntityRemote remote = null;
80
81     /**
82      * time in millisec. to keep objects in memory when not used.
83      */

84     protected long inactivityTimeout; // millisec.
85

86     /**
87      * shared=true if the bean can be modify outside this container.
88      */

89     protected boolean shared;
90
91     /**
92      * Lock policy used for this entity bean. Possible values are :
93      * <dl>
94      * <li>0 = LOCK_CONTAINER_READ_UNCOMMITTED (1 instance) </li>
95      * <li>1 = LOCK_CONTAINER_SERIALIZED (1 instance) </li>
96      * <li>2 = LOCK_CONTAINER_READ_COMMITTED (2 instances) </li>
97      * <li>3 = LOCK_DATABASE (n instances) </li>
98      * <li>4 = LOCK_READ_ONLY (1 instance) </li>
99      * </dl>
100      */

101     protected int lockpolicy;
102
103     /**
104      * True if a transaction is mandatory for all modifying methods. In this
105      * case, all methods outside tranction is considered as read-only.
106      */

107     protected boolean txUpdates;
108
109     /**
110      * The Timer Service
111      */

112     protected TimerService JavaDoc myTimerService = null;
113
114     /**
115      * nb of non transacted requests running
116      */

117     protected int countIH = 0;
118
119     /**
120      * nb of transacted requests running
121      */

122     protected int countIT = 0;
123
124     /**
125      * nb of threads waiting (synchronization)
126      */

127     protected int waiters = 0;
128
129     /**
130      * True if this instance may have been modified outside transactions. Avoids
131      * to put it twice in dirty list.
132      */

133     protected boolean inDirtyList = false;
134
135     /**
136      * True if instance has been modified by a Transaction. This means that
137      * reading outside transaction should reload it before use.
138      * Always false for policies CS and CRU (only 1 instance)
139      */

140     protected boolean mustReload = false;
141
142     /**
143      * True if a TX need this instance currently used outside tx When the last
144      * release is done, we must store this instance.
145      */

146     protected boolean mustStore = false;
147
148     /**
149      * True when context/instance has been discarded, to avoids that passivate
150      * store its state on storage.
151      */

152     protected boolean todiscard = false;
153
154     // transaction on which a Context has been registered.
155
// used only for policies CS and CRC
156
protected Transaction JavaDoc runningtx = null;
157
158     // transaction that has modified an instance
159
// used only for policies DB and CRU
160
protected Transaction JavaDoc writingtx = null;
161
162     /**
163      * last transaction blocked by synchronization. this is to do a very basic
164      * deadlock detection.
165      */

166     protected Transaction JavaDoc blockedtx = null;
167
168     protected boolean isremoved = false;
169
170     // ident is used mainly for debugging
171
protected static int counter = 0;
172
173     protected String JavaDoc ident="0000 ";
174
175     /**
176      * timestamp used to free objects when not used for a specified time.
177      */

178     protected long timestamp = System.currentTimeMillis();
179
180     private final static long FEW_MINUTS = 180000;
181
182     private static final int MAX_NB_RETRY = 2;
183
184     /**
185      * true if we can differ the registration at first write.
186      */

187     protected boolean lazyregister = false;
188
189     /**
190      * reentrant=true if a bean instance can be accessed concurrently
191      * in the same transaction, or outside transaction.
192      */

193     protected boolean reentrant;
194
195     abstract JEntityContext getContext4Tx(Transaction JavaDoc tx);
196
197     abstract void setContext4Tx(Transaction JavaDoc tx, JEntityContext ctx);
198
199     abstract void removeContext4Tx(Transaction JavaDoc tx);
200
201     abstract protected void initpolicy(JEntityFactory bf);
202
203     abstract public boolean passivateIH(boolean passivation);
204
205     abstract public void endIH();
206
207     abstract public void notifyWriting(Transaction JavaDoc tx, JEntityContext bctx);
208
209     /**
210      * empty constructor. Object is initialized via init() because it is
211      * implemented differently according to jorm mappers.
212      */

213     public JEntitySwitch() {
214     }
215
216     /**
217      * constructor. A new object is build when a new PK is known in the
218      * container, either when a new bean is created, or when a find occurs. For
219      * create(), PK is not known yet when this object is build.
220      * @param bf The Entity Factory
221      * @param pk The Primary Key
222      */

223     public void init(JEntityFactory bf, Object JavaDoc pk) {
224         this.bf = bf;
225         this.pk = pk;
226         isremoved = false;
227         if (pk == null) {
228             TraceEjb.logger.log(BasicLevel.ERROR, "Init Entity Switch with a null PK!");
229             throw new EJBException JavaDoc("Init Entity Switch with a null PK!");
230         }
231
232         shared = bf.isShared();
233         reentrant = bf.isReentrant();
234         inactivityTimeout = bf.getInactivityTimeout() * 1000;
235         initpolicy(bf);
236         if (TraceEjb.isDebugSynchro()) {
237             // generate new ident (debug)
238
ident = "<" + counter + ":" + pk + ">";
239         }
240         counter++;
241
242         countIH = 0;
243         countIT = 0;
244         waiters = 0;
245
246         // Create EJBObject if bean has a Remote Interface
247
if (bf.getHome() != null) {
248             try {
249                 remote = ((JEntityHome) bf.getHome()).createRemoteObject();
250                 remote.setEntitySwitch(this);
251             } catch (RemoteException JavaDoc e) {
252                 throw new EJBException JavaDoc("cannot create Remote Object", e);
253             }
254         }
255
256         // Create EJBLocalObject if bean has a Local Interface
257
if (bf.getLocalHome() != null) {
258             local = ((JEntityLocalHome) bf.getLocalHome()).createLocalObject();
259             local.setEntitySwitch(this);
260         }
261         // Set the timestamp to a big value because instance will be used.
262
// This solve a problem in case of findAll() for example :
263
// Avoids to discard instance just before it will be used.
264
if (inactivityTimeout > 0) {
265             timestamp = System.currentTimeMillis() + FEW_MINUTS;
266             if (TraceTimer.isDebug()) {
267                 TraceTimer.logger.log(BasicLevel.DEBUG, ident + timestamp);
268             }
269         }
270     }
271
272     /**
273      * @return the underlaying EJBLocalObject
274      */

275     public JEntityLocal getLocal() {
276         return local;
277     }
278
279     /**
280      * @return the underlaying EJBObject
281      */

282     public JEntityRemote getRemote() {
283         return remote;
284     }
285
286     /**
287      * Obtains the TimerService associated for this Entity Bean (one / pk)
288      * @return a JTimerService instance.
289      */

290     public TimerService JavaDoc getEntityTimerService() {
291         // In case this object is used for a finder method,
292
// pk should be null, and TimerService must not be retrieved.
293
if (pk == null) {
294             throw new java.lang.IllegalStateException JavaDoc();
295         }
296         if (myTimerService == null) {
297             // TODO : Check that instance implements TimedObject ?
298
myTimerService = new JTimerService(this);
299         }
300         return myTimerService;
301     }
302
303     /**
304      * Notify a timeout for this bean and this Pk
305      * @param timer timer whose expiration caused this notification.
306      */

307     public void notifyTimeout(Timer JavaDoc timer) {
308         if (TraceTimer.isDebug()) {
309             TraceTimer.logger.log(BasicLevel.DEBUG, ident);
310         }
311
312         boolean committed = false;
313         for (int nbretry = 0; ! committed && nbretry < MAX_NB_RETRY; nbretry++) {
314             RequestCtx rctx = bf.preInvoke(bf.getTimerTxAttribute());
315             try {
316                 JEntityContext bctx = getICtx(rctx.currTx, false);
317                 EntityBean JavaDoc eb = bctx.getInstance();
318                 bf.checkSecurity(null);
319                 if (eb instanceof TimedObject JavaDoc) {
320                     TimedObject JavaDoc instance = (TimedObject JavaDoc) eb;
321                     instance.ejbTimeout(timer);
322                 } else {
323                     throw new EJBException JavaDoc("The bean does not implement the `TimedObject` interface");
324                 }
325                 committed = (rctx.currTx == null) ||
326                 (rctx.currTx.getStatus() != Status.STATUS_MARKED_ROLLBACK);
327             } catch (EJBException JavaDoc e) {
328                 rctx.sysExc = e;
329                 throw e;
330             } catch (RuntimeException JavaDoc e) {
331                 rctx.sysExc = e;
332                 throw new EJBException JavaDoc("RuntimeException thrown by an enterprise Bean", e);
333             } catch (Error JavaDoc e) {
334                 rctx.sysExc = e;
335                 throw new EJBException JavaDoc("Error thrown by an enterprise Bean" + e);
336             } catch (RemoteException JavaDoc e) {
337                 rctx.sysExc = e;
338                 throw new EJBException JavaDoc("Remote Exception raised:", e);
339             } catch (SystemException JavaDoc e) {
340                 rctx.sysExc = e;
341                 throw new EJBException JavaDoc("Cannot get transaction status:", e);
342             } finally {
343                 try {
344                     bf.postInvoke(rctx);
345                 } finally {
346                     if (rctx.sysExc != null) {
347                         discardICtx(rctx.currTx);
348                     } else {
349                         releaseICtx(rctx.currTx);
350                     }
351                 }
352             }
353         }
354     }
355
356     /**
357      * @return the Primary Key Object for this instance.
358      */

359     public Object JavaDoc getPrimaryKey() {
360         if (pk == null) {
361             throw new java.lang.IllegalStateException JavaDoc();
362         }
363         return pk;
364     }
365
366     /**
367      * bind a JEntityContext for a create method.
368      * @param tx - the Transaction object
369      * @param bctx - the JEntityContext to bind
370      */

371     public void bindICtx(Transaction JavaDoc tx, JEntityContext bctx) {
372         mapICtx(tx, bctx, true, false, false);
373     }
374
375     /**
376      * Try to bind a JEntityContext if none already bound. Called by finder
377      * methods
378      * @param tx - the Transaction object
379      * @param bctx The Entity Context
380      * @return true if context has been bound to this EntitySwitch.
381      */

382     public synchronized boolean tryBindICtx(Transaction JavaDoc tx, JEntityContext bctx, boolean simple) throws ObjectNotFoundException JavaDoc {
383
384         // Don't bind if already a context.
385
if (getContext4Tx(tx) != null) {
386             if (TraceEjb.isDebugIc()) {
387                 TraceEjb.context.log(BasicLevel.DEBUG, ident + "context already mapped!");
388             }
389             if (getContext4Tx(tx).isMarkedRemoved()) {
390                 if (TraceEjb.isDebugIc()) {
391                     TraceEjb.context.log(BasicLevel.DEBUG, ident + " currently being removed");
392                 }
393                 if (simple) {
394                     throw new ObjectNotFoundException JavaDoc("Instance is currently being removed");
395                 } else {
396                     // Don't throw exception in case of finder multiple.
397
return false;
398                 }
399             }
400             // bctx will be released by caller. (See JEntityHome.vm)
401
return false;
402         }
403         
404         // No synchronization, since this is used for readonly methods (finder);
405
try {
406             bctx.initEntityContext(this);
407             bctx.activate(true);
408         } catch (Exception JavaDoc e) {
409             TraceEjb.synchro.log(BasicLevel.WARN, ident + "Cannot bind Ctx: " + e);
410             return false;
411         }
412         setContext4Tx(tx, bctx); // after activate (CRU => full parallellism)
413
if (!lazyregister) {
414             if (tx == null) {
415                 if (TraceEjb.isDebugSynchro()) {
416                     TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "IH find");
417                 }
418             } else {
419                 if (TraceEjb.isDebugSynchro()) {
420                     TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "IT find");
421                 }
422                 registerCtx(tx, bctx);
423             }
424         }
425         return true;
426     }
427
428     /**
429      * bind a JEntityContext for a remove method. called in case of remove(pk)
430      * or remove(handle)
431      * @param tx - the Transaction object
432      * @param newctx - the JEntityContext to bind
433      * @return the BeanContext
434      */

435     public JEntityContext getICtx(Transaction JavaDoc tx, JEntityContext newctx) {
436         return mapICtx(tx, newctx, false, false, false);
437     }
438
439     /**
440      * Get a context/instance associated with this transaction Called at each
441      * request on the bean (including remove)
442      * @param tx - the Transaction object
443      * @return the BeanContext
444      */

445     public JEntityContext getICtx(Transaction JavaDoc tx, boolean checkr) {
446         return mapICtx(tx, null, false, true, checkr);
447     }
448
449     /**
450      * Release completely this object, since another one will be used.
451      * this occurs in case of create, when another EntitySwitch exist
452      * already.
453      * @param tx - the Transaction object
454      */

455     public synchronized boolean terminate(Transaction JavaDoc tx) {
456         if (TraceEjb.isDebugSwapper()) {
457             TraceEjb.swapper.log(BasicLevel.DEBUG, ident);
458         }
459         waitmyturn(tx);
460         JEntityContext jec = getContext4Tx(tx);
461         if (jec != null) {
462             if (todiscard || jec.isMarkedRemoved()) {
463                 discardContext(tx, true, true);
464             } else {
465                 try {
466                     jec.storeIfModified();
467                 } catch (Exception JavaDoc e) {
468                     TraceEjb.logger.log(BasicLevel.ERROR, ident, "error while storing bean state:", e);
469                 }
470                 jec.passivate();
471                 discardContext(tx, false, true);
472             }
473             if (waiters > 0) {
474                 if (TraceEjb.isDebugSynchro()) {
475                     TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " notify");
476                 }
477                 notifyAll();
478             }
479         }
480         return true;
481     }
482
483     abstract void waitmyturn(Transaction JavaDoc tx);
484
485     /**
486      * Map a context and its instance.
487      * @param tx - the Transaction object
488      * @param bctx - the JEntityContext to bind if not null
489      * @param forced - force to take this context. (case of create)
490      * @param holdit - increment count to hold it, a release will be called
491      * later.
492      * @return JEntityContext actually mapped
493      */

494     public synchronized JEntityContext mapICtx(Transaction JavaDoc tx, JEntityContext bctx, boolean forced, boolean holdit, boolean checkreentrance) {
495
496         try {
497             // DEBUG only
498
if (bf == null) {
499                 TraceEjb.synchro.log(BasicLevel.ERROR, "JEntitySwitch not initialized!");
500                 throw new EJBException JavaDoc("JEntitySwitch not initialized");
501             }
502             if (TraceEjb.isDebugSynchro()) {
503                 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " tx=" + tx);
504             }
505
506             // Check non-reentrance (See spec EJB 2.1 section 12.1.13)
507
if (!reentrant && checkreentrance) {
508                 if (runningtx != null && countIT > 0 && tx != null && tx.equals(runningtx)) {
509                     throw new EJBException JavaDoc("non-reentrant bean accessed twice in same transaction");
510                 }
511                 if (tx == null && countIH > 0) {
512                     throw new EJBException JavaDoc("non-reentrant bean accessed twice outside transaction");
513                 }
514             }
515             // synchro (lock-policy dependant)
516
waitmyturn(tx);
517
518             // Set the timestamp to a big value because instance will be used.
519
if (inactivityTimeout > 0) {
520                 timestamp = System.currentTimeMillis() + FEW_MINUTS;
521                 if (TraceTimer.isDebug()) {
522                     TraceTimer.logger.log(BasicLevel.DEBUG, ident + timestamp);
523                 }
524             }
525
526             // Choose the context to use.
527
boolean newtrans = false;
528             JEntityContext jec = getContext4Tx(tx);
529             if (forced) {
530                 // If the new context is enforced, we must first release the older
531
if (jec != null) {
532                     if (TraceEjb.isDebugContext()) {
533                         TraceEjb.context.log(BasicLevel.DEBUG, ident + "new context is enforced!");
534                     }
535                     discardContext(tx, false, true);
536                 }
537                 jec = bctx;
538                 setContext4Tx(tx, jec);
539                 jec.initEntityContext(this);
540                 newtrans = true;
541                 isremoved = false; // in case of create following a remove
542
} else {
543                 // First check if bean still exists
544
if (isremoved) {
545                     TraceEjb.logger.log(BasicLevel.WARN, ident + " has been removed.");
546                     throw new NoSuchObjectLocalException JavaDoc("Try to access a bean previously removed");
547                 }
548                 if (jec != null) {
549                     if (todiscard) {
550                         TraceEjb.logger.log(BasicLevel.WARN, ident + " has been discarded.");
551                         throw new NoSuchObjectLocalException JavaDoc("Try to access a bean previously discarded");
552                     }
553                     // Reuse the Context for this transaction.
554
// If a context was supplied, release it first.
555
if (bctx != null) {
556                         if (TraceEjb.isDebugContext()) {
557                             TraceEjb.context.log(BasicLevel.DEBUG, ident + "a context was supplied!");
558                         }
559                         bf.releaseJContext(bctx);
560                     }
561                     // In case the same instance is used for all tx, must check if
562
// new one. For LOCK_DATABASE policy, it cannot be a new tx.
563
if (runningtx == null && lockpolicy != EntityDesc.LOCK_DATABASE) {
564                         newtrans = true;
565                     }
566                     jec.reuseEntityContext(newtrans);
567                 } else {
568                     if (bctx != null) {
569                         jec = bctx;
570                     } else {
571                         // no Context available : get one from the pool.
572
jec = bf.getJContext(this);
573                     }
574                     jec.initEntityContext(this);
575                     jec.activate(true);
576                     setContext4Tx(tx, jec); // after activate
577
newtrans = true;
578                 }
579             }
580
581             if (tx != null) {
582                 // Register Context now, except if no new transaction
583
if (newtrans && !lazyregister) {
584                     try {
585                         registerCtx(tx, jec);
586                         if (TraceEjb.isDebugSynchro()) {
587                             TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "mapICtx IT: new tx, registerSynchronization");
588                         }
589                     } catch (IllegalStateException JavaDoc e) {
590                         if (TraceEjb.synchro.isLoggable(BasicLevel.WARN)) {
591                             TraceEjb.synchro.log(BasicLevel.WARN, ident + "mapICtx IT: not registered!", e);
592                         }
593                     }
594                 }
595             } else {
596                 if (holdit) {
597                     if (TraceEjb.isDebugSynchro()) {
598                         TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "mapICtx IH count=" + countIH);
599                     }
600                     if ((shared || mustReload) && countIH == 0) {
601                         // reload state that could have been modified by
602
// transactions.
603
jec.activate(false);
604                         mustReload = false;
605                     }
606                 }
607                 if (!inDirtyList && !txUpdates) {
608                     inDirtyList = true;
609                     bf.registerEJB(this);
610                 }
611             }
612
613             return jec;
614         } finally {
615             if (holdit) {
616                 if (tx == null) {
617                     countIH++;
618                 } else {
619                     countIT++;
620                 }
621             }
622         }
623     }
624
625     public Transaction JavaDoc getBlockedTx() {
626         Transaction JavaDoc tx = blockedtx;
627         if (tx != null && TraceEjb.isDebugSynchro()) {
628             TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "tx " + runningtx + " is blocking " + blockedtx);
629         }
630         return tx;
631     }
632
633     public Transaction JavaDoc getBlockingTx() {
634         Transaction JavaDoc tx = ( blockedtx != null ? runningtx : null);
635         if (tx != null && TraceEjb.isDebugSynchro()) {
636             TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "tx " + runningtx + " is blocking " + blockedtx);
637         }
638         return tx;
639     }
640
641     /**
642      * Release a context/instance at end of request.
643      * @param tx - transaction associated to this context
644      */

645     public synchronized void releaseICtx(Transaction JavaDoc tx) {
646
647         if (tx == null) {
648             // ------------- IH end -------------
649
countIH--;
650             if (TraceEjb.isDebugSynchro()) {
651                     TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "releaseICtx IH count=" + countIH);
652             }
653             if (countIH == 0) {
654                 JEntityContext jec = getContext4Tx(tx);
655                 if (jec == null) {
656                     TraceEjb.context.log(BasicLevel.ERROR, ident + " No context!");
657                     Thread.dumpStack();
658                     return;
659                 }
660                 if (todiscard || jec.isMarkedRemoved()) {
661                     // TODO why test todiscard here ?
662
discardContext(tx, true, true);
663                 } else {
664                     if (mustStore) {
665                         try {
666                             jec.storeIfModified();
667                         } catch (EJBException JavaDoc e) {
668                             if (TraceEjb.isVerbose()) {
669                                 TraceEjb.logger.log(BasicLevel.WARN, ident + " ejbexception", e);
670                             }
671                         }
672                         mustStore = false;
673                     }
674                 }
675                 if (waiters > 0) {
676                     if (TraceEjb.isDebugSynchro()) {
677                         TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " notify");
678                     }
679                     notifyAll();
680                 }
681             }
682         } else {
683             // ------------- IT end -------------
684
// nothing to do now. Wait for tx completed.
685
if (TraceEjb.isDebugSynchro()) {
686                 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + "releaseICtx IT");
687             }
688             countIT--;
689         }
690     }
691
692     /**
693      * Discard a context/instance at end of request. A problem occured on this
694      * insance and it must be discarded.
695      * @param tx - transaction associated to this context
696      */

697     public synchronized void discardICtx(Transaction JavaDoc tx) {
698         if (tx == null) {
699             countIH--;
700             if (countIH == 0) {
701                 discardContext(tx, false, false);
702                 return;
703             }
704         } else {
705             countIT--;
706             if (runningtx == null) {
707                 // The transaction has already been committed.
708
return;
709             }
710         }
711         // to be done later.
712
todiscard = true;
713     }
714
715     /**
716      * This transaction is now over. We can dispose of the instance for another
717      * transaction or discard it.
718      * @param tx the transaction object
719      * @param committed true if transaction was committed.
720      */

721     public synchronized void txCompleted(Transaction JavaDoc tx, boolean committed) {
722         JEntityContext jec = getContext4Tx(tx);
723         if (jec == null) {
724             TraceEjb.context.log(BasicLevel.ERROR, ident + " No context for this tx");
725             return;
726         }
727         runningtx = null;
728         if (writingtx != null && tx.equals(writingtx)) {
729             writingtx = null;
730         }
731
732         if (TraceEjb.isDebugSynchro()) {
733             TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " txCompleted " + committed);
734         }
735
736         // The create was eventually rolled back or instance has been discarded.
737
// We must remove PK entry.
738
if (todiscard || (jec.isNewInstance() && !committed)) {
739             discardContext(tx, !todiscard, false);
740             return;
741         }
742
743         if (jec.isMarkedRemoved()) {
744             if (TraceEjb.isDebugContext()) {
745                 TraceEjb.context.log(BasicLevel.DEBUG, ident + "remove!");
746             }
747             discardContext(tx, committed, true);
748         } else {
749             if (shared || !committed) {
750                 // TODO read-only policy: keep instance until a specified timeout.
751
// LOCK_DATABASE policy should go here (shared = true)
752
if (TraceEjb.isDebugContext()) {
753                     TraceEjb.context.log(BasicLevel.DEBUG, jec + " passivated!");
754                 }
755                 jec.passivate();
756                 if (committed) {
757                     // context is discarded if transaction rolled back
758
bf.releaseJContext(jec);
759                 }
760                 removeContext4Tx(tx);
761             } else {
762                 // do this before reallocating the Context for another tx
763
// (inside this lock)
764
jec.detachTx();
765             }
766             // If special instance for no transactional accesses, it must be
767
// reloaded
768
if (lockpolicy == EntityDesc.LOCK_CONTAINER_READ_COMMITTED || lockpolicy == EntityDesc.LOCK_DATABASE) {
769                 mustReload = true;
770             }
771         }
772         if (waiters > 0) {
773             if (TraceEjb.isDebugSynchro()) {
774                 TraceEjb.synchro.log(BasicLevel.DEBUG, ident + " notify");
775             }
776             notifyAll();
777         }
778     }
779
780     /**
781      * register a Context on the transaction, as a Synchronization. this will be
782      * used later to store instance state when needed : before a finder, or at
783      * beforeCompletion, and to release instance at commit.
784      * @param tx Transaction object
785      * @param bctx The Context to be registered
786      */

787     protected void registerCtx(Transaction JavaDoc tx, JEntityContext bctx) {
788         // DEBUG only
789
if (bf == null) {
790             TraceEjb.synchro.log(BasicLevel.ERROR, "JEntitySwitch not initialized!");
791             throw new EJBException JavaDoc("JEntitySwitch not initialized");
792         }
793         // register this context to the transaction, as a Synchronization.
794
try {
795             if (bf.registerContext(tx, bctx)) {
796                 if (TraceEjb.isDebugContext()) {
797                     TraceEjb.context.log(BasicLevel.DEBUG, bctx + "registered!");
798                 }
799                 bctx.setRunningTx(tx);
800                 runningtx = tx;
801             } else {
802                 TraceEjb.context.log(BasicLevel.WARN, bctx + "could not be registered!");
803             }
804         } catch (IllegalStateException JavaDoc e) {
805             TraceEjb.logger.log(BasicLevel.ERROR, ident + "Transaction is in an illegal state");
806             throw e;
807         }
808     }
809
810     /**
811      * Discard instance/Context and free all objects.
812      * @param tx - the Transaction object
813      * @param forgetpk - true if remove pk from the list
814      * @param pool - true if instance can be pooled.
815      */

816     protected void discardContext(Transaction JavaDoc tx, boolean forgetpk, boolean pool) {
817         if (TraceEjb.isDebugContext()) {
818             TraceEjb.context.log(BasicLevel.DEBUG, "");
819         }
820         JEntityContext jec = getContext4Tx(tx);
821         if (jec != null) {
822             if (pool) {
823                 bf.releaseJContext(jec);
824             }
825             removeContext4Tx(tx);
826         }
827         if (forgetpk) {
828             if (remote != null) {
829                 try {
830                     remote.unexportObject();
831                 } catch (NoSuchObjectException JavaDoc e) {
832                     TraceEjb.logger.log(BasicLevel.ERROR, ident + " unexport entity failed: ", e);
833                 }
834             }
835             bf.removeEJB(getPrimaryKey());
836
837             // Cancel all timers for this bean (= remove the TimerService)
838
if (myTimerService != null) {
839                 ((JTimerService) myTimerService).cancelAllTimers();
840                 myTimerService = null;
841             }
842             // this Entity switch must not be used any longer.
843
isremoved = true;
844         }
845         todiscard = false; // done.
846
}
847
848     /**
849      * @return lock policy for this bean
850      */

851     public int getPolicy() {
852         return lockpolicy;
853     }
854
855     /**
856      * @return State of this instance. State values are 0=in-tx, 1=out-tx, 2=idle,
857      * 3=passive, 4=removed. we don't synchronize this method to avoid
858      * jadmin blocks
859      */

860     abstract public int getState();
861     /**
862      * @return the JFactory
863      */

864     public JFactory getBeanFactory() {
865         return bf;
866     }
867
868 }
869
Popular Tags