package org.jboss.ejb.plugins;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.rmi.RemoteException;
import java.security.PrivilegedAction;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.RemoveException;
import org.jboss.ejb.Container;
import org.jboss.ejb.StatefulSessionContainer;
import org.jboss.ejb.StatefulSessionPersistenceManager;
import org.jboss.ejb.StatefulSessionEnterpriseContext;
import org.jboss.system.server.ServerConfigLocator;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.util.id.UID;
public class StatefulSessionFilePersistenceManager
extends ServiceMBeanSupport
implements StatefulSessionPersistenceManager, StatefulSessionFilePersistenceManagerMBean
{
public static final String DEFAULT_STORE_DIRECTORY_NAME = "sessions";
private StatefulSessionContainer con;
private String storeDirName = DEFAULT_STORE_DIRECTORY_NAME;
private File storeDir;
private boolean purgeEnabled = true;
public void setContainer(final Container con)
{
this.con = (StatefulSessionContainer) con;
}
public void setStoreDirectoryName(final String dirName)
{
this.storeDirName = dirName;
}
public String getStoreDirectoryName()
{
return storeDirName;
}
public void setPurgeEnabled(final boolean flag)
{
this.purgeEnabled = flag;
}
public boolean getPurgeEnabled()
{
return purgeEnabled;
}
public File getStoreDirectory()
{
return storeDir;
}
protected void createService() throws Exception
{
boolean debug = log.isDebugEnabled();
String ejbName = con.getBeanMetaData().getEjbName();
File dir = ServerConfigLocator.locate().getServerTempDir();
dir = new File(dir, storeDirName);
dir = new File(dir, ejbName + "-" + new UID().toString());
storeDir = dir;
if( debug )
{
log.debug("Storing sessions for '" + ejbName + "' in: " + storeDir);
}
if( !storeDir.exists() )
{
if( MkdirsFileAction.mkdirs(storeDir) == false )
{
throw new IOException("Failed to create directory: " + storeDir);
}
}
if( !storeDir.isDirectory() )
{
throw new IOException("File exists where directory expected: " + storeDir);
}
if( !storeDir.canWrite() || !storeDir.canRead() )
{
throw new IOException("Directory must be readable and writable: " + storeDir);
}
purgeAllSessionData();
}
private void purgeAllSessionData()
{
if( !purgeEnabled ) return;
log.debug("Purging all session data in: " + storeDir);
File[] sessions = storeDir.listFiles();
for(int i = 0; i < sessions.length; i++)
{
if( !sessions[i].delete() )
{
log.warn("Failed to delete session state file: " + sessions[i]);
}
else
{
log.debug("Removed stale session state: " + sessions[i]);
}
}
}
protected void destroyService() throws Exception
{
purgeAllSessionData();
if( purgeEnabled && !storeDir.delete() )
{
log.warn("Failed to delete session state storage directory: " + storeDir);
}
}
private File getFile(final Object id)
{
return new File(storeDir, String.valueOf(id) + ".ser");
}
public Object createId(StatefulSessionEnterpriseContext ctx)
throws Exception
{
return new UID();
}
public void createdSession(StatefulSessionEnterpriseContext ctx)
throws Exception
{
}
public void activateSession(final StatefulSessionEnterpriseContext ctx)
throws RemoteException
{
boolean debug = log.isDebugEnabled();
if( debug )
{
log.debug("Attempting to activate; ctx=" + ctx);
}
Object id = ctx.getId();
File file = getFile(id);
if( debug )
{
log.debug("Reading session state from: " + file);
}
try
{
FileInputStream fis = FISAction.open(file);
SessionObjectInputStream in = new SessionObjectInputStream(ctx,
new BufferedInputStream(fis));
try
{
Object obj = in.readObject();
log.debug("Session state: " + obj);
ctx.setInstance(obj);
}
finally
{
in.close();
}
}
catch(Exception e)
{
throw new EJBException("Could not activate; failed to " +
"restore state", e);
}
removePassivated(id);
SessionBean bean = (SessionBean) ctx.getInstance();
bean.ejbActivate();
if( debug )
{
log.debug("Activation complete; ctx=" + ctx);
}
}
public void passivateSession(final StatefulSessionEnterpriseContext ctx)
throws RemoteException
{
boolean debug = log.isDebugEnabled();
if( debug )
{
log.debug("Attempting to passivate; ctx=" + ctx);
}
SessionBean bean = (SessionBean) ctx.getInstance();
bean.ejbPassivate();
File file = getFile(ctx.getId());
if( debug )
{
log.debug("Saving session state to: " + file);
}
try
{
FileOutputStream fos = FOSAction.open(file);
SessionObjectOutputStream out = new SessionObjectOutputStream(
new BufferedOutputStream(fos));
Object obj = ctx.getInstance();
if( debug )
{
log.debug("Writing session state: " + obj);
}
try
{
out.writeObject(obj);
}
finally
{
out.close();
}
}
catch(Exception e)
{
throw new EJBException("Could not passivate; failed to save state", e);
}
if( debug )
{
log.debug("Passivation complete; ctx=" + ctx);
}
}
public void removeSession(final StatefulSessionEnterpriseContext ctx)
throws RemoteException, RemoveException
{
boolean debug = log.isDebugEnabled();
if( debug )
{
log.debug("Attempting to remove; ctx=" + ctx);
}
SessionBean bean = (SessionBean) ctx.getInstance();
bean.ejbRemove();
if( debug )
{
log.debug("Removal complete; ctx=" + ctx);
}
}
public void removePassivated(final Object id)
{
boolean debug = log.isDebugEnabled();
File file = getFile(id);
if( file.exists() )
{
if( debug )
{
log.debug("Removing passivated state file: " + file);
}
if( DeleteFileAction.delete(file) == false )
{
log.warn("Failed to delete passivated state file: " + file);
}
}
}
static class DeleteFileAction implements PrivilegedAction
{
File file;
DeleteFileAction(File file)
{
this.file = file;
}
public Object run()
{
boolean deleted = file.delete();
return new Boolean(deleted);
}
static boolean delete(File file)
{
DeleteFileAction action = new DeleteFileAction(file);
Boolean deleted = (Boolean) AccessController.doPrivileged(action);
return deleted.booleanValue();
}
}
static class MkdirsFileAction implements PrivilegedAction
{
File file;
MkdirsFileAction(File file)
{
this.file = file;
}
public Object run()
{
boolean ok = file.mkdirs();
return new Boolean(ok);
}
static boolean mkdirs(File file)
{
MkdirsFileAction action = new MkdirsFileAction(file);
Boolean ok = (Boolean) AccessController.doPrivileged(action);
return ok.booleanValue();
}
}
static class FISAction implements PrivilegedExceptionAction
{
File file;
FISAction(File file)
{
this.file = file;
}
public Object run() throws Exception
{
FileInputStream fis = new FileInputStream(file);
return fis;
}
static FileInputStream open(File file) throws FileNotFoundException
{
FISAction action = new FISAction(file);
FileInputStream fis = null;
try
{
fis = (FileInputStream) AccessController.doPrivileged(action);
}
catch(PrivilegedActionException e)
{
throw (FileNotFoundException) e.getException();
}
return fis;
}
}
static class FOSAction implements PrivilegedExceptionAction
{
File file;
FOSAction(File file)
{
this.file = file;
}
public Object run() throws Exception
{
FileOutputStream fis = new FileOutputStream(file);
return fis;
}
static FileOutputStream open(File file) throws FileNotFoundException
{
FOSAction action = new FOSAction(file);
FileOutputStream fos = null;
try
{
fos = (FileOutputStream) AccessController.doPrivileged(action);
}
catch(PrivilegedActionException e)
{
throw (FileNotFoundException) e.getException();
}
return fos;
}
}
}