/**
   * Log the interesting request parameters, invoke the next Valve in the sequence, and log the
   * interesting response parameters.
   *
   * @param request The servlet request to be processed
   * @param response The servlet response to be created
   * @exception IOException if an input/output error occurs
   * @exception ServletException if a servlet error occurs
   */
  @Override
  public void invoke(Request request, Response response) throws IOException, ServletException {
    long totalstart = 0;

    // this happens before the request
    if (doStatistics()) {
      totalstart = System.currentTimeMillis();
    }
    if (primaryIndicator) {
      createPrimaryIndicator(request);
    }
    Context context = request.getContext();
    boolean isCrossContext =
        context != null
            && context instanceof StandardContext
            && ((StandardContext) context).getCrossContext();
    try {
      if (isCrossContext) {
        if (log.isDebugEnabled()) log.debug(sm.getString("ReplicationValve.crossContext.add"));
        // FIXME add Pool of Arraylists
        crossContextSessions.set(new ArrayList<DeltaSession>());
      }
      getNext().invoke(request, response);
      if (context != null) {
        Manager manager = context.getManager();
        if (manager != null && manager instanceof ClusterManager) {
          ClusterManager clusterManager = (ClusterManager) manager;
          CatalinaCluster containerCluster = (CatalinaCluster) getContainer().getCluster();
          if (containerCluster == null) {
            if (log.isWarnEnabled()) log.warn(sm.getString("ReplicationValve.nocluster"));
            return;
          }
          // valve cluster can access manager - other cluster handle replication
          // at host level - hopefully!
          if (containerCluster.getManager(clusterManager.getName()) == null) return;
          if (containerCluster.hasMembers()) {
            sendReplicationMessage(
                request, totalstart, isCrossContext, clusterManager, containerCluster);
          } else {
            resetReplicationRequest(request, isCrossContext);
          }
        }
      }
    } finally {
      // Array must be remove: Current master request send endAccess at recycle.
      // Don't register this request session again!
      if (isCrossContext) {
        if (log.isDebugEnabled()) log.debug(sm.getString("ReplicationValve.crossContext.remove"));
        // crossContextSessions.remove() only exist at Java 5
        // register ArrayList at a pool
        crossContextSessions.set(null);
      }
    }
  }
 /**
  * send manager requestCompleted message to cluster
  *
  * @param manager SessionManager
  * @param cluster replication cluster
  * @param sessionId sessionid from the manager
  * @see DeltaManager#requestCompleted(String)
  * @see SimpleTcpCluster#send(ClusterMessage)
  */
 protected void send(ClusterManager manager, CatalinaCluster cluster, String sessionId) {
   ClusterMessage msg = manager.requestCompleted(sessionId);
   if (msg != null) {
     cluster.send(msg);
     if (doStatistics()) nrOfSendRequests++;
   }
 }
 /**
  * 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 {
   mManagerRunning = false;
   mChannelStarted = false;
   super.stop();
   try {
     this.sessions.clear();
     cluster.removeManager(this);
   } catch (Exception x) {
     log.error("Unable to stop SimpleTcpReplicationManager", 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
  */
 protected void messageReceived(SessionMessage msg, Member sender) {
   try {
     if (log.isInfoEnabled()) {
       log.debug("Received SessionMessage of type=" + msg.getEventTypeString());
       log.debug("Received SessionMessage sender=" + sender);
     }
     switch (msg.getEventType()) {
       case SessionMessage.EVT_GET_ALL_SESSIONS:
         {
           // get a list of all the session from this manager
           Object[] sessions = findSessions();
           java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
           java.io.ObjectOutputStream oout = new java.io.ObjectOutputStream(bout);
           oout.writeInt(sessions.length);
           for (int i = 0; i < sessions.length; i++) {
             ReplicatedSession ses = (ReplicatedSession) sessions[i];
             oout.writeUTF(ses.getIdInternal());
             byte[] data = writeSession(ses);
             oout.writeObject(data);
           } // for
           // don't send a message if we don't have to
           oout.flush();
           oout.close();
           byte[] data = bout.toByteArray();
           SessionMessage newmsg =
               new SessionMessageImpl(
                   name,
                   SessionMessage.EVT_ALL_SESSION_DATA,
                   data,
                   "SESSION-STATE",
                   "SESSION-STATE-" + getName());
           cluster.send(newmsg, sender);
           break;
         }
       case SessionMessage.EVT_ALL_SESSION_DATA:
         {
           java.io.ByteArrayInputStream bin = new java.io.ByteArrayInputStream(msg.getSession());
           java.io.ObjectInputStream oin = new java.io.ObjectInputStream(bin);
           int size = oin.readInt();
           for (int i = 0; i < size; i++) {
             String id = oin.readUTF();
             byte[] data = (byte[]) oin.readObject();
             Session session = readSession(data, id);
           } // for
           stateTransferred = true;
           break;
         }
       case SessionMessage.EVT_SESSION_CREATED:
         {
           Session session = this.readSession(msg.getSession(), msg.getSessionID());
           if (log.isDebugEnabled()) {
             log.debug("Received replicated session=" + session + " isValid=" + session.isValid());
           }
           break;
         }
       case SessionMessage.EVT_SESSION_EXPIRED:
         {
           Session session = findSession(msg.getSessionID());
           if (session != null) {
             session.expire();
             this.remove(session);
           } // end if
           break;
         }
       case SessionMessage.EVT_SESSION_ACCESSED:
         {
           Session session = findSession(msg.getSessionID());
           if (session != null) {
             session.access();
             session.endAccess();
           }
           break;
         }
       default:
         {
           // we didn't recognize the message type, do nothing
           break;
         }
     } // switch
   } catch (Exception x) {
     log.error("Unable to receive message through TCP channel", x);
   }
 }
  /**
   * 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 {
    mManagerRunning = true;
    super.start();
    try {
      // the channel is already running
      if (mChannelStarted) return;
      if (log.isInfoEnabled()) log.info("Starting clustering manager...:" + getName());
      if (cluster == null) {
        log.error("Starting... no cluster associated with this context:" + getName());
        return;
      }
      cluster.registerManager(this);

      if (cluster.getMembers().length > 0) {
        Member mbr = cluster.getMembers()[0];
        SessionMessage msg =
            new SessionMessageImpl(
                this.getName(),
                SessionMessage.EVT_GET_ALL_SESSIONS,
                null,
                "GET-ALL",
                "GET-ALL-" + this.getName());
        cluster.send(msg, mbr);
        if (log.isWarnEnabled())
          log.warn(
              "Manager["
                  + getName()
                  + "], requesting session state from "
                  + mbr
                  + ". This operation will timeout if no session state has been received within "
                  + "60 seconds");
        long reqStart = System.currentTimeMillis();
        long reqNow = 0;
        boolean isTimeout = false;
        do {
          try {
            Thread.sleep(100);
          } catch (Exception sleep) {
          }
          reqNow = System.currentTimeMillis();
          isTimeout = ((reqNow - reqStart) > (1000 * 60));
        } while ((!isStateTransferred()) && (!isTimeout));
        if (isTimeout || (!isStateTransferred())) {
          log.error("Manager[" + getName() + "], No session state received, timing out.");
        } else {
          if (log.isInfoEnabled())
            log.info(
                "Manager["
                    + getName()
                    + "], session state received in "
                    + (reqNow - reqStart)
                    + " ms.");
        }
      } else {
        if (log.isInfoEnabled())
          log.info(
              "Manager["
                  + getName()
                  + "], skipping state transfer. No members active in cluster group.");
      } // end if
      mChannelStarted = true;
    } catch (Exception x) {
      log.error("Unable to start SimpleTcpReplicationManager", x);
    }
  }