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>&lt;description&gt;/&lt;version&gt;</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]>

Reply via email to