/**
* JOnAS: Java(TM) Open Application Server
* Copyright (C) 1999 Bull S.A.
* Contact: jonas-team@objectweb.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id: JStatefulFactory.java 9238 2006-07-26 08:52:36Z coqp $
* --------------------------------------------------------------------------
*/
package org.objectweb.jonas_ejb.container;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Iterator;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.TimerService;
import javax.transaction.Transaction;
import org.objectweb.jonas_ejb.deployment.api.SessionDesc;
import org.objectweb.jonas_ejb.deployment.api.SessionStatefulDesc;
import org.objectweb.jonas_ejb.lib.EJBInvocation;
import org.objectweb.util.monolog.api.BasicLevel;
/**
* This class is a factory for a Session Stateful Bean.
* @author Philippe Durieux
*/
public class JStatefulFactory extends JSessionFactory {
/**
* List of JStatefulSwitch objects (pool of session instances)
* Key is the sessionId
*/
protected HashMap statefulList = new HashMap();
/**
* Current Cache Size
*/
protected int cacheSize = 0;
/**
* Context id (increment counter)
*/
private int sessionCount = 0;
/**
* constructor
* @param dd Bean Deployment Descriptor
* @param cont Container where the bean is defined
*/
public JStatefulFactory(SessionStatefulDesc dd, JContainer cont) {
super((SessionDesc) dd, cont);
TraceEjb.interp.log(BasicLevel.DEBUG, "");
isSynchro = javax.ejb.SessionSynchronization.class.isAssignableFrom(beanclass);
isStateful = true;
singleswitch = false;
}
// ---------------------------------------------------------------
// BeanFactory implementation
// ---------------------------------------------------------------
/**
* @return the Instance cache size for this Ejb
*/
public int getCacheSize() {
return sessionList.size();
}
/**
* No pool for stateful session beans
* @return 0
*/
public int getPoolSize() {
return 0;
}
/**
* Reduce number of instances in memory
*/
public void reduceCache() {
}
/**
* No pool of instances for stateful session beans
*/
public void initInstancePool() {
}
// ---------------------------------------------------------------
// preInvoke / postInvoke
// ---------------------------------------------------------------
/**
* preInvoke for Session beans stateful
* @param txa Transaction Attribute (Supports, Required, ...)
* @return A RequestCtx object
* @throws EJBException
*/
public RequestCtx preInvoke(int txa) {
if (TraceEjb.isDebugIc()) {
TraceEjb.interp.log(BasicLevel.DEBUG, "");
}
RequestCtx rctx = super.preInvoke(txa);
return rctx;
}
/**
* Check if the access to the bean is authorized
* @param ejbInv object containing security signature of the method, args of
* method, etc
*/
public void checkSecurity(EJBInvocation ejbInv) {
if (TraceEjb.isDebugIc()) {
TraceEjb.interp.log(BasicLevel.DEBUG, "");
}
super.checkSecurity(ejbInv);
}
/**
* postinvoke
* @param rctx The RequestCtx that was returned at preInvoke()
* @throws EJBException
*/
public void postInvoke(RequestCtx rctx) {
if (TraceEjb.isDebugIc()) {
TraceEjb.interp.log(BasicLevel.DEBUG, "");
}
super.postInvoke(rctx);
}
// ---------------------------------------------------------------
// other public methods
// ---------------------------------------------------------------
/**
* Obtains the TimerService associated for this Bean
* @return a JTimerService instance.
*/
public TimerService getTimerService() {
throw new EJBException("No TimerService for Stateful Session beans");
}
/**
* Creates a new Session Stateful called back from createEJB
* @return The Session Switch object
*/
public JSessionSwitch createNewSession() throws RemoteException {
JStatefulSwitch bs = new JStatefulSwitch(this);
return bs;
}
public int getNewSessionId(JStatefulSwitch jss) {
// Check if we must passivate some beans first
while (maxCacheSize > 0 && cacheSize >= maxCacheSize) {
// LRU algo
long maxtime = Long.MAX_VALUE;
JStatefulSwitch victim = null;
synchronized (this) {
for (Iterator i = statefulList.values().iterator(); i.hasNext(); ) {
JStatefulSwitch ss = (JStatefulSwitch) i.next();
long time = ss.getLastAccessTime();
if (time < maxtime) {
if (ss.canPassivate()) {
victim = ss;
}
}
}
}
if (victim != null) {
// Keeping the lock here may leads to deadlocks.
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "try to passivate a bean");
if (victim.passivate()) {
cacheSize--;
}
} else {
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Could not find enough beans to passivate");
break;
}
}
// Get a unique identifier
synchronized(this) {
int sid = sessionCount++;
if (TraceEjb.isDebugSsfpool()) {
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
}
// Put in in the statefulList
statefulList.put(new Integer(sid), jss);
cacheSize++;
return sid;
}
}
/**
* Return the name of the stateful passivated file
* @param sid session ident
* @return name of the stateful passivated file
* @throws IOException
*/
private File getSsfFileName(int sid) throws IOException {
return new File(passivationDir, String.valueOf(sid) + ".ssf");
}
private FileOutputStream getFileOutputStream(int sid) throws IOException {
File file = getSsfFileName(sid);
file.createNewFile();
return new FileOutputStream(file);
}
private FileInputStream getFileInputStream(int sid) throws IOException {
File file = getSsfFileName(sid);
return new FileInputStream(file);
}
private void removePassivatedState(int sid) throws IOException {
File file = getSsfFileName(sid);
file.delete();
}
public boolean passivateStateful(JStatefulSwitch jss) {
int sid = jss.getSessionId();
if (TraceEjb.isDebugSsfpool()) {
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
}
JStatefulContext ctx = jss.getStatefulContext();
try {
SessionBean instance = ctx.getInstance();
instance.ejbPassivate();
JStatefulOutputStream out =
new JStatefulOutputStream(new BufferedOutputStream(getFileOutputStream(sid)));
out.writeObject(instance);
out.close();
} catch (Exception e) {
TraceEjb.ssfpool.log(BasicLevel.WARN, "Cannot passivate instance:" + e);
return false;
}
TraceEjb.ssfpool.log(BasicLevel.WARN, "passivated: #" + sid);
return true;
}
public JStatefulContext activateStateful(JStatefulSwitch jss) {
int sid = jss.getSessionId();
if (TraceEjb.isDebugSsfpool()) {
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
}
// In this version, only instance is garbaged at passivation (not Context)
JStatefulContext bctx = (JStatefulContext) jss.getStatefulContext();
try {
JStatefulInputStream in =
new JStatefulInputStream(new BufferedInputStream(getFileInputStream(sid)), jss);
Object obj = in.readObject();
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "Deserialized object:" + obj);
SessionBean sb = (SessionBean) obj;
in.close();
if (sb == null) {
TraceEjb.ssfpool.log(BasicLevel.ERROR, "Bad stateful deserialization");
return null;
}
bctx.setInstance(sb);
sb.ejbActivate();
// remove passivated file
removePassivatedState(sid);
cacheSize++;
} catch (Exception e) {
TraceEjb.ssfpool.log(BasicLevel.WARN, "Cannot activate instance:" + e);
return null;
}
TraceEjb.ssfpool.log(BasicLevel.WARN, "reactivated : #" + sid);
return bctx;
}
public synchronized void removeStateful(int sid) {
if (TraceEjb.isDebugSsfpool()) {
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "#" + sid);
}
// Remove from statefulList
JStatefulSwitch jss = (JStatefulSwitch) statefulList.remove(new Integer(sid));
if (jss.isPassivated()) {
// remove passivated file
try {
removePassivatedState(sid);
} catch (Exception e) {
TraceEjb.ssfpool.log(BasicLevel.WARN, "Cannot remove passivated instance:" + e);
}
} else {
cacheSize--;
}
if (TraceEjb.isDebugSsfpool()) {
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "statefulList size = " + statefulList.size());
TraceEjb.ssfpool.log(BasicLevel.DEBUG, "cache size = " + cacheSize);
}
}
/**
* get a new session context must call newInstance (EJB specs) => no pool
* should be used.
* @return a new Session Context
*/
public JSessionContext getJContext(JSessionSwitch ss) {
if (TraceEjb.isDebugIc()) {
TraceEjb.interp.log(BasicLevel.DEBUG, "");
}
JStatefulContext bctx = null;
try {
bctx = createNewInstance(ss);
} catch (Exception e) {
throw new EJBException("Cannot create a new instance", e);
}
return bctx;
}
// ---------------------------------------------------------------
// private methods
// ---------------------------------------------------------------
/**
* Create a new instance of the bean and its StatefulContext
*/
private JStatefulContext createNewInstance(JSessionSwitch ss) throws Exception {
if (TraceEjb.isDebugIc()) {
TraceEjb.interp.log(BasicLevel.DEBUG, "");
}
// create the bean instance
SessionBean bean = null;
try {
bean = (SessionBean) beanclass.newInstance();
} catch (InstantiationException e) {
TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot instantiate session bean");
throw e;
} catch (IllegalAccessException e) {
TraceEjb.logger.log(BasicLevel.ERROR, ejbname + " cannot instantiate session bean");
throw e;
}
// create a new StatefulContext and bind it to the instance
JStatefulContext bctx = null;
if (!dd.isClusterReplicated()) {
bctx = new JStatefulContext(this, bean, isSynchro);
} else {
bctx = new JRepStatefulContext(this, bean, isSynchro);
}
bean.setSessionContext(bctx);
bctx.setState(1);
return bctx;
}
/*
* Make sense only for entities
*/
public void storeInstances(Transaction tx) {
// unused
}
}
|