/** * 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); } }