KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * JOnAS: Java(TM) Open Application Server
3  * Copyright (C) 1999-2004 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: JStatefulSwitch.java,v 1.22 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 import java.util.ArrayList JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34
35 import javax.ejb.EJBException JavaDoc;
36 import javax.ejb.NoSuchObjectLocalException JavaDoc;
37 import javax.ejb.RemoveException JavaDoc;
38 import javax.ejb.SessionSynchronization JavaDoc;
39 import javax.ejb.TransactionRolledbackLocalException JavaDoc;
40 import javax.transaction.InvalidTransactionException JavaDoc;
41 import javax.transaction.RollbackException JavaDoc;
42 import javax.transaction.SystemException JavaDoc;
43 import javax.transaction.Transaction JavaDoc;
44
45 import org.objectweb.transaction.jta.ResourceManagerEvent;
46 import org.objectweb.util.monolog.api.BasicLevel;
47
48 /**
49  * JStatefulSwitch is the implementation of JSessionSwitch dedicated to the
50  * Stateful Session Bean.
51  * @author Philippe Durieux
52  */

53 public class JStatefulSwitch extends JSessionSwitch {
54
55     private int sessionId;
56
57     private JStatefulContext bctx = null;
58
59     private Transaction JavaDoc currTx = null;
60
61     private boolean mustCommit = false;
62
63     private boolean expired = false;
64
65     private Transaction JavaDoc beanTx = null; // only for bean managed tx
66

67     private long lastaccesstime;
68
69     private boolean passivated = false;
70
71     /**
72      * Saved connectionList for this instance. The real connectionList
73      * is maintained in a ThreadLocal variable in ThreadData.
74      * Used only for session stateful methods, if they keep
75      * connection along several calls, not always in same thread.
76      * This list must not be shared between all instances.
77      * This info is transient because JDBC connection have not to be
78      * saved on disk at passivation time : They should be closed in
79      * ejbPassivate().
80      */

81     private List JavaDoc connectionList = Collections.synchronizedList(new ArrayList JavaDoc());
82
83     /**
84      * constructor.
85      * @param bf The Bean Factory
86      * @param sid the unique statefulSession ident
87      */

88     public JStatefulSwitch(JStatefulFactory bf) throws RemoteException JavaDoc {
89         super(bf);
90         if (TraceEjb.isDebugIc()) {
91             TraceEjb.interp.log(BasicLevel.DEBUG, "");
92         }
93     }
94
95     public int getSessionId() {
96         return sessionId;
97     }
98
99     /**
100      * @return true if instance can be passivated
101      */

102     public boolean canPassivate() {
103         if (currTx != null || beanTx != null) {
104             TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Victim is busy");
105             return false;
106         }
107         return (! passivated);
108     }
109
110     /**
111      * @return true if instance has been passivated.
112      */

113     public boolean isPassivated() {
114         return passivated;
115     }
116
117     /**
118      * Passivate this instance
119      */

120     public boolean passivate() {
121         if (currTx != null || beanTx != null) {
122             TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Cannot passivate: busy");
123             return false;
124         }
125         TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Instance will be passivated");
126         passivated = ((JStatefulFactory)bf).passivateStateful(this);
127         if (passivated) {
128             // Disconnect instance so that it will be garbaged.
129
bctx.setInstance(null);
130         }
131         return passivated;
132     }
133
134     /**
135      * Activate this instance.
136      */

137     public void activate() {
138         TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Instance will be activated");
139         ((JStatefulFactory)bf).activateStateful(this);
140     }
141
142     /**
143      * Set the connection list associated to the current thread
144      * with the list associated to this stateful session.
145      */

146     public void pushConnectionList() {
147         if (TraceEjb.isDebugTx()) {
148             TraceEjb.tx.log(BasicLevel.DEBUG, "pushed connectionList =" + connectionList);
149         }
150         bf.getTransactionManager().pushThreadLocalRMEventList(connectionList);
151     }
152
153     /**
154      * save the current connectionList for future use (next preInvoke).
155      */

156     public void popConnectionList() {
157         connectionList = bf.getTransactionManager().popThreadLocalRMEventList();
158         if (TraceEjb.isDebugTx()) {
159             TraceEjb.tx.log(BasicLevel.DEBUG, "poped connectionList =" + connectionList);
160         }
161     }
162
163     /**
164      * Save the Connection List after a create method.
165      */

166     public void setConnectionList(List JavaDoc cl) {
167         connectionList = cl;
168         if (TraceEjb.isDebugTx()) {
169             TraceEjb.tx.log(BasicLevel.DEBUG, "init connectionList =" + connectionList);
170         }
171     }
172
173     /**
174      * enlist all connection of the list
175      */

176     public void enlistConnections(Transaction JavaDoc tx) {
177         if (tx != null && connectionList != null) {
178             try {
179                 for (Iterator JavaDoc it = connectionList.iterator(); it.hasNext();) {
180                     ResourceManagerEvent rme = (ResourceManagerEvent) it.next();
181                     if (rme != null) {
182                         rme.enlistConnection(tx);
183                     } else {
184                         TraceEjb.tx.log(BasicLevel.WARN, "Null ResourceManagerEvent in Connection List");
185                     }
186                 }
187             } catch (SystemException JavaDoc e) {
188                 TraceEjb.tx.log(BasicLevel.ERROR, "cannot enlist connection", e);
189             }
190         }
191         // push the connection list in case a tx is started by the bean
192
// or if the method close a connection previously enlisted.
193
pushConnectionList();
194     }
195
196     /**
197      * delist all connections of the list
198      */

199     public void delistConnections(Transaction JavaDoc tx) {
200         popConnectionList();
201     }
202
203     // ===============================================================
204
// TimerEventListener implementation
205
// ===============================================================
206

207     /**
208      * The session timeout has expired
209      * @param arg Not Used.
210      */

211     public synchronized void timeoutExpired(Object JavaDoc arg) {
212         if (TraceEjb.isVerbose()) {
213             TraceEjb.logger.log(BasicLevel.WARN, "stateful session timeout expired");
214         }
215         mytimer = null;
216         // Do not remove if still used in a transaction
217
if (currTx != null) {
218             expired = true;
219         } else {
220             if (bctx != null) {
221                 try {
222                     bctx.setRemoved();
223                 } catch (RemoteException JavaDoc e) {
224                     if (TraceEjb.isVerbose()) {
225                         TraceEjb.logger.log(BasicLevel.WARN, "timeout expired", e);
226                     }
227                 } catch (RemoveException JavaDoc e) {
228                     if (TraceEjb.isVerbose()) {
229                         TraceEjb.logger.log(BasicLevel.WARN, "timeout expired", e);
230                     }
231                 }
232             }
233             noLongerUsed();
234         }
235     }
236
237     // ===============================================================
238
// other public methods
239
// ===============================================================
240

241     /**
242      * @return the StatefulContext (for passivation)
243      */

244     public JStatefulContext getStatefulContext() {
245         if (sessionId == -1) {
246             throw new EJBException JavaDoc("This Session has been removed");
247         }
248         if (bctx == null) {
249             throw new EJBException JavaDoc("Already passivated");
250         }
251         return bctx;
252     }
253
254     /**
255      * At each business method, get a BeanContext to run it
256      * @param tx The Transaction Context
257      * @return The Session Context
258      */

259     public JSessionContext getICtx(Transaction JavaDoc tx) {
260         if (TraceEjb.isDebugIc()) {
261             TraceEjb.interp.log(BasicLevel.DEBUG, "");
262         }
263
264         // If session has been removed, we must throw
265
// NoSuchObject[Local]Exception
266
// to the caller.
267
if (sessionId == -1) {
268             throw new NoSuchObjectLocalException JavaDoc("This Session has been removed");
269         }
270
271         // reload Context if it was passivated
272
if (passivated) { // or bctx == null
273
activate();
274         }
275         // Check Transaction
276
checkTx(tx);
277
278         lastaccesstime = System.currentTimeMillis();
279         return bctx;
280     }
281
282     /**
283      * At each create, bind the Context to the transaction
284      * @param tx The current Transaction Context
285      * @param bctx The Context to bind
286      */

287     public void bindICtx(Transaction JavaDoc tx, JStatefulContext bctx) {
288         TraceEjb.interp.log(BasicLevel.DEBUG, "");
289         sessionId = ((JStatefulFactory)bf).getNewSessionId(this);
290         this.bctx = bctx;
291         bctx.initSessionContext(this);
292         // Check Transaction
293
checkTx(tx);
294         lastaccesstime = System.currentTimeMillis();
295     }
296
297     /**
298      * Release the Context after use.
299      * @param tx The current Transaction Context
300      */

301     public void releaseICtx(Transaction JavaDoc tx) {
302         TraceEjb.interp.log(BasicLevel.DEBUG, "");
303
304         // In case getICtx failed, bctx may be null.
305
if (bctx == null) {
306             return;
307         }
308
309         if (bctx.isMarkedRemoved()) {
310             stopTimer();
311             noLongerUsed();
312         }
313     }
314
315     /**
316      * Discard a context/instance at end of request. A problem occured on this
317      * instance and it must be discarded.
318      * @param tx - transaction associated to this context
319      */

320     public void discardICtx(Transaction JavaDoc tx) {
321         if (TraceEjb.isDebugIc()) {
322             TraceEjb.interp.log(BasicLevel.DEBUG, "");
323         }
324
325         // In case getICtx failed, bctx may be null.
326
if (bctx == null) {
327             return;
328         }
329
330         stopTimer();
331         noLongerUsed();
332     }
333
334     /**
335      * This Session is no longer used: - unexport Remote Object - return the
336      * Session in the pool
337      */

338     public void noLongerUsed() {
339         if (TraceEjb.isDebugIc()) {
340             TraceEjb.interp.log(BasicLevel.DEBUG, "");
341         }
342
343         // Unexport the EJBObject from the Orb
344
if (remote != null) {
345             try {
346                 remote.unexportObject();
347             } catch (NoSuchObjectException JavaDoc e) {
348                 TraceEjb.logger.log(BasicLevel.ERROR, "unexportObject failed", e);
349             }
350         }
351
352         // Forget transaction that could be uncommitted.
353
// Avoids to get it in another session bean later.
354
if (beanTx != null) {
355             TraceEjb.tx.log(BasicLevel.WARN, "transaction not ended. forget it");
356             beanTx = null;
357         }
358
359         // return the SessionSwitch in the pool.
360
// will be reused for another Session.
361
bf.removeEJB(this);
362
363         // Remove this object.
364
// Stateful session contexts are not pooled. (EJB spec.)
365
((JStatefulFactory)bf).removeStateful(sessionId);
366         bctx = null;
367         sessionId = -1;
368     }
369
370     /**
371      * End of Transaction
372      */

373     public void txCompleted() {
374         if (TraceEjb.isDebugIc()) {
375             TraceEjb.interp.log(BasicLevel.DEBUG, "");
376         }
377         currTx = null;
378         if (expired) {
379             timeoutExpired(null); // try again
380
}
381     }
382
383     /**
384      * This is used for remove on stateful session beans only.
385      * @return True if bean is participating in a client transaction
386      */

387     public boolean isInTransaction() {
388         return (currTx != null && !mustCommit);
389     }
390
391     /**
392      * set a flag to remember that the transaction must be committed
393      */

394     public void setMustCommit(boolean mc) {
395         mustCommit = mc;
396     }
397
398     /**
399      * Keep the bean opened transaction for later use in other methods. Stateful
400      * session bean may open a transaction and use it in other methods. This is
401      * called at postInvoke
402      */

403     public void saveBeanTx() {
404         if (bf.isTxBeanManaged()) {
405             if (TraceEjb.isDebugTx()) {
406                 TraceEjb.tx.log(BasicLevel.DEBUG, "");
407             }
408             try {
409                 beanTx = bf.getTransactionManager().suspend();
410             } catch (SystemException JavaDoc e) {
411                 TraceEjb.logger.log(BasicLevel.ERROR, "cannot suspend transaction:", e);
412             }
413         }
414     }
415
416     /**
417      * @return the last access time in milliseconds
418      */

419     public long getLastAccessTime() {
420         return lastaccesstime;
421     }
422
423     // ===============================================================
424
// private methods
425
// ===============================================================
426

427     /**
428      * Check Transaction This is called at preInvoke
429      * @param tx The current Transaction Context
430      * @throws EJBException
431      * @throws TransactionRolledbackLocalException
432      */

433     private void checkTx(Transaction JavaDoc tx) {
434
435         // No check if no Synchro, except for bean managed transaction.
436
if (bf.isSessionSynchro() == false) {
437             // Resume bean associated transaction.
438
// Stateful session bean may open a transaction and use it in other
439
// methods.
440
if (bf.isTxBeanManaged() && beanTx != null) {
441                 if (TraceEjb.isDebugTx()) {
442                     TraceEjb.tx.log(BasicLevel.DEBUG, "resuming Bean Managed Tx");
443                 }
444                 try {
445                     bf.getTransactionManager().resume(beanTx);
446                 } catch (SystemException JavaDoc e) {
447                     TraceEjb.logger.log(BasicLevel.ERROR, "cannot resume transaction", e);
448                 } catch (InvalidTransactionException JavaDoc e) {
449                     TraceEjb.logger.log(BasicLevel.ERROR, "Cannot resume transaction", e);
450                 }
451             } else {
452                 if (TraceEjb.isDebugTx()) {
453                     TraceEjb.tx.log(BasicLevel.DEBUG, "no checkTx");
454                 }
455             }
456             return;
457         }
458
459         if (tx == null) {
460             if (TraceEjb.isDebugTx()) {
461                 TraceEjb.tx.log(BasicLevel.DEBUG, "(No Tx)");
462             }
463             if (currTx != null) {
464                 // A synchronized session must be called in the same transaction
465
// between afterBegin and beforeCompletion calls
466
TraceEjb.logger.log(BasicLevel.ERROR, "synchronized session called outside transaction context");
467                 throw new EJBException JavaDoc("Synchronized session called outside transaction context");
468             }
469         } else {
470             if (TraceEjb.isDebugTx()) {
471                 TraceEjb.tx.log(BasicLevel.DEBUG, "");
472             }
473             if (currTx == null) {
474                 // A new transaction starts on this synchronized session
475
try {
476                     tx.registerSynchronization(bctx);
477                     SessionSynchronization JavaDoc ssbean = (SessionSynchronization JavaDoc) bctx.getInstance();
478                     ssbean.afterBegin();
479                 } catch (RollbackException JavaDoc e) {
480                     throw new TransactionRolledbackLocalException JavaDoc("Session rolled back");
481                 } catch (SystemException JavaDoc e) {
482                     throw new EJBException JavaDoc("checkTx error", e);
483                 } catch (RemoteException JavaDoc e) {
484                     throw new EJBException JavaDoc("checkTx error", e);
485                 }
486                 currTx = tx;
487             } else {
488                 // A synchronized session must be called in the same transaction
489
// between afterBegin and beforeCompletion calls
490
if (tx.equals(currTx) == false) {
491                     TraceEjb.logger.log(BasicLevel.ERROR, "synchronized session called in another transaction context");
492                     throw new EJBException JavaDoc("Synchronized session called in another transaction context");
493                 }
494             }
495         }
496     }
497 }
498
Popular Tags