/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 (at your option) any later version. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb.plugins; import java.rmi.RemoteException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.IOException; import javax.ejb.SessionBean; import javax.ejb.RemoveException; import javax.ejb.EJBException; import javax.naming.Context; import javax.naming.InitialContext; import org.jboss.ejb.Container; import org.jboss.ejb.StatefulSessionContainer; import org.jboss.ejb.StatefulSessionPersistenceManager; import org.jboss.ejb.StatefulSessionEnterpriseContext; import org.jboss.metadata.ClusterConfigMetaData; import org.jboss.ha.hasessionstate.interfaces.HASessionState; import org.jboss.ha.hasessionstate.interfaces.PackagedSession; import org.jboss.ha.framework.interfaces.HAPartition; import org.jboss.system.ServiceMBeanSupport; import org.jboss.util.id.UID; /** * This persistence manager work with an underlying HASessionState to get * clustered state. * * @see HASessionState * @see org.jboss.ha.hasessionstate.server.HASessionStateImpl * * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a> * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> * @version $Revision: 81001 $ * * <p><b>Revisions:</b> */ public class StatefulHASessionPersistenceManager extends ServiceMBeanSupport implements StatefulSessionPersistenceManager, HASessionState.HASessionStateListener, HAPersistentManager { /** Creates new StatefulHASessionPersistenceManager */ public StatefulHASessionPersistenceManager () { } // Constants ----------------------------------------------------- // Attributes ---------------------------------------------------- private StatefulSessionContainer con; private HASessionState sessionState = null; private String localNodeName = null; private String appName = null; // Static -------------------------------------------------------- private static String DEFAULT_HASESSIONSTATE_JNDI_NAME = "/HASessionState/Default"; // Constructors -------------------------------------------------- // Public -------------------------------------------------------- public void setContainer (Container c) { con = (StatefulSessionContainer)c; } protected void createService() throws Exception { // Initialize the dataStore // Find HASessionState that we will use // String sessionStateName = org.jboss.metadata.ClusterConfigMetaData.DEFAULT_SESSION_STATE_NAME; ClusterConfigMetaData config = con.getBeanMetaData ().getClusterConfigMetaData (); if (config != null) sessionStateName = config.getHaSessionStateName (); Context ctx = new InitialContext (); try { this.sessionState = (HASessionState)ctx.lookup (sessionStateName); } finally { ctx.close(); } this.localNodeName = this.sessionState.getNodeName (); this.appName = this.con.getBeanMetaData ().getJndiName (); this.sessionState.subscribe (this.appName, this); } protected void stopService () { this.sessionState.unsubscribe (this.appName, this); } public Object createId(StatefulSessionEnterpriseContext ctx) throws Exception { // // jason: could probably use org.jboss.util.collection.CompoundKey here // for better uniqueness based on hashCode and such... // // return new CompoundKey(this.localNodeName, new UID()); // return this.localNodeName + ":" + new UID(); } public void createdSession(StatefulSessionEnterpriseContext ctx) throws Exception { this.sessionState.createSession(this.appName, ctx.getId()); } public void activateSession (StatefulSessionEnterpriseContext ctx) throws RemoteException { try { ObjectInputStream in; // Load state PackagedSession state = this.sessionState.getStateWithOwnership (this.appName, ctx.getId ()); if (state == null) throw new EJBException ("Could not activate; failed to recover session (session has been probably removed by session-timeout)"); in = new SessionObjectInputStream (ctx, new ByteArrayInputStream (state.getState ()));; ctx.setInstance ( in.readObject () ); in.close (); //removePassivated (ctx.getId ()); // Instruct the bean to perform activation logic ((SessionBean)ctx.getInstance()).ejbActivate(); } catch (ClassNotFoundException e) { throw new EJBException ("Could not activate", e); } catch (IOException e) { throw new EJBException ("Could not activate", e); } } public void passivateSession (StatefulSessionEnterpriseContext ctx) throws RemoteException { // do nothing } public void synchroSession (StatefulSessionEnterpriseContext ctx) throws RemoteException { try { // Call bean ((SessionBean)ctx.getInstance()).ejbPassivate(); // Store state ByteArrayOutputStream baos = new ByteArrayOutputStream (); ObjectOutputStream out = new SessionObjectOutputStream (baos); out.writeObject (ctx.getInstance ()); out.close (); this.sessionState.setState (this.appName, ctx.getId (), baos.toByteArray ()); ((SessionBean)ctx.getInstance()).ejbActivate(); //needed? } catch (IOException e) { throw new EJBException ("Could not passivate", e); } } public void removeSession (StatefulSessionEnterpriseContext ctx) throws RemoteException, RemoveException { try { // Call bean ((SessionBean)ctx.getInstance()).ejbRemove(); } finally { this.sessionState.removeSession (this.appName, ctx.getId ()); } } public void removePassivated (Object key) { this.sessionState.removeSession (this.appName, key); } // HASessionState.HASessionStateListener methods ----------------- // public void sessionExternallyModified (PackagedSession session) { // this callback warns us that a session (i.e. a bean) has been externally modified // this means that we need to tell to our InstanceCache that this bean is no more under its control // and that it should not keep a cached version of it => CACHE MISS // log.trace ("Invalidating cache for session: " + session.getKey()); this.con.getInstanceCache ().remove (session.getKey ()); } public void newSessionStateTopology (HAPartition haSubPartition) { } }