001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: JStatefulFactory.java 9238 2006-07-26 08:52:36Z coqp $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.container;
025:
026: import java.io.BufferedInputStream;
027: import java.io.BufferedOutputStream;
028: import java.io.File;
029: import java.io.FileInputStream;
030: import java.io.FileOutputStream;
031: import java.io.IOException;
032: import java.rmi.RemoteException;
033: import java.util.HashMap;
034: import java.util.Iterator;
035:
036: import javax.ejb.EJBException;
037: import javax.ejb.SessionBean;
038: import javax.ejb.TimerService;
039: import javax.transaction.Transaction;
040:
041: import org.objectweb.jonas_ejb.deployment.api.SessionDesc;
042: import org.objectweb.jonas_ejb.deployment.api.SessionStatefulDesc;
043: import org.objectweb.jonas_ejb.lib.EJBInvocation;
044: import org.objectweb.util.monolog.api.BasicLevel;
045:
046: /**
047: * This class is a factory for a Session Stateful Bean.
048: * @author Philippe Durieux
049: */
050: public class JStatefulFactory extends JSessionFactory {
051:
052: /**
053: * List of JStatefulSwitch objects (pool of session instances)
054: * Key is the sessionId
055: */
056: protected HashMap statefulList = new HashMap();
057:
058: /**
059: * Current Cache Size
060: */
061: protected int cacheSize = 0;
062:
063: /**
064: * Context id (increment counter)
065: */
066: private int sessionCount = 0;
067:
068: /**
069: * constructor
070: * @param dd Bean Deployment Descriptor
071: * @param cont Container where the bean is defined
072: */
073: public JStatefulFactory(SessionStatefulDesc dd, JContainer cont) {
074: super ((SessionDesc) dd, cont);
075: TraceEjb.interp.log(BasicLevel.DEBUG, "");
076: isSynchro = javax.ejb.SessionSynchronization.class
077: .isAssignableFrom(beanclass);
078: isStateful = true;
079: singleswitch = false;
080: }
081:
082: // ---------------------------------------------------------------
083: // BeanFactory implementation
084: // ---------------------------------------------------------------
085:
086: /**
087: * @return the Instance cache size for this Ejb
088: */
089: public int getCacheSize() {
090: return sessionList.size();
091: }
092:
093: /**
094: * No pool for stateful session beans
095: * @return 0
096: */
097: public int getPoolSize() {
098: return 0;
099: }
100:
101: /**
102: * Reduce number of instances in memory
103: */
104: public void reduceCache() {
105: }
106:
107: /**
108: * No pool of instances for stateful session beans
109: */
110: public void initInstancePool() {
111: }
112:
113: // ---------------------------------------------------------------
114: // preInvoke / postInvoke
115: // ---------------------------------------------------------------
116:
117: /**
118: * preInvoke for Session beans stateful
119: * @param txa Transaction Attribute (Supports, Required, ...)
120: * @return A RequestCtx object
121: * @throws EJBException
122: */
123: public RequestCtx preInvoke(int txa) {
124: if (TraceEjb.isDebugIc()) {
125: TraceEjb.interp.log(BasicLevel.DEBUG, "");
126: }
127: RequestCtx rctx = super .preInvoke(txa);
128: return rctx;
129: }
130:
131: /**
132: * Check if the access to the bean is authorized
133: * @param ejbInv object containing security signature of the method, args of
134: * method, etc
135: */
136: public void checkSecurity(EJBInvocation ejbInv) {
137: if (TraceEjb.isDebugIc()) {
138: TraceEjb.interp.log(BasicLevel.DEBUG, "");
139: }
140: super .checkSecurity(ejbInv);
141: }
142:
143: /**
144: * postinvoke
145: * @param rctx The RequestCtx that was returned at preInvoke()
146: * @throws EJBException
147: */
148: public void postInvoke(RequestCtx rctx) {
149: if (TraceEjb.isDebugIc()) {
150: TraceEjb.interp.log(BasicLevel.DEBUG, "");
151: }
152: super .postInvoke(rctx);
153: }
154:
155: // ---------------------------------------------------------------
156: // other public methods
157: // ---------------------------------------------------------------
158:
159: /**
160: * Obtains the TimerService associated for this Bean
161: * @return a JTimerService instance.
162: */
163: public TimerService getTimerService() {
164: throw new EJBException(
165: "No TimerService for Stateful Session beans");
166: }
167:
168: /**
169: * Creates a new Session Stateful called back from createEJB
170: * @return The Session Switch object
171: */
172: public JSessionSwitch createNewSession() throws RemoteException {
173: JStatefulSwitch bs = new JStatefulSwitch(this );
174: return bs;
175: }
176:
177: public int getNewSessionId(JStatefulSwitch jss) {
178: // Check if we must passivate some beans first
179: while (maxCacheSize > 0 && cacheSize >= maxCacheSize) {
180: // LRU algo
181: long maxtime = Long.MAX_VALUE;
182: JStatefulSwitch victim = null;
183: synchronized (this ) {
184: for (Iterator i = statefulList.values().iterator(); i
185: .hasNext();) {
186: JStatefulSwitch ss = (JStatefulSwitch) i.next();
187: long time = ss.getLastAccessTime();
188: if (time < maxtime) {
189: if (ss.canPassivate()) {
190: victim = ss;
191: }
192: }
193: }
194: }
195: if (victim != null) {
196: // Keeping the lock here may leads to deadlocks.
197: TraceEjb.ssfpool.log(BasicLevel.DEBUG,
198: "try to passivate a bean");
199: if (victim.passivate()) {
200: cacheSize--;
201: }
202: } else {
203: TraceEjb.ssfpool.log(BasicLevel.DEBUG,
204: "Could not find enough beans to passivate");
205: break;
206: }
207: }
208: // Get a unique identifier
209: synchronized (this ) {
210: int sid = sessionCount++;
211: if (TraceEjb.isDebugSsfpool()) {
212: TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
213: }
214: // Put in in the statefulList
215: statefulList.put(new Integer(sid), jss);
216: cacheSize++;
217: return sid;
218: }
219: }
220:
221: /**
222: * Return the name of the stateful passivated file
223: * @param sid session ident
224: * @return name of the stateful passivated file
225: * @throws IOException
226: */
227: private File getSsfFileName(int sid) throws IOException {
228: return new File(passivationDir, String.valueOf(sid) + ".ssf");
229: }
230:
231: private FileOutputStream getFileOutputStream(int sid)
232: throws IOException {
233: File file = getSsfFileName(sid);
234: file.createNewFile();
235: return new FileOutputStream(file);
236: }
237:
238: private FileInputStream getFileInputStream(int sid)
239: throws IOException {
240: File file = getSsfFileName(sid);
241: return new FileInputStream(file);
242: }
243:
244: private void removePassivatedState(int sid) throws IOException {
245: File file = getSsfFileName(sid);
246: file.delete();
247: }
248:
249: public boolean passivateStateful(JStatefulSwitch jss) {
250: int sid = jss.getSessionId();
251: if (TraceEjb.isDebugSsfpool()) {
252: TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
253: }
254: JStatefulContext ctx = jss.getStatefulContext();
255: try {
256: SessionBean instance = ctx.getInstance();
257: instance.ejbPassivate();
258: JStatefulOutputStream out = new JStatefulOutputStream(
259: new BufferedOutputStream(getFileOutputStream(sid)));
260: out.writeObject(instance);
261: out.close();
262: } catch (Exception e) {
263: TraceEjb.ssfpool.log(BasicLevel.WARN,
264: "Cannot passivate instance:" + e);
265: return false;
266: }
267: TraceEjb.ssfpool.log(BasicLevel.WARN, "passivated: #" + sid);
268: return true;
269: }
270:
271: public JStatefulContext activateStateful(JStatefulSwitch jss) {
272: int sid = jss.getSessionId();
273: if (TraceEjb.isDebugSsfpool()) {
274: TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
275: }
276: // In this version, only instance is garbaged at passivation (not Context)
277: JStatefulContext bctx = (JStatefulContext) jss
278: .getStatefulContext();
279: try {
280: JStatefulInputStream in = new JStatefulInputStream(
281: new BufferedInputStream(getFileInputStream(sid)),
282: jss);
283: Object obj = in.readObject();
284: TraceEjb.ssfpool.log(BasicLevel.DEBUG,
285: "Deserialized object:" + obj);
286: SessionBean sb = (SessionBean) obj;
287: in.close();
288: if (sb == null) {
289: TraceEjb.ssfpool.log(BasicLevel.ERROR,
290: "Bad stateful deserialization");
291: return null;
292: }
293: bctx.setInstance(sb);
294: sb.ejbActivate();
295: // remove passivated file
296: removePassivatedState(sid);
297: cacheSize++;
298: } catch (Exception e) {
299: TraceEjb.ssfpool.log(BasicLevel.WARN,
300: "Cannot activate instance:" + e);
301: return null;
302: }
303: TraceEjb.ssfpool.log(BasicLevel.WARN, "reactivated : #" + sid);
304: return bctx;
305: }
306:
307: public synchronized void removeStateful(int sid) {
308: if (TraceEjb.isDebugSsfpool()) {
309: TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
310: }
311: // Remove from statefulList
312: JStatefulSwitch jss = (JStatefulSwitch) statefulList
313: .remove(new Integer(sid));
314: if (jss.isPassivated()) {
315: // remove passivated file
316: try {
317: removePassivatedState(sid);
318: } catch (Exception e) {
319: TraceEjb.ssfpool.log(BasicLevel.WARN,
320: "Cannot remove passivated instance:" + e);
321: }
322: } else {
323: cacheSize--;
324: }
325: if (TraceEjb.isDebugSsfpool()) {
326: TraceEjb.ssfpool.log(BasicLevel.DEBUG,
327: "statefulList size = " + statefulList.size());
328: TraceEjb.ssfpool.log(BasicLevel.DEBUG, "cache size = "
329: + cacheSize);
330: }
331: }
332:
333: /**
334: * get a new session context must call newInstance (EJB specs) => no pool
335: * should be used.
336: * @return a new Session Context
337: */
338: public JSessionContext getJContext(JSessionSwitch ss) {
339: if (TraceEjb.isDebugIc()) {
340: TraceEjb.interp.log(BasicLevel.DEBUG, "");
341: }
342: JStatefulContext bctx = null;
343: try {
344: bctx = createNewInstance(ss);
345: } catch (Exception e) {
346: throw new EJBException("Cannot create a new instance", e);
347: }
348: return bctx;
349: }
350:
351: // ---------------------------------------------------------------
352: // private methods
353: // ---------------------------------------------------------------
354:
355: /**
356: * Create a new instance of the bean and its StatefulContext
357: */
358: private JStatefulContext createNewInstance(JSessionSwitch ss)
359: throws Exception {
360: if (TraceEjb.isDebugIc()) {
361: TraceEjb.interp.log(BasicLevel.DEBUG, "");
362: }
363: // create the bean instance
364: SessionBean bean = null;
365: try {
366: bean = (SessionBean) beanclass.newInstance();
367: } catch (InstantiationException e) {
368: TraceEjb.logger.log(BasicLevel.ERROR, ejbname
369: + " cannot instantiate session bean");
370: throw e;
371: } catch (IllegalAccessException e) {
372: TraceEjb.logger.log(BasicLevel.ERROR, ejbname
373: + " cannot instantiate session bean");
374: throw e;
375: }
376: // create a new StatefulContext and bind it to the instance
377:
378: JStatefulContext bctx = null;
379: if (!dd.isClusterReplicated()) {
380: bctx = new JStatefulContext(this , bean, isSynchro);
381: } else {
382: bctx = new JRepStatefulContext(this , bean, isSynchro);
383: }
384:
385: bean.setSessionContext(bctx);
386: bctx.setState(1);
387: return bctx;
388: }
389:
390: /*
391: * Make sense only for entities
392: */
393: public void storeInstances(Transaction tx) {
394: // unused
395: }
396: }
|