KickJava   Java API By Example, From Geeks To Geeks.

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


1 /**
2  * JOnAS: Java(TM) Open Application Server
3  * Copyright (C) 1999-2005 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: JStatelessFactory.java,v 1.35.2.1 2005/08/11 15:30:47 benoitf Exp $
23  * --------------------------------------------------------------------------
24  */

25
26 package org.objectweb.jonas_ejb.container;
27
28 import java.lang.reflect.InvocationTargetException JavaDoc;
29 import java.rmi.RemoteException JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.ListIterator JavaDoc;
33
34 import javax.ejb.EJBException JavaDoc;
35 import javax.ejb.SessionBean JavaDoc;
36 import javax.ejb.TimedObject JavaDoc;
37 import javax.ejb.Timer JavaDoc;
38 import javax.ejb.TimerService JavaDoc;
39 import javax.naming.Context JavaDoc;
40 import javax.transaction.Status JavaDoc;
41 import javax.transaction.SystemException JavaDoc;
42
43 import org.objectweb.jonas_timer.TraceTimer;
44
45 import org.objectweb.jonas_ejb.deployment.api.SessionStatelessDesc;
46
47 import org.objectweb.util.monolog.api.BasicLevel;
48
49 /**
50  * This class is a factory for a Session Stateless Bean.
51  * @author Philippe Durieux
52  */

53 public class JStatelessFactory extends JSessionFactory {
54
55     /**
56      * instance pool management (list of available JSessionContext objects)
57      * Contexts are pooled only for Stateless Session beans because for Stateful
58      * sessions a newInstance() is required by the spec.
59      */

60     protected List JavaDoc bctxlist = new ArrayList JavaDoc();
61
62     // service endpoint home
63
protected JServiceEndpointHome sehome = null;
64
65     protected int instanceCount = 0;
66
67     // initial value for pool size
68
protected int minPoolSize = 0;
69
70     // nb max of instances in pool
71
protected int maxCacheSize = 0;
72
73     private static final int MAX_NB_RETRY = 2;
74
75
76     /**
77      * constructor
78      * @param dd Session Stateless Deployment Descriptor
79      * @param cnt Container where the bean is defined
80      */

81     public JStatelessFactory(SessionStatelessDesc dd, JContainer cont) {
82         super(dd, cont);
83         if (TraceEjb.isDebugIc()) {
84             TraceEjb.interp.log(BasicLevel.DEBUG, "");
85         }
86
87         // Create the ServiceEndpointHome if defined in DD
88
Class JavaDoc sehomeclass = null;
89         String JavaDoc clname = dd.getFullWrpSEHomeName();
90         if (clname != null) {
91             try {
92                 sehomeclass = cont.getClassLoader().loadClass(clname);
93             } catch (ClassNotFoundException JavaDoc e) {
94                 throw new EJBException JavaDoc(ejbname + " Cannot load " + clname, e);
95             }
96             if (TraceEjb.isDebugIc()) {
97                 TraceEjb.interp.log(BasicLevel.DEBUG, ejbname + ": " + clname + " loaded");
98             }
99             try {
100                 // new JServiceEndpointHome(dd, this)
101
int nbp = 2;
102                 Class JavaDoc[] ptype = new Class JavaDoc[nbp];
103                 Object JavaDoc[] pobj = new Object JavaDoc[nbp];
104                 ptype[0] = org.objectweb.jonas_ejb.deployment.api.SessionStatelessDesc.class;
105                 pobj[0] = (Object JavaDoc) dd;
106                 ptype[1] = org.objectweb.jonas_ejb.container.JStatelessFactory.class;
107                 pobj[1] = (Object JavaDoc) this;
108                 sehome = (JServiceEndpointHome) sehomeclass.getConstructor(ptype).newInstance(pobj);
109             } catch (Exception JavaDoc e) {
110                 throw new EJBException JavaDoc(ejbname + " Cannot create serviceEndpointHome ", e);
111             }
112             // register it in JNDI
113
try {
114                 sehome.register();
115             } catch (Exception JavaDoc e) {
116                 throw new EJBException JavaDoc(ejbname + " Cannot register serviceEndpointHome ", e);
117             }
118         }
119
120         isStateful = false;
121         maxCacheSize = dd.getCacheMax();
122         minPoolSize = dd.getPoolMin();
123         if (TraceEjb.isDebugSwapper()) {
124             TraceEjb.swapper.log(BasicLevel.DEBUG, " maxCacheSize = " + maxCacheSize);
125         }
126     }
127
128     /**
129      * Init pool of instances.
130      */

131     public void initInstancePool() {
132         // pre-allocate a set of JSessionContext (bean instances)
133
if (minPoolSize != 0) {
134             TraceEjb.interp.log(BasicLevel.INFO, "pre-allocate a set of " + minPoolSize
135                     + " stateless session instances");
136             JSessionSwitch ss = null;
137             try {
138                 ss = createNewSession();
139             } catch (RemoteException JavaDoc e) {
140                 TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot create new session", e);
141                 throw new EJBException JavaDoc(ejbname + " Cannot create session for pool: ", e);
142             }
143             Context JavaDoc bnctx = setComponentContext(); // for createNewInstance
144
// Set also the EJB classloader as context classloader
145
ClassLoader JavaDoc old = Thread.currentThread().getContextClassLoader();
146             Thread.currentThread().setContextClassLoader(this.myClassLoader());
147             try {
148                 synchronized (bctxlist) {
149                     for (int i = 0; i < minPoolSize; i++) {
150                         JSessionContext ctx = null;
151                         try {
152                             // must pass a session switch (for ejbCreate)
153
ctx = createNewInstance(ss);
154                             bctxlist.add(ctx);
155                         } catch (Exception JavaDoc e) {
156                             TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot create new instance", e);
157                             throw new EJBException JavaDoc(ejbname + " Cannot init pool of instances ", e);
158                         }
159                     }
160                 }
161             } finally {
162                 // reset the context classloader
163
Thread.currentThread().setContextClassLoader(old);
164
165                 // reset java:comp/env
166
resetComponentContext(bnctx);
167             }
168
169         }
170     }
171
172     public JServiceEndpointHome getSEHome() {
173         return sehome;
174     }
175
176     // ---------------------------------------------------------------
177
// BeanFactory implementation
178
// ---------------------------------------------------------------
179

180     /**
181      * @return the Instance pool size for this Ejb
182      */

183     public int getPoolSize() {
184         return bctxlist.size();
185     }
186
187     /**
188      * Reduce number of instances in memory in the free list we reduce to the
189      * minPoolSize
190      */

191     public void reduceCache() {
192         if (TraceEjb.isDebugSwapper()) {
193             TraceEjb.swapper.log(BasicLevel.DEBUG, "");
194         }
195         // reduce the pool to the minPoolSize
196
int poolsz = minPoolSize;
197         synchronized (bctxlist) {
198             if (TraceEjb.isDebugSwapper()) {
199                 TraceEjb.swapper.log(BasicLevel.DEBUG, "try to reduce " + bctxlist.size() + " to " + poolsz);
200             }
201             while (bctxlist.size() > poolsz) {
202                 ListIterator JavaDoc i = bctxlist.listIterator();
203                 if (i.hasNext()) {
204                     i.next();
205                     i.remove();
206                     instanceCount--;
207                 }
208             }
209         }
210         if (TraceEjb.isDebugSwapper()) {
211             TraceEjb.swapper.log(BasicLevel.DEBUG, "cacheSize= " + getCacheSize());
212         }
213
214     }
215
216     // ---------------------------------------------------------------
217
// other public methods
218
// ---------------------------------------------------------------
219

220     /**
221      * Obtains the TimerService associated for this Bean
222      * @return a JTimerService instance.
223      */

224     public TimerService JavaDoc getTimerService() {
225         if (myTimerService == null) {
226             // TODO : Check that instance implements TimedObject ?
227
myTimerService = new JTimerService(this);
228         }
229         return myTimerService;
230     }
231
232     /**
233      * Creates a new Session Stateless
234      * @return the new JSessionSwitch
235      */

236     public JSessionSwitch createNewSession() throws RemoteException JavaDoc {
237         if (TraceEjb.isDebugIc()) {
238             TraceEjb.interp.log(BasicLevel.DEBUG, "");
239         }
240         JStatelessSwitch bs = new JStatelessSwitch(this);
241         return bs;
242     }
243
244     /**
245      * @return a SessionContext for Stateless Session Bean
246      */

247     public JSessionContext getJContext(JSessionSwitch ss) {
248         if (TraceEjb.isDebugIc()) {
249             TraceEjb.interp.log(BasicLevel.DEBUG, "");
250         }
251         JStatelessContext bctx = null;
252
253         // try to find a free context in the pool
254
synchronized (bctxlist) {
255             try {
256                 ListIterator JavaDoc i = bctxlist.listIterator();
257                 if (i.hasNext()) {
258                     bctx = (JStatelessContext) i.next();
259                     bctx.initSessionContext(ss);
260                     i.remove();
261                 }
262             } catch (IndexOutOfBoundsException JavaDoc ex) {
263                 // pool is empty
264
}
265         }
266
267         if (bctx == null) {
268             // create a new one if pool empty
269
try {
270                 bctx = createNewInstance(ss);
271             } catch (Exception JavaDoc e) {
272                 TraceEjb.logger.log(BasicLevel.ERROR, "exception:" + e);
273                 throw new EJBException JavaDoc("Cannot create a new instance", e);
274             }
275         }
276         if (TraceEjb.isDebugSwapper()) {
277             TraceEjb.swapper.log(BasicLevel.DEBUG, "nb instances " + getCacheSize());
278         }
279         return bctx;
280     }
281
282     /**
283      * Called after each method call
284      * @param ctx the Session Context
285      */

286     public void releaseJContext(JContext ctx) {
287         if (ctx == null) {
288             TraceEjb.swapper.log(BasicLevel.ERROR, "null ctx!");
289             return;
290         }
291         TraceEjb.swapper.log(BasicLevel.DEBUG, "");
292         JStatelessContext bctx = (JStatelessContext) ctx;
293
294         // if the max number of instances is reached
295
// we can reduce the number of instances
296
synchronized (bctxlist) {
297             if (maxCacheSize != 0 && instanceCount > maxCacheSize) {
298                 if (TraceEjb.isDebugSwapper()) {
299                     TraceEjb.swapper.log(BasicLevel.DEBUG, "too much instances :" + instanceCount + ", max="
300                             + maxCacheSize);
301                 }
302                 instanceCount--;
303             } else {
304                 bctxlist.add(bctx);
305             }
306         }
307         if (TraceEjb.isDebugSwapper()) {
308             TraceEjb.swapper.log(BasicLevel.DEBUG, "nb instances " + getCacheSize());
309         }
310     }
311
312     /**
313      * Notify a timeout for this bean
314      * @param timer timer whose expiration caused this notification.
315      */

316     public void notifyTimeout(Timer JavaDoc timer) {
317         if (TraceTimer.isDebug()) {
318             TraceTimer.logger.log(BasicLevel.DEBUG, "");
319         }
320
321         // Get a JStatelessSwitch (similar to createEJB)
322
// In fact, we don't need the Local and Remote objects here.
323
// This object is just needed to initilize the SessionContext.
324
JSessionSwitch bs = null;
325         if (sessionList.size() > 0) {
326             if (TraceEjb.isDebugIc()) {
327                 TraceEjb.interp.log(BasicLevel.DEBUG, "get a JStatelessSwitch from the pool");
328             }
329             bs = (JSessionSwitch) sessionList.remove(0);
330             // must re-export the remote object in the Orb since EJBObjects
331
// to avoids a fail when object is put in the pool.
332
JSessionRemote remote = bs.getRemote();
333             if (remote != null) {
334                 if (!remote.exportObject()) {
335                     TraceEjb.logger.log(BasicLevel.ERROR, "bad JSessionSwitch found in pool.");
336                 }
337             }
338         } else {
339             if (TraceEjb.isDebugIc()) {
340                 TraceEjb.interp.log(BasicLevel.DEBUG, "create a new JStatelessSwitch");
341             }
342             try {
343                 bs = new JStatelessSwitch(this);
344             } catch (RemoteException JavaDoc e) {
345                 TraceEjb.logger.log(BasicLevel.ERROR, "Notify Timeout - Unexpected : ", e);
346                 return;
347             }
348         }
349         //EJBInvocation ejbInv = new EJBInvocation();
350
//ejbInv.methodPermissionSignature = getEjbTimeoutSignature();
351
//ejbInv.arguments = new Object [] {timer};
352

353         for (int nbretry = 0; nbretry < MAX_NB_RETRY; nbretry++) {
354             RequestCtx rctx = preInvoke(getTimerTxAttribute());
355             JSessionContext bctx = null;
356             try {
357                 bctx = bs.getICtx(rctx.currTx);
358                 TimedObject JavaDoc instance = (TimedObject JavaDoc) bctx.getInstance();
359                 //ejbInv.bean = (EnterpriseBean) instance;
360
checkSecurity(null);
361                 instance.ejbTimeout(timer);
362                 if (rctx.currTx == null
363                         || (rctx.currTx.getStatus() != Status.STATUS_MARKED_ROLLBACK)) {
364                     break;
365                 }
366             } catch (EJBException JavaDoc e) {
367                 rctx.sysExc = e;
368                 throw e;
369             } catch (RuntimeException JavaDoc e) {
370                 rctx.sysExc = e;
371                 throw new EJBException JavaDoc("RuntimeException thrown by an enterprise Bean", e);
372             } catch (Error JavaDoc e) {
373                 rctx.sysExc = e;
374                 throw new EJBException JavaDoc("Error thrown by an enterprise Bean" + e);
375             } catch (RemoteException JavaDoc e) {
376                 rctx.sysExc = e;
377                 throw new EJBException JavaDoc("Remote Exception raised:", e);
378             } catch (SystemException JavaDoc e) {
379                 rctx.sysExc = e;
380                 throw new EJBException JavaDoc("Cannot get transaction status:", e);
381             } finally {
382                 postInvoke(rctx);
383             }
384         }
385
386         // release everything - Exactly as if the session timeout had
387
// expired.
388
bs.timeoutExpired(null);
389     }
390
391     /**
392      * @return min pool size for Jmx
393      */

394     public int getMinPoolSize() {
395         return minPoolSize;
396     }
397
398     /**
399      * @return max cache size for Jmx
400      */

401     public int getMaxCacheSize() {
402         return maxCacheSize;
403     }
404
405     /**
406      * @return current cache size ( = nb of instance created) for Jmx
407      */

408     public int getCacheSize() {
409         return instanceCount;
410     }
411
412     // ---------------------------------------------------------------
413
// private methods
414
// ---------------------------------------------------------------
415

416     /**
417      * Create a new instance of the bean and its StatelessContext
418      */

419     private JStatelessContext createNewInstance(JSessionSwitch ss) throws Exception JavaDoc {
420         if (TraceEjb.isDebugIc()) {
421             TraceEjb.interp.log(BasicLevel.DEBUG, "");
422         }
423         // create the bean instance
424
SessionBean JavaDoc bean = null;
425         try {
426             bean = (SessionBean JavaDoc) beanclass.newInstance();
427         } catch (InstantiationException JavaDoc e) {
428             TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot instantiate session bean");
429             throw e;
430         } catch (IllegalAccessException JavaDoc e) {
431             TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " Cannot instantiate SessionBean");
432             throw e;
433         }
434
435         // create a new StatelessContext and bind it to the instance
436
JStatelessContext bctx = new JStatelessContext(this, bean);
437         bean.setSessionContext(bctx);
438         bctx.setState(1);
439         bctx.initSessionContext(ss);
440         // call ejbCreate on the instance
441
if (TraceEjb.isDebugIc()) {
442             TraceEjb.interp.log(BasicLevel.DEBUG, "call ejbCreate on the instance");
443         }
444         try {
445             beanclass.getMethod("ejbCreate", (Class JavaDoc[]) null).invoke(bean, (Object JavaDoc[]) null);
446         } catch (InvocationTargetException JavaDoc ite) {
447             // get the root cause
448
Throwable JavaDoc t = ite.getTargetException();
449             TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot call ejbCreate on Stateless Session " + t.getMessage(), t);
450         } catch (Exception JavaDoc e) {
451             TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot call ejbCreate on Stateless Session " + e.getMessage(), e);
452         }
453         bctx.setState(2);
454
455         // We always create an instance even if the max is reached
456
// see releaseJContext
457
synchronized (bctxlist) {
458             instanceCount++;
459         }
460         return bctx;
461     }
462
463 }
464
Popular Tags