remm 2002/12/05 04:25:09 Added: catalina/src/share/org/apache/catalina/cluster JGCluster.java JGManager.java ReplicatedSession.java ReplicationStream.java SerializablePrincipal.java SessionMessage.java Log: - Clustered JG support (untested for now; it builds, and doesn't appear to crash TC). Revision Changes Path 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/JGCluster.java Index: JGCluster.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/JGCluster.java,v 1.1 2002/12/05 12:25:09 remm Exp $ * $Revision: 1.1 $ * $Date: 2002/12/05 12:25:09 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster; import java.beans.PropertyChangeSupport; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.UnknownHostException; import java.io.IOException; import java.util.HashMap; import org.apache.catalina.Cluster; import org.apache.catalina.Container; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleListener; import org.apache.catalina.Logger; import org.apache.catalina.Manager; import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.util.StringManager; import org.javagroups.View; import org.javagroups.JChannel; import org.javagroups.Message; import org.javagroups.stack.IpAddress; /** * A <b>Cluster</b> implementation using JavaGroups. Responsible for setting * up a cluster and provides callers with a valid multicast receiver/sender. * * @author Filip Hanik * @author Remy Maucherat * @version $Revision: 1.1 $, $Date: 2002/12/05 12:25:09 $ */ public class JGCluster implements Cluster, Lifecycle { private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog( JGCluster.class ); // ----------------------------------------------------- Instance Variables /** * Descriptive information about this component implementation. */ private static final String info = "JavaGroupsCluster/1.0"; /** * Name to register for the background thread. */ private String threadName = "JavaGroupsCluster"; /** * Name for logging purpose */ private String clusterImpName = "JavaGroupsCluster"; /** * The string manager for this package. */ private StringManager sm = StringManager.getManager(Constants.Package); /** * The background thread completion semaphore. */ private boolean threadDone = false; /** * The cluster name to join */ private String clusterName = null; /** * The Container associated with this Cluster. */ private Container container = null; /** * The lifecycle event support for this component. */ private LifecycleSupport lifecycle = new LifecycleSupport(this); /** * Has this component been started? */ private boolean started = false; /** * The property change support for this component. */ private PropertyChangeSupport support = new PropertyChangeSupport(this); /** * The debug level for this Container */ private int debug = 0; /** * The context name <-> manager association. */ protected HashMap managers = new HashMap(); //A reference to the communication channel private JChannel mChannel = null; //the channel configuration private String mChannelConfig = null; //we keep a thread to listen to the channel private ReceiverThread mChannelListener = null; //somehow start() gets called more than once private boolean mChannelStarted = false; // ------------------------------------------------------------- Properties /** * Return descriptive information about this Cluster implementation and * the corresponding version number, in the format * <code><description>/<version></code>. */ public String getInfo() { return(this.info); } /** * Set the debug level for this component * * @param debug The debug level */ public void setDebug(int debug) { this.debug = debug; } /** * Get the debug level for this component * * @return The debug level */ public int getDebug() { return(this.debug); } /** * Set the name of the cluster to join, if no cluster with * this name is present create one. * * @param clusterName The clustername to join */ public void setClusterName(String clusterName) { String oldClusterName = this.clusterName; this.clusterName = clusterName; support.firePropertyChange("clusterName", oldClusterName, this.clusterName); } /** * Return the name of the cluster that this Server is currently * configured to operate within. * * @return The name of the cluster associated with this server */ public String getClusterName() { return(this.clusterName); } /** * Set the Container associated with our Cluster * * @param container The Container to use */ public void setContainer(Container container) { Container oldContainer = this.container; this.container = container; support.firePropertyChange("container", oldContainer, this.container); } /** * Get the Container associated with our Cluster * * @return The Container associated with our Cluster */ public Container getContainer() { return(this.container); } /** * Sets the configurable protocol stack. This is a setting in server.xml * where you can configure your protocol. * * @param protocol the protocol stack - this method is called by * the server configuration at startup * @see <a href="www.javagroups.com">JavaGroups</a> for details */ public void setProtocol(String protocol) { String oldProtocol = this.mChannelConfig; this.mChannelConfig = protocol; support.firePropertyChange("protocol", oldProtocol, this.mChannelConfig); } /** * Returns the protocol. */ public String getProtocol() { return (this.mChannelConfig); } // --------------------------------------------------------- Public Methods public synchronized Manager createManager(String name) { JGManager manager = new JGManager(); manager.setCluster(this); managers.put(name, manager); return manager; } /** * Sends a SessionMessage to the other nodes in the cluster.<BR> * If the SessionMessage type is EVT_GET_ALL_SESSION we only send it * to one node in the cluster because all the nodes look identical.<BR> * We choose the send this call to the coordinator, a better solution * would be to send this request using a round robin algorithm to split * up the communication load between nodes. * * @param msg - the SessionMessage to be sent, if the message is * of type EVT_GET_ALL_SESSION then we only send to one node, otherwise * we send to everybody * @param sender - the sender of the message */ public void send(SessionMessage msg, IpAddress dest) throws Exception { if (!mChannelStarted) { log.warn("Channel is not active, not sending message=" + msg); return; } // Check the event type, // if it is EVT_GET_ALL_SESSION then // only send the message to the coordinator // otherwise send the message to everybody IpAddress destination = dest; if (msg.getEventType() == SessionMessage.EVT_GET_ALL_SESSIONS) { destination = (IpAddress) mChannel.getView().getMembers().elementAt(0); } Message jgmsg = new Message(destination, mChannel.getLocalAddress(), msg); mChannel.send(jgmsg); } /** * Recieve a message from the JavaGrtoups cluster. */ public void receive(SessionMessage msg, IpAddress sender) { // Discard all message from ourself if (sender.equals(mChannel.getLocalAddress())) { return; } JGManager manager = (JGManager) managers.get(msg.getWebapp()); if (manager != null) { manager.messageReceived(msg, sender); } } // ------------------------------------------------------ Lifecycle Methods /** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } /** * Get the lifecycle listeners associated with this lifecycle. If this * Lifecycle has no listeners registered, a zero-length array is returned. */ public LifecycleListener[] findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } /** * Remove a lifecycle event listener from this component. * * @param listener The listener to remove */ public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); } /** * Prepare for the beginning of active use of the public methods of this * component. This method should be called after <code>configure()</code>, * and before any of the public methods of the component are utilized.<BR> * Starts the cluster communication channel, this will connect with the * other nodes in the cluster, and request the current session state to * be transferred to this node. * * @exception IllegalStateException if this component has already been * started * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ public void start() throws LifecycleException { //start the javagroups channel try { //the channel is already running if (mChannelStarted) return; mChannel = new JChannel(mChannelConfig); mChannelListener = new ReceiverThread(this, mChannel); mChannel.connect("TomcatReplication"); mChannelListener.start(); mChannelStarted = true; log.info("Javagroups channel started inside tomcat"); } catch (Exception x) { log.error("Unable to start javagroups channel",x); } } /** * Gracefully terminate the active use of the public methods of this * component. This method should be the last one called on a given * instance of this component.<BR> * This will disconnect the cluster communication channel and stop * the listener thread. * * @exception IllegalStateException if this component has not been started * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ public void stop() throws LifecycleException { mChannelStarted = false; //stop the javagroup channel try { mChannelListener.stopRunning(); mChannel.disconnect(); mChannel.close(); } catch (Exception x) { log.error("Unable to stop javagroups channel",x); } } /** * Thread that listens to the cluster communication channel. * */ private class ReceiverThread extends Thread { private JGCluster mParentCluster = null; private JChannel mParentChannel = null; private boolean mOkToRun = true; public ReceiverThread(JGCluster parent, JChannel listenTo) { mParentCluster = parent; mParentChannel = listenTo; } public void stopRunning() { mOkToRun = false; } public void run() { while (mOkToRun) { try { // Receive a message from the channel Object obj = mParentChannel.receive(0); // Make sure it is a data message if ((obj != null) && (obj instanceof Message)) { Message msg = (Message)obj; //we are only interested in our own messages ReplicationStream stream = new ReplicationStream (new java.io.ByteArrayInputStream (msg.getBuffer()), getClass().getClassLoader()); Object myobj = stream.readObject(); if (myobj instanceof SessionMessage) { // notify the cluster mParentCluster.receive((SessionMessage) myobj, (IpAddress) msg.getSrc()); } else { mParentCluster.log.info ("Received unwanted message="+obj); } } else if (obj instanceof View) { mParentCluster.log.info ("Received membership message="+obj); } } catch (Exception x) { if (mChannelStarted) mParentCluster.log.warn ("Unable to receive JavaGroup message",x); } } } } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/JGManager.java Index: JGManager.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/JGManager.java,v 1.1 2002/12/05 12:25:09 remm Exp $ * $Revision: 1.1 $ * $Date: 2002/12/05 12:25:09 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster; import org.apache.catalina.LifecycleException; import org.apache.catalina.Session; import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.session.StandardManager; import org.apache.catalina.util.CustomObjectInputStream; import org.javagroups.stack.IpAddress; /** * Title: Tomcat Session Replication for Tomcat<BR> * Description: A very simple straight forward implementation of * session replication of servers in a cluster.<BR> * This session replication is implemented "live". By live * I mean, when a session attribute is added into a session on * Node A a message is broadcasted to other messages * and setAttribute is called on the replicated sessions.<BR> * A full description of this implementation can be found under * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a> * <BR> * Company: www.filip.net<br> * Description: The JGManager is a session manager * that replicated session information in memory. It uses * <a href="www.javagroups.com">JavaGroups</a> as a communication protocol * to ensure guaranteed and ordered message delivery. * JavaGroups also provides a very flexible protocol stack to ensure that the * replication can be used in any environment. * <BR><BR> * The JGManager extends the StandardManager hence it allows * for us to inherit all the basic session management features like * expiration, session listeners, etc. * <BR><BR> * To communicate with other nodes in the cluster, * the JGManager sends out 7 different type of multicast * messages all defined in the SessionMessage class.<BR> * When a session is replicated (not an attribute added/removed) the session * is serialized into a byte array using the StandardSession.readObjectData, * StandardSession.writeObjectData methods. * * @author <a href="mailto:[EMAIL PROTECTED]">Filip Hanik</a> */ public class JGManager extends StandardManager { private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog( JGManager.class ); /** * Associated JavaGroups cluster. */ protected JGCluster cluster = null; /** * Constructor, just calls super() * */ public JGManager() { super(); } /** * Set JavaGroups cluster. */ public void setCluster(JGCluster cluster) { if (this.cluster != null) { return; } this.cluster = cluster; } /** * Creates a HTTP session. * Most of the code in here is copied from the StandardManager. * This is not pretty, yeah I know, but it was necessary since the * StandardManager had hard coded the session instantiation to the a * StandardSession, when we actually want to instantiate a ReplicatedSession<BR> * If the call comes from the Tomcat servlet engine, a SessionMessage goes out to the other * nodes in the cluster that this session has been created. * @param notify - if set to true the other nodes in the cluster will be notified. * This flag is needed so that we can create a session before we deserialize * a replicated one * * @see ReplicatedSession */ protected Session createSession(boolean notify) { //inherited from the basic manager if ((getMaxActiveSessions() >= 0) && (sessions.size() >= getMaxActiveSessions())) throw new IllegalStateException(sm.getString("standardManager.createSession.ise")); // Recycle or create a Session instance Session session = null; //modified to make sure we only recycle sessions that are of type=ReplicatedSession //I personally believe the VM does a much better job pooling object instances //than the synchronized penalty gives us synchronized (recycled) { int size = recycled.size(); int index = size; if (size > 0) { do { index--; session = (Session) recycled.get(index); recycled.remove(index); } while ( index > 0 && (session instanceof ReplicatedSession)); } }//synchronized //set the current manager if (session != null) session.setManager(this); else session = new ReplicatedSession(this); // Initialize the properties of the new session and return it session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); String sessionId = generateSessionId(); String jvmRoute = getJvmRoute(); // @todo Move appending of jvmRoute generateSessionId()??? if (jvmRoute != null) { sessionId += '.' + jvmRoute; session.setId(sessionId); } /* synchronized (sessions) { while (sessions.get(sessionId) != null) // Guarantee uniqueness sessionId = generateSessionId(); } */ session.setId(sessionId); if ( notify ) { log.info("Replicated Session created with ID="+session.getId()); //notify javagroups SessionMessage msg = new SessionMessage (container.getName(), SessionMessage.EVT_SESSION_CREATED, writeSession(session), session.getId(), null, null, null); sendSessionEvent(msg); } return (session); } //========================================================================= // OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION //========================================================================= /** * Construct and return a new session object, based on the default * settings specified by this Manager's properties. The session * id will be assigned by this method, and available via the getId() * method of the returned session. If a new session cannot be created * for any reason, return <code>null</code>. * * @exception IllegalStateException if a new session cannot be * instantiated for any reason */ public Session createSession() { //create a session and notify the other nodes in the cluster Session session = createSession(true); add(session); return session; } /** * Serialize a session into a byte array.<BR> * This method simple calls the writeObjectData method on the session * and returns the byte data from that call. * * @param session - the session to be serialized * @return a byte array containing the session data, null if the * serialization failed */ protected byte[] writeSession(Session session) { ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader(); try { java.io.ByteArrayOutputStream session_data = new java.io.ByteArrayOutputStream(); java.io.ObjectOutputStream session_out = new java.io.ObjectOutputStream(session_data); ClassLoader classLoader = container.getLoader().getClassLoader(); Thread.currentThread().setContextClassLoader(classLoader); ((ReplicatedSession)session).writeObjectData(session_out); session_out.flush(); session_out.close(); return session_data.toByteArray(); } catch ( Exception x ) { log.warn("Failed to serialize the session!",x); } finally { Thread.currentThread().setContextClassLoader(oldCtxClassLoader); } return null; } /** * Reinstantiates a serialized session from the data passed in. * This will first call createSession() so that we get a fresh instance * with all the managers set and all the transient fields validated. * Then it calls Session.readObjectData(byte[]) to deserialize the object. * * @param data - a byte array containing session data * @return a valid Session object, null if an error occurs */ protected Session readSession(byte[] data) { ClassLoader oldCtxClassLoader = Thread.currentThread().getContextClassLoader(); try { java.io.ByteArrayInputStream session_data = new java.io.ByteArrayInputStream(data); ReplicationStream session_in = new ReplicationStream (session_data,container.getLoader().getClassLoader()); Session session = createSession(false); ClassLoader classLoader = container.getLoader().getClassLoader(); Thread.currentThread().setContextClassLoader(classLoader); ((ReplicatedSession)session).readObjectData(session_in); return session; } catch (Exception x) { log.warn("Failed to deserialize the session!",x); } finally { Thread.currentThread().setContextClassLoader(oldCtxClassLoader); } return null; } public void start() throws LifecycleException { super.start(); try { SessionMessage msg = new SessionMessage (container.getName(), SessionMessage.EVT_GET_ALL_SESSIONS, null, null, null, null, null); sendSessionEvent(msg); } catch (Exception x) { log.error("Unable to start manager", x); } } public void stop() throws LifecycleException { super.stop(); } protected void sendSessionEvent(SessionMessage msg) { sendSessionEvent(msg, null); } protected void sendSessionEvent( SessionMessage msg, IpAddress dest ) { try { cluster.send(msg, dest); } catch ( Exception x ) { log.error("Unable to send message through javagroups channel",x); } } /** * This method is called by the received thread when a SessionMessage has * been received from one of the other nodes in the cluster. * * @param msg - the message received * @param sender - the sender of the message, this is used if we receive a * EVT_GET_ALL_SESSION message, so that we only reply to * the requesting node */ public void messageReceived( SessionMessage msg, IpAddress sender ) { try { log.debug("Received SessionMessage of type="+msg.getEventTypeString()); switch ( msg.getEventType() ) { case SessionMessage.EVT_ATTRIBUTE_ADDED: { //add the attribute to the replicated session ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{1} ["+msg.getSessionID() + "] doesn't exist!"); return; }//end if //log("Attribute added to session="+msg.getSessionID()+ " will be inserted into session="+session); //how does the call below affect session binding listeners? session.setAttribute(msg.getAttributeName(),msg.getAttributeValue(),false); break; } case SessionMessage.EVT_ATTRIBUTE_REMOVED_WNOTIFY: case SessionMessage.EVT_ATTRIBUTE_REMOVED_WONOTIFY: { boolean notify = (msg.getEventType() == SessionMessage.EVT_ATTRIBUTE_REMOVED_WNOTIFY); //remove the attribute from the session ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{2} ["+msg.getSessionID() + "] doesn't exist!"); return; }//end if //how does this affect the listeners? session.removeAttribute(msg.getAttributeName(),notify,false); break; } case SessionMessage.EVT_REMOVE_SESSION_NOTE: { //remove the note from the session ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{3} ["+msg.getSessionID() + "] doesn't exist!"); return; }//end if //how does this affect the listeners? session.removeNote(msg.getAttributeName(),false); break; } case SessionMessage.EVT_SET_SESSION_NOTE: { //add the note to the session ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{4} ["+msg.getSessionID() + "] doesn't exist!"); return; }//end if //how does this affect the listeners? session.setNote(msg.getAttributeName(),msg.getAttributeValue(),false); break; } case SessionMessage.EVT_GET_ALL_SESSIONS: { //get a list of all the session from this manager Object[] sessions = findSessions(); for (int i=0; i<sessions.length; i++) { //make sure we only replicate sessions //that are replicatable :) if ( sessions[i] instanceof ReplicatedSession) { ReplicatedSession ses = (ReplicatedSession)sessions[i]; SessionMessage newmsg = new SessionMessage (container.getName(), SessionMessage.EVT_SESSION_CREATED, writeSession(ses), ses.getId(), null, null, null); sendSessionEvent(newmsg,sender); //since the principal doesn't get serialized, we better send it over too if ( ses.getPrincipal() != null ) { SessionMessage pmsg = new SessionMessage (container.getName(), SessionMessage.EVT_SET_USER_PRINCIPAL, null, ses.getId(), null, null, SerializablePrincipal.createPrincipal ((GenericPrincipal)ses.getPrincipal())); sendSessionEvent(pmsg,sender); } } else log.warn("System contains non standard sessions="+sessions[i]); } break; } case SessionMessage.EVT_SESSION_ACCESSED: { //this is so that the replicated session doesn't expire in any //other node ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{5} ["+msg.getSessionID() + "] doesn't exist!"); return; } session.access(false); break; } case SessionMessage.EVT_SET_USER_PRINCIPAL: { //set the user principal ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{6} ["+msg.getSessionID() + "] doesn't exist!"); return; } //log("Setting principal for the session"); GenericPrincipal principal = (msg.getPrincipal()).getPrincipal(getContainer().getRealm()); session.setPrincipal(principal,false); break; } case SessionMessage.EVT_SESSION_CREATED: { //log("Session got replicated="+msg.getSessionID()); Session session = this.readSession(msg.getSession()); session.setManager(this); add(session); break; } case SessionMessage.EVT_SESSION_EXPIRED_WNOTIFY: case SessionMessage.EVT_SESSION_EXPIRED_WONOTIFY: { //session has expired boolean notify = msg.getEventType() == SessionMessage.EVT_SESSION_EXPIRED_WNOTIFY; ReplicatedSession session = (ReplicatedSession)findSession(msg.getSessionID()); if ( session == null ) { log.warn("Replicated session with ID{7} ["+msg.getSessionID() + "] doesn't exist!"); return; } session.expire(notify, false); break; } default: { //we didn't recognize the message type, do nothing break; } }//switch } catch ( Exception x ) { log.error("Unable to receive message through javagroups channel", x); } } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/ReplicatedSession.java Index: ReplicatedSession.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/ReplicatedSession.java,v 1.1 2002/12/05 12:25:09 remm Exp $ * $Revision: 1.1 $ * $Date: 2002/12/05 12:25:09 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.Principal; import org.apache.catalina.Manager; import org.apache.catalina.SessionListener; import org.apache.catalina.realm.GenericPrincipal; import org.apache.catalina.session.StandardSession; /** * Title: Tomcat Session Replication for Tomcat 4.0 <BR> * Description: A very simple straight forward implementation of * session replication of servers in a cluster.<BR> * This session replication is implemented "live". By live * I mean, when a session attribute is added into a session on Node A * a message is broadcasted to other messages and setAttribute is called on the replicated * sessions.<BR> * A full description of this implementation can be found under * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR> * * Copyright: See apache license * Company: www.filip.net * @author <a href="mailto:[EMAIL PROTECTED]">Filip Hanik</a> * @version 1.0 for TC 4.0 * Description:<BR> * The ReplicatedSession class is a simple extension of the StandardSession class * It overrides a few methods (setAttribute, removeAttribute, expire, access) and has * hooks into the JGManager to broadcast and receive events from the cluster.<BR> * This class inherits the readObjectData and writeObject data methods from the StandardSession * and does not contain any serializable elements in addition to the inherited ones from the StandardSession * */ public class ReplicatedSession extends StandardSession { private transient Manager mManager = null; public ReplicatedSession(Manager manager) { super(manager); mManager = manager; } /** * Update the accessed time information for this session. This method * should be called by the context when a request comes in for a particular * session, even if the application does not reference it. * @param notify - if true the other cluster nodes will be notified */ public void access(boolean notify) { super.access(); //notify javagroups that session has been accessed if ( notify ) { SessionMessage msg = new SessionMessage (mManager.getContainer().getName(), SessionMessage.EVT_SESSION_ACCESSED, null, this.getId(), null, null, null); sendMessage(msg); } //log("Access has been called in the session"); } /** * Update the accessed time information for this session. This method * should be called by the context when a request comes in for a particular * session, even if the application does not reference it. */ public void access() { access(true); } /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. * @param notify - the inherited notify from StandardSession * @param jgnotify - if true other nodes in the cluster will be notified */ public void expire(boolean notify, boolean jgnotify) { String id = getId(); super.expire(); //notify javagroups about the expiration if ( jgnotify ) { int event = notify ? SessionMessage.EVT_SESSION_EXPIRED_WNOTIFY : SessionMessage.EVT_SESSION_EXPIRED_WONOTIFY; SessionMessage msg = new SessionMessage(mManager.getContainer().getName(), event, null, id, null, null, null); sendMessage(msg); } log("Expire called on session with jgnotify="+jgnotify+ " and notify="+notify); }//expire /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire() { expire(true,true); } /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire(boolean notify) { expire(notify,true); } /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. * <p> * After this method executes, and if the object implements * <code>HttpSessionBindingListener</code>, the container calls * <code>valueUnbound()</code> on the object. * * @param name Name of the object to remove from this session. * @param notify - notify the listeners in the session * @param jgnotify - notify the other nodes in the cluster * * @exception IllegalStateException if this method is called on an * invalidated session */ public void removeAttribute(String name, boolean notify, boolean jgnotify) { super.removeAttribute(name); if ( jgnotify ) { SessionMessage msg = new SessionMessage(mManager.getContainer().getName(), notify?SessionMessage.EVT_ATTRIBUTE_REMOVED_WNOTIFY:SessionMessage.EVT_ATTRIBUTE_REMOVED_WONOTIFY, null, getId(), name, null, null); sendMessage(msg); } } /** * see parent description, * plus we also notify other nodes in the cluster */ public void removeAttribute(String name, boolean notify) { removeAttribute(name,notify,true); } /** * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. * <p> * After this method executes, and if the object implements * <code>HttpSessionBindingListener</code>, the container calls * <code>valueBound()</code> on the object. * * @param name Name to which the object is bound, cannot be null * @param value Object to be bound, cannot be null * @param notify true to notify the other nodes in the cluster * @exception IllegalArgumentException if an attempt is made to add a * non-serializable object in an environment marked distributable. * @exception IllegalStateException if this method is called on an * invalidated session * @excpetion IllegalArgumentException if the value is not serializable */ public void setAttribute(String name, Object value, boolean notify) { if (!(value instanceof java.io.Serializable)) { throw new java.lang.IllegalArgumentException("Attribute["+name+"] is not serializable."); } super.setAttribute(name,value); //notify javagroups if ( notify) { SessionMessage msg = new SessionMessage(mManager.getContainer().getName(), SessionMessage.EVT_ATTRIBUTE_ADDED, null, getId(), name, value, null); sendMessage(msg); } } /** * Sets an attribute and notifies the other nodes in the cluster */ public void setAttribute(String name, Object value) { setAttribute(name,value,true); } /** * Sets the manager for this session * @param mgr - the servers JGManager */ public void setManager(JGManager mgr) { mManager = mgr; super.setManager(mgr); } /** * Set the authenticated Principal that is associated with this Session. * This provides an <code>Authenticator</code> with a means to cache a * previously authenticated Principal, and avoid potentially expensive * <code>Realm.authenticate()</code> calls on every request. * * @param principal The new Principal, or <code>null</code> if none * @param jgnotify notify the other nodes in the cluster? (true/false) */ public void setPrincipal(Principal principal) { setPrincipal(principal,true); } public void setPrincipal(Principal principal, boolean jgnotify) { //log("setPrincipal called in the ReplicatedSession"); super.setPrincipal(principal); if ( jgnotify) { //log("Sending message"); SessionMessage msg = new SessionMessage(mManager.getContainer().getName(), SessionMessage.EVT_SET_USER_PRINCIPAL, null, getId(), null, null, SerializablePrincipal.createPrincipal((GenericPrincipal)principal)); sendMessage(msg); } } /** * Remove any object bound to the specified name in the internal notes * for this session. * * @param name Name of the note to be removed */ public void removeNote(String name, boolean jgnotify) { super.removeNote(name); if ( jgnotify) { SessionMessage msg = new SessionMessage(mManager.getContainer().getName(), SessionMessage.EVT_REMOVE_SESSION_NOTE, null, getId(), name, null, null); sendMessage(msg); } } public void removeNote(String name) { //disable replication of notes removeNote(name,false); //removeNote(name,true); } /** * Bind an object to a specified name in the internal notes associated * with this session, replacing any existing binding for this name. * * @param name Name to which the object should be bound * @param value Object to be bound to the specified name */ public void setNote(String name, Object value, boolean jgnotify) { super.setNote(name,value); if ( jgnotify ) { SessionMessage msg = new SessionMessage(mManager.getContainer().getName(), SessionMessage.EVT_SET_SESSION_NOTE, null, getId(), name, value, null); sendMessage(msg); } } public void setNote(String name, Object value) { //for now disable replication of notes setNote(name,value,false); //setNote(name,value,true); } /** * Uses the manager to send a message to the other nodes */ private void sendMessage(SessionMessage msg ) { if ( this.mManager != null && this.mManager instanceof JGManager ) { JGManager transport = (JGManager) mManager; transport.sendSessionEvent(msg); } else { log("Not sending session event through javagroups - invalid manager="+mManager); } } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/ReplicationStream.java Index: ReplicationStream.java =================================================================== /* * CustomObjectInputStream.java * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/ReplicationStream.java,v 1.1 2002/12/05 12:25:09 remm Exp $ * $Revision: 1.1 $ * $Date: 2002/12/05 12:25:09 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster; import java.io.InputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; /** * Custom subclass of <code>ObjectInputStream</code> that loads from the * class loader for this web application. This allows classes defined only * with the web application to be found correctly. * * @@author Craig R. McClanahan * @@author Bip Thelin * @@version $Revision: 1.1 $, $Date: 2002/12/05 12:25:09 $ */ public final class ReplicationStream extends ObjectInputStream { /** * The class loader we will use to resolve classes. */ private ClassLoader classLoader = null; /** * Construct a new instance of CustomObjectInputStream * * @@param stream The input stream we will read from * @@param classLoader The class loader used to instantiate objects * * @@exception IOException if an input/output error occurs */ public ReplicationStream(InputStream stream, ClassLoader classLoader) throws IOException { super(stream); this.classLoader = classLoader; } /** * Load the local class equivalent of the specified stream class * description, by using the class loader assigned to this Context. * * @@param classDesc Class description from the input stream * * @@exception ClassNotFoundException if this class cannot be found * @@exception IOException if an input/output error occurs */ public Class resolveClass(ObjectStreamClass classDesc) throws ClassNotFoundException, IOException { try { return (classLoader.loadClass(classDesc.getName())); } catch ( Exception x ) { return getClass().getClassLoader().loadClass(classDesc.getName()); } } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/SerializablePrincipal.java Index: SerializablePrincipal.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/SerializablePrincipal.java,v 1.1 2002/12/05 12:25:09 remm Exp $ * $Revision: 1.1 $ * $Date: 2002/12/05 12:25:09 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster; import java.security.Principal; import java.util.Arrays; import java.util.List; import org.apache.catalina.Realm; import org.apache.catalina.realm.GenericPrincipal; /** * Generic implementation of <strong>java.security.Principal</strong> that * is available for use by <code>Realm</code> implementations. * The GenericPrincipal does NOT implement serializable and I didn't want to change that implementation * hence I implemented this one instead. * @author Filip Hanik * @version $Revision: 1.1 $ $Date: 2002/12/05 12:25:09 $ */ public class SerializablePrincipal implements java.io.Serializable { // ----------------------------------------------------------- Constructors public SerializablePrincipal() { super(); } /** * Construct a new Principal, associated with the specified Realm, for the * specified username and password. * * @param realm The Realm that owns this Principal * @param name The username of the user represented by this Principal * @param password Credentials used to authenticate this user */ public SerializablePrincipal(Realm realm, String name, String password) { this(realm, name, password, null); } /** * Construct a new Principal, associated with the specified Realm, for the * specified username and password, with the specified role names * (as Strings). * * @param realm The Realm that owns this principal * @param name The username of the user represented by this Principal * @param password Credentials used to authenticate this user * @param roles List of roles (must be Strings) possessed by this user */ public SerializablePrincipal(Realm realm, String name, String password, List roles) { super(); this.realm = realm; this.name = name; this.password = password; if (roles != null) { this.roles = new String[roles.size()]; this.roles = (String[]) roles.toArray(this.roles); if (this.roles.length > 0) Arrays.sort(this.roles); } } // ------------------------------------------------------------- Properties /** * The username of the user represented by this Principal. */ protected String name = null; public String getName() { return (this.name); } /** * The authentication credentials for the user represented by * this Principal. */ protected String password = null; public String getPassword() { return (this.password); } /** * The Realm with which this Principal is associated. */ protected transient Realm realm = null; public Realm getRealm() { return (this.realm); } public void setRealm(Realm realm) { this.realm = realm; } /** * The set of roles associated with this user. */ protected String roles[] = new String[0]; public String[] getRoles() { return (this.roles); } // --------------------------------------------------------- Public Methods /** * Return a String representation of this object, which exposes only * information that should be public. */ public String toString() { StringBuffer sb = new StringBuffer("SerializablePrincipal["); sb.append(this.name); sb.append("]"); return (sb.toString()); } public static SerializablePrincipal createPrincipal(GenericPrincipal principal) { if ( principal==null) return null; return new SerializablePrincipal(principal.getRealm(), principal.getName(), principal.getPassword(), principal.getRoles()!=null?Arrays.asList(principal.getRoles()):null); } public GenericPrincipal getPrincipal( Realm realm ) { return new GenericPrincipal(realm,name,password,getRoles()!=null?Arrays.asList(getRoles()):null); } } 1.1 jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/SessionMessage.java Index: SessionMessage.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/cluster/SessionMessage.java,v 1.1 2002/12/05 12:25:09 remm Exp $ * $Revision: 1.1 $ * $Date: 2002/12/05 12:25:09 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster; import java.security.Principal; /** * Title: Tomcat Session Replication for Tomcat 4.0 <BR> * Description: A very simple straight forward implementation of * session replication of servers in a cluster.<BR> * This session replication is implemented "live". By live * I mean, when a session attribute is added into a session on Node A * a message is broadcasted to other messages and setAttribute is called on the replicated * sessions.<BR> * A full description of this implementation can be found under * <href="http://www.filip.net/tomcat/">Filip's Tomcat Page</a><BR> * * Copyright: See apache license * Company: www.filip.net * @author <a href="mailto:[EMAIL PROTECTED]">Filip Hanik</a> * @version 1.0 for 4.0 * * <B>Class Description:</B><BR> * The SessionMessage class is a class that is used when a session has been * created, modified, expired in a Tomcat cluster node.<BR> * * The following events are currently available: * <ul> * <li><pre>public static final int EVT_SESSION_CREATED</pre><li> * <li><pre>public static final int EVT_SESSION_ACCESSED</pre><li> * <li><pre>public static final int EVT_ATTRIBUTE_ADDED</pre><li> * <li><pre>public static final int EVT_ATTRIBUTE_REMOVED</pre><li> * <li><pre>public static final int EVT_SESSION_EXPIRED_WONOTIFY</pre><li> * <li><pre>public static final int EVT_SESSION_EXPIRED_WNOTIFY</pre><li> * <li><pre>public static final int EVT_GET_ALL_SESSIONS</pre><li> * <li><pre>public static final int EVT_SET_USER_PRINCIPAL</pre><li> * <li><pre>public static final int EVT_SET_SESSION_NOTE</pre><li> * <li><pre>public static final int EVT_REMOVE_SESSION_NOTE</pre><li> * </ul> * * These message are being sent and received from and to the * InMemoryReplicationManager * * @see JGManager */ public class SessionMessage implements java.io.Serializable { /** * Event type used when a session has been created on a node */ public static final int EVT_SESSION_CREATED = 1; /** * Event type used when a session has expired, but we don't * want to notify the session listeners */ public static final int EVT_SESSION_EXPIRED_WONOTIFY = 2; /** * Event type used when a session has expired, and we do * want to notify the session listeners */ public static final int EVT_SESSION_EXPIRED_WNOTIFY = 7; /** * Event type used when a session has been accessed (ie, last access time * has been updated. This is used so that the replicated sessions will not expire * on the network */ public static final int EVT_SESSION_ACCESSED = 3; /** * Event type used when a server comes online for the first time. * The first thing the newly started server wants to do is to grab the * all the sessions from one of the nodes and keep the same state in there */ public static final int EVT_GET_ALL_SESSIONS = 4; /** * Event type used when an attribute has been added to a session, * the attribute will be sent to all the other nodes in the cluster */ public static final int EVT_ATTRIBUTE_ADDED = 5; /** * Event type used when an attribute has been removed from a session, * the attribute will be sent to all the other nodes in the cluster */ public static final int EVT_ATTRIBUTE_REMOVED_WONOTIFY = 6; /** * Event type used when an attribute has been removed from a session, * the attribute will be sent to all the other nodes in the cluster */ public static final int EVT_ATTRIBUTE_REMOVED_WNOTIFY = 8; /** * Event type used when a user principal is being cached in the session */ public static final int EVT_SET_USER_PRINCIPAL = 9; /** * Event type used when a user principal is being cached in the session */ public static final int EVT_REMOVE_SESSION_NOTE = 10; /** * Event type used when a user principal is being cached in the session */ public static final int EVT_SET_SESSION_NOTE = 11; /** * Private serializable variables to keep the messages state */ private String webapp; private int mEvtType = -1; private byte[] mSession; private String mSessionID; private String mAttributeName; private Object mAttributeValue; private SerializablePrincipal mPrincipal; /** * Creates a session message. Depending on what event type you want this * message to represent, you populate the different parameters in the constructor<BR> * The following rules apply dependent on what event type argument you use:<BR> * <B>EVT_SESSION_CREATED</B><BR> * The parameters: session, sessionID must be set.<BR> * <B>EVT_SESSION_EXPIRED</B><BR> * The parameters: sessionID must be set.<BR> * <B>EVT_SESSION_ACCESSED</B><BR> * The parameters: sessionID must be set.<BR> * <B>EVT_SESSION_EXPIRED_XXXX</B><BR> * The parameters: sessionID must be set.<BR> * <B>EVT_ATTRIBUTE_ADDED</B><BR> * The parameters: sessionID, attrName, attrValue must be set.<BR> * <B>EVT_ATTRIBUTE_REMOVED</B><BR> * The parameters: sessionID, attrName must be set.<BR> * <B>EVT_SET_USER_PRINCIPAL</B><BR> * The parameters: sessionID, principal<BR> * <B>EVT_REMOVE_SESSION_NOTE</B><BR> * The parameters: sessionID, attrName< * <B>EVT_SET_SESSION_NOTE</B><BR> * The parameters: sessionID, attrName, attrValue< * * @param eventtype - one of the 8 event type defined in this class * @param session - the serialized byte array of the session itself * @param sessionID - the id that identifies this session * @param attrName - the name of the attribute added/removed * @param attrValue - the value of the attribute added */ public SessionMessage( String webapp, int eventtype, byte[] session, String sessionID, String attrName, Object attrValue, SerializablePrincipal principal) { this.webapp = webapp; mEvtType = eventtype; mSession = session; mSessionID = sessionID; mAttributeName = attrName; mAttributeValue = attrValue; mPrincipal = principal; } public String getWebapp() { return (this.webapp); } /** * returns the event type * @return one of the event types EVT_XXXX */ public int getEventType() { return mEvtType; } /** * @return the serialized data for the session */ public byte[] getSession() { return mSession;} /** * @return the session ID for the session */ public String getSessionID(){ return mSessionID; } /** * @return the name of the attribute */ public String getAttributeName() { return mAttributeName; } /** * the value of the attribute */ public Object getAttributeValue() { return mAttributeValue; } public SerializablePrincipal getPrincipal() { return mPrincipal;} /** * @return the event type in a string representating, useful for debugging */ public String getEventTypeString() { switch (mEvtType) { case EVT_SESSION_CREATED : return "SESSION-CREATED"; case EVT_SESSION_EXPIRED_WNOTIFY : return "SESSION-EXPIRED-WITH-NOTIFY"; case EVT_SESSION_EXPIRED_WONOTIFY : return "SESSION-EXPIRED-WITHOUT-NOTIFY"; case EVT_ATTRIBUTE_ADDED : return "SESSION-ATTRIBUTE-ADDED"; case EVT_ATTRIBUTE_REMOVED_WNOTIFY : return "SESSION-ATTRIBUTE-REMOVED-WITH-NOTIFY"; case EVT_ATTRIBUTE_REMOVED_WONOTIFY: return "SESSION-ATTRIBUTE-REMOVED-WITHOUT-NOTIFY"; case EVT_SESSION_ACCESSED : return "SESSION-ACCESSED"; case EVT_GET_ALL_SESSIONS : return "SESSION-GET-ALL"; case EVT_SET_SESSION_NOTE: return "SET-SESSION-NOTE"; case EVT_SET_USER_PRINCIPAL : return "SET-USER-PRINCIPAL"; case EVT_REMOVE_SESSION_NOTE : return "REMOVE-SESSION-NOTE"; default : return "UNKNOWN-EVENT-TYPE"; } } }//SessionMessage
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>