Author: jfrederic.clere(a)jboss.com
Date: 2007-11-01 09:15:22 -0400 (Thu, 01 Nov 2007)
New Revision: 331
Added:
sandbox/tomcat/tomcat6/cluster.patch
Log:
Patch to remove the cluster.
Added: sandbox/tomcat/tomcat6/cluster.patch
===================================================================
--- sandbox/tomcat/tomcat6/cluster.patch (rev 0)
+++ sandbox/tomcat/tomcat6/cluster.patch 2007-11-01 13:15:22 UTC (rev 331)
@@ -0,0 +1,35186 @@
+Index: java/org/apache/catalina/ha/session/BackupManager.java
+===================================================================
+--- java/org/apache/catalina/ha/session/BackupManager.java (revision 590752)
++++ java/org/apache/catalina/ha/session/BackupManager.java (working copy)
+@@ -1,308 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.session;
+-
+-import java.io.ByteArrayInputStream;
+-import java.io.IOException;
+-import java.util.Iterator;
+-
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.session.StandardManager;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.io.ReplicationStream;
+-import org.apache.catalina.tribes.tipis.LazyReplicatedMap;
+-import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
+-import org.apache.catalina.tribes.tipis.AbstractReplicatedMap;
+-
+-/**
+- *@author Filip Hanik
+- *@version 1.0
+- */
+-public class BackupManager extends StandardManager implements ClusterManager, MapOwner
+-{
+- public static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( BackupManager.class );
+-
+- protected static long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
+-
+- /** Set to true if we don't want the sessions to expire on shutdown */
+- protected boolean mExpireSessionsOnShutdown = true;
+-
+- /**
+- * The name of this manager
+- */
+- protected String name;
+-
+- /**
+- * A reference to the cluster
+- */
+- protected CatalinaCluster cluster;
+-
+- /**
+- * Should listeners be notified?
+- */
+- private boolean notifyListenersOnReplication;
+- /**
+- *
+- */
+- private int mapSendOptions =
Channel.SEND_OPTIONS_SYNCHRONIZED_ACK|Channel.SEND_OPTIONS_USE_ACK;
+-
+- /**
+- * Constructor, just calls super()
+- *
+- */
+- public BackupManager() {
+- super();
+- }
+-
+-
+-//******************************************************************************/
+-// ClusterManager Interface
+-//******************************************************************************/
+-
+- public void messageDataReceived(ClusterMessage msg) {
+- }
+-
+- public boolean doDomainReplication() {
+- return false;
+- }
+-
+- /**
+- * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+- */
+- public void setDomainReplication(boolean sendClusterDomainOnly) {
+- }
+-
+- /**
+- * @return Returns the defaultMode.
+- */
+- public boolean isDefaultMode() {
+- return false;
+- }
+- /**
+- * @param defaultMode The defaultMode to set.
+- */
+- public void setDefaultMode(boolean defaultMode) {
+- }
+-
+- public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)
+- {
+- mExpireSessionsOnShutdown = expireSessionsOnShutdown;
+- }
+-
+- public void setCluster(CatalinaCluster cluster) {
+- if(log.isDebugEnabled())
+- log.debug("Cluster associated with SimpleTcpReplicationManager");
+- this.cluster = cluster;
+- }
+-
+- public boolean getExpireSessionsOnShutdown()
+- {
+- return mExpireSessionsOnShutdown;
+- }
+-
+-
+- /**
+- * Override persistence since they don't go hand in hand with replication for
now.
+- */
+- public void unload() throws IOException {
+- }
+-
+- public ClusterMessage requestCompleted(String sessionId) {
+- if ( !this.started ) return null;
+- LazyReplicatedMap map = (LazyReplicatedMap)sessions;
+- map.replicate(sessionId,false);
+- return null;
+- }
+-
+-
+-//=========================================================================
+-// OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION
+-//=========================================================================
+- public void objectMadePrimay(Object key, Object value) {
+- if (value!=null && value instanceof DeltaSession) {
+- DeltaSession session = (DeltaSession)value;
+- synchronized (session) {
+- session.access();
+- session.endAccess();
+- }
+- }
+- }
+-
+- public Session createEmptySession() {
+- return new DeltaSession(this);
+- }
+-
+- public ClassLoader[] getClassLoaders() {
+- return ClusterManagerBase.getClassLoaders(this.container);
+- }
+-
+- /**
+- * Open Stream and use correct ClassLoader (Container) Switch
+- * ThreadClassLoader
+- *
+- * @param data
+- * @return The object input stream
+- * @throws IOException
+- */
+- public ReplicationStream getReplicationStream(byte[] data) throws IOException {
+- return getReplicationStream(data,0,data.length);
+- }
+-
+- public ReplicationStream getReplicationStream(byte[] data, int offset, int length)
throws IOException {
+- ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);
+- return new ReplicationStream(fis, getClassLoaders());
+- }
+-
+-
+-
+-
+- public String getName() {
+- return this.name;
+- }
+- /**
+- * 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 {
+- if ( this.started ) return;
+-
+- try {
+- cluster.registerManager(this);
+- CatalinaCluster catclust = (CatalinaCluster)cluster;
+- LazyReplicatedMap map = new LazyReplicatedMap(this,
+- catclust.getChannel(),
+- DEFAULT_REPL_TIMEOUT,
+- getMapName(),
+- getClassLoaders());
+- map.setChannelSendOptions(mapSendOptions);
+- this.sessions = map;
+- super.start();
+- this.started = true;
+- } catch ( Exception x ) {
+- log.error("Unable to start BackupManager",x);
+- throw new LifecycleException("Failed to start BackupManager",x);
+- }
+- }
+-
+- public String getMapName() {
+- CatalinaCluster catclust = (CatalinaCluster)cluster;
+- String name =
catclust.getManagerName(getName(),this)+"-"+"map";
+- if ( log.isDebugEnabled() ) log.debug("Backup manager, Setting map name
to:"+name);
+- return name;
+- }
+-
+- /**
+- * 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
+- {
+-
+- LazyReplicatedMap map = (LazyReplicatedMap)sessions;
+- if ( map!=null ) {
+- map.breakdown();
+- }
+- if ( !this.started ) return;
+- try {
+- } catch ( Exception x ){
+- log.error("Unable to stop BackupManager",x);
+- throw new LifecycleException("Failed to stop BackupManager",x);
+- } finally {
+- super.stop();
+- }
+- cluster.removeManager(this);
+-
+- }
+-
+- public void setDistributable(boolean dist) {
+- this.distributable = dist;
+- }
+-
+- public boolean getDistributable() {
+- return distributable;
+- }
+-
+- public void setName(String name) {
+- this.name = name;
+- }
+- public boolean isNotifyListenersOnReplication() {
+- return notifyListenersOnReplication;
+- }
+- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+- this.notifyListenersOnReplication = notifyListenersOnReplication;
+- }
+-
+- public void setMapSendOptions(int mapSendOptions) {
+- this.mapSendOptions = mapSendOptions;
+- }
+-
+- /*
+- * @see org.apache.catalina.ha.ClusterManager#getCluster()
+- */
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- public int getMapSendOptions() {
+- return mapSendOptions;
+- }
+-
+- public String[] getInvalidatedSessions() {
+- return new String[0];
+- }
+-
+- public ClusterManager cloneFromTemplate() {
+- BackupManager result = new BackupManager();
+- result.mExpireSessionsOnShutdown = mExpireSessionsOnShutdown;
+- result.name = "Clone-from-"+name;
+- result.cluster = cluster;
+- result.notifyListenersOnReplication = notifyListenersOnReplication;
+- result.mapSendOptions = mapSendOptions;
+- return result;
+- }
+-
+- public int getActiveSessionsFull() {
+- LazyReplicatedMap map = (LazyReplicatedMap)sessions;
+- return map.sizeFull();
+- }
+-
+- public String listSessionIdsFull() {
+- StringBuffer sb=new StringBuffer();
+- LazyReplicatedMap map = (LazyReplicatedMap)sessions;
+- Iterator keys = map.keySetFull().iterator();
+- while (keys.hasNext()) {
+- sb.append(keys.next()).append(" ");
+- }
+- return sb.toString();
+- }
+-}
+Index: java/org/apache/catalina/ha/session/DeltaRequest.java
+===================================================================
+--- java/org/apache/catalina/ha/session/DeltaRequest.java (revision 590752)
++++ java/org/apache/catalina/ha/session/DeltaRequest.java (working copy)
+@@ -1,387 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha.session;
+-
+-/**
+- * This class is used to track the series of actions that happens when
+- * a request is executed. These actions will then translate into invokations of methods
+- * on the actual session.
+- * This class is NOT thread safe. One DeltaRequest per session
+- * @author <a href="mailto:fhanik@apache.org">Filip Hanik</a>
+- * @version 1.0
+- */
+-
+-import java.io.Externalizable;
+-import java.security.Principal;
+-import java.util.LinkedList;
+-
+-import org.apache.catalina.realm.GenericPrincipal;
+-import org.apache.catalina.util.StringManager;
+-import java.io.ByteArrayOutputStream;
+-import java.io.IOException;
+-import java.io.ObjectOutputStream;
+-
+-
+-public class DeltaRequest implements Externalizable {
+-
+- public static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog( DeltaRequest.class );
+-
+- /**
+- * The string manager for this package.
+- */
+- protected static StringManager sm = StringManager
+- .getManager(Constants.Package);
+-
+- public static final int TYPE_ATTRIBUTE = 0;
+- public static final int TYPE_PRINCIPAL = 1;
+- public static final int TYPE_ISNEW = 2;
+- public static final int TYPE_MAXINTERVAL = 3;
+-
+- public static final int ACTION_SET = 0;
+- public static final int ACTION_REMOVE = 1;
+-
+- public static final String NAME_PRINCIPAL = "__SET__PRINCIPAL__";
+- public static final String NAME_MAXINTERVAL = "__SET__MAXINTERVAL__";
+- public static final String NAME_ISNEW = "__SET__ISNEW__";
+-
+- private String sessionId;
+- private LinkedList actions = new LinkedList();
+- private LinkedList actionPool = new LinkedList();
+-
+- private boolean recordAllActions = false;
+-
+- public DeltaRequest() {
+-
+- }
+-
+- public DeltaRequest(String sessionId, boolean recordAllActions) {
+- this.recordAllActions=recordAllActions;
+- if(sessionId != null)
+- setSessionId(sessionId);
+- }
+-
+-
+- public void setAttribute(String name, Object value) {
+- int action = (value==null)?ACTION_REMOVE:ACTION_SET;
+- addAction(TYPE_ATTRIBUTE,action,name,value);
+- }
+-
+- public void removeAttribute(String name) {
+- int action = ACTION_REMOVE;
+- addAction(TYPE_ATTRIBUTE,action,name,null);
+- }
+-
+- public void setMaxInactiveInterval(int interval) {
+- int action = ACTION_SET;
+- addAction(TYPE_MAXINTERVAL,action,NAME_MAXINTERVAL,new Integer(interval));
+- }
+-
+- /**
+- * convert principal at SerializablePrincipal for backup nodes.
+- * Only support principals from type {@link GenericPrincipal GenericPrincipal}
+- * @param p Session principal
+- * @see GenericPrincipal
+- */
+- public void setPrincipal(Principal p) {
+- int action = (p==null)?ACTION_REMOVE:ACTION_SET;
+- SerializablePrincipal sp = null;
+- if ( p != null ) {
+- if(p instanceof GenericPrincipal) {
+- sp = SerializablePrincipal.createPrincipal((GenericPrincipal)p);
+- if(log.isDebugEnabled())
+- log.debug(sm.getString("deltaRequest.showPrincipal",
p.getName() , getSessionId()));
+- } else
+-
log.error(sm.getString("deltaRequest.wrongPrincipalClass",p.getClass().getName()));
+- }
+- addAction(TYPE_PRINCIPAL,action,NAME_PRINCIPAL,sp);
+- }
+-
+- public void setNew(boolean n) {
+- int action = ACTION_SET;
+- addAction(TYPE_ISNEW,action,NAME_ISNEW,new Boolean(n));
+- }
+-
+- protected synchronized void addAction(int type,
+- int action,
+- String name,
+- Object value) {
+- AttributeInfo info = null;
+- if ( this.actionPool.size() > 0 ) {
+- try {
+- info = (AttributeInfo) actionPool.removeFirst();
+- }catch ( Exception x ) {
+- log.error("Unable to remove element:",x);
+- info = new AttributeInfo(type, action, name, value);
+- }
+- info.init(type,action,name,value);
+- } else {
+- info = new AttributeInfo(type, action, name, value);
+- }
+- //if we have already done something to this attribute, make sure
+- //we don't send multiple actions across the wire
+- if ( !recordAllActions) {
+- try {
+- actions.remove(info);
+- } catch (java.util.NoSuchElementException x) {
+- //do nothing, we wanted to remove it anyway
+- }
+- }
+- //add the action
+- actions.addLast(info);
+- }
+-
+- public void execute(DeltaSession session) {
+- execute(session,true);
+- }
+-
+- public synchronized void execute(DeltaSession session, boolean notifyListeners) {
+- if ( !this.sessionId.equals( session.getId() ) )
+- throw new java.lang.IllegalArgumentException("Session id mismatch, not
executing the delta request");
+- session.access();
+- for ( int i=0; i<actions.size(); i++ ) {
+- AttributeInfo info = (AttributeInfo)actions.get(i);
+- switch ( info.getType() ) {
+- case TYPE_ATTRIBUTE: {
+- if ( info.getAction() == ACTION_SET ) {
+- if ( log.isTraceEnabled() )
log.trace("Session.setAttribute('"+info.getName()+"',
'"+info.getValue()+"')");
+- session.setAttribute(info.getName(),
info.getValue(),notifyListeners,false);
+- } else {
+- if ( log.isTraceEnabled() )
log.trace("Session.removeAttribute('"+info.getName()+"')");
+- session.removeAttribute(info.getName(),notifyListeners,false);
+- }
+-
+- break;
+- }//case
+- case TYPE_ISNEW: {
+- if ( log.isTraceEnabled() )
log.trace("Session.setNew('"+info.getValue()+"')");
+- session.setNew(((Boolean)info.getValue()).booleanValue(),false);
+- break;
+- }//case
+- case TYPE_MAXINTERVAL: {
+- if ( log.isTraceEnabled() )
log.trace("Session.setMaxInactiveInterval('"+info.getValue()+"')");
+-
session.setMaxInactiveInterval(((Integer)info.getValue()).intValue(),false);
+- break;
+- }//case
+- case TYPE_PRINCIPAL: {
+- Principal p = null;
+- if ( info.getAction() == ACTION_SET ) {
+- SerializablePrincipal sp =
(SerializablePrincipal)info.getValue();
+- p =
(Principal)sp.getPrincipal(session.getManager().getContainer().getRealm());
+- }
+- session.setPrincipal(p,false);
+- break;
+- }//case
+- default : throw new java.lang.IllegalArgumentException("Invalid
attribute info type="+info);
+- }//switch
+- }//for
+- session.endAccess();
+- reset();
+- }
+-
+- public synchronized void reset() {
+- while ( actions.size() > 0 ) {
+- try {
+- AttributeInfo info = (AttributeInfo) actions.removeFirst();
+- info.recycle();
+- actionPool.addLast(info);
+- }catch ( Exception x ) {
+- log.error("Unable to remove element",x);
+- }
+- }
+- actions.clear();
+- }
+-
+- public String getSessionId() {
+- return sessionId;
+- }
+- public void setSessionId(String sessionId) {
+- this.sessionId = sessionId;
+- if ( sessionId == null ) {
+- new Exception("Session Id is null for
setSessionId").fillInStackTrace().printStackTrace();
+- }
+- }
+- public int getSize() {
+- return actions.size();
+- }
+-
+- public synchronized void clear() {
+- actions.clear();
+- actionPool.clear();
+- }
+-
+- public synchronized void readExternal(java.io.ObjectInput in) throws
IOException,ClassNotFoundException {
+- //sessionId - String
+- //recordAll - boolean
+- //size - int
+- //AttributeInfo - in an array
+- reset();
+- sessionId = in.readUTF();
+- recordAllActions = in.readBoolean();
+- int cnt = in.readInt();
+- if (actions == null)
+- actions = new LinkedList();
+- else
+- actions.clear();
+- for (int i = 0; i < cnt; i++) {
+- AttributeInfo info = null;
+- if (this.actionPool.size() > 0) {
+- try {
+- info = (AttributeInfo) actionPool.removeFirst();
+- } catch ( Exception x ) {
+- log.error("Unable to remove element",x);
+- info = new AttributeInfo(-1,-1,null,null);
+- }
+- }
+- else {
+- info = new AttributeInfo(-1,-1,null,null);
+- }
+- info.readExternal(in);
+- actions.addLast(info);
+- }//for
+- }
+-
+-
+-
+- public synchronized void writeExternal(java.io.ObjectOutput out ) throws
java.io.IOException {
+- //sessionId - String
+- //recordAll - boolean
+- //size - int
+- //AttributeInfo - in an array
+- out.writeUTF(getSessionId());
+- out.writeBoolean(recordAllActions);
+- out.writeInt(getSize());
+- for ( int i=0; i<getSize(); i++ ) {
+- AttributeInfo info = (AttributeInfo)actions.get(i);
+- info.writeExternal(out);
+- }
+- }
+-
+- /**
+- * serialize DeltaRequest
+- * @see DeltaRequest#writeExternal(java.io.ObjectOutput)
+- *
+- * @param deltaRequest
+- * @return serialized delta request
+- * @throws IOException
+- */
+- protected byte[] serialize() throws IOException {
+- ByteArrayOutputStream bos = new ByteArrayOutputStream();
+- ObjectOutputStream oos = new ObjectOutputStream(bos);
+- writeExternal(oos);
+- oos.flush();
+- oos.close();
+- return bos.toByteArray();
+- }
+-
+- private static class AttributeInfo implements java.io.Externalizable {
+- private String name = null;
+- private Object value = null;
+- private int action;
+- private int type;
+-
+- public AttributeInfo() {}
+-
+- public AttributeInfo(int type,
+- int action,
+- String name,
+- Object value) {
+- super();
+- init(type,action,name,value);
+- }
+-
+- public void init(int type,
+- int action,
+- String name,
+- Object value) {
+- this.name = name;
+- this.value = value;
+- this.action = action;
+- this.type = type;
+- }
+-
+- public int getType() {
+- return type;
+- }
+-
+- public int getAction() {
+- return action;
+- }
+-
+- public Object getValue() {
+- return value;
+- }
+- public int hashCode() {
+- return name.hashCode();
+- }
+-
+- public String getName() {
+- return name;
+- }
+-
+- public void recycle() {
+- name = null;
+- value = null;
+- type=-1;
+- action=-1;
+- }
+-
+- public boolean equals(Object o) {
+- if ( ! (o instanceof AttributeInfo ) ) return false;
+- AttributeInfo other = (AttributeInfo)o;
+- return other.getName().equals(this.getName());
+- }
+-
+- public synchronized void readExternal(java.io.ObjectInput in ) throws
IOException,ClassNotFoundException {
+- //type - int
+- //action - int
+- //name - String
+- //hasvalue - boolean
+- //value - object
+- type = in.readInt();
+- action = in.readInt();
+- name = in.readUTF();
+- boolean hasValue = in.readBoolean();
+- if ( hasValue ) value = in.readObject();
+- }
+-
+- public synchronized void writeExternal(java.io.ObjectOutput out) throws
IOException {
+- //type - int
+- //action - int
+- //name - String
+- //hasvalue - boolean
+- //value - object
+- out.writeInt(getType());
+- out.writeInt(getAction());
+- out.writeUTF(getName());
+- out.writeBoolean(getValue()!=null);
+- if (getValue()!=null) out.writeObject(getValue());
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("AttributeInfo[type=");
+- buf.append(getType()).append(", action=").append(getAction());
+- buf.append(", name=").append(getName()).append(",
value=").append(getValue());
+- buf.append(",
addr=").append(super.toString()).append("]");
+- return buf.toString();
+- }
+-
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/session/SessionIDMessage.java
+===================================================================
+--- java/org/apache/catalina/ha/session/SessionIDMessage.java (revision 590752)
++++ java/org/apache/catalina/ha/session/SessionIDMessage.java (working copy)
+@@ -1,130 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.session;
+-
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.ClusterMessageBase;
+-
+-/**
+- * Session id change cluster message
+- *
+- * @author Peter Rossbach
+- *
+- * @version $Revision$ $Date$
+- */
+-public class SessionIDMessage extends ClusterMessageBase implements ClusterMessage {
+-
+- private int messageNumber;
+-
+- private String orignalSessionID;
+-
+- private String backupSessionID;
+-
+- private String host ;
+- private String contextPath;
+-
+- public String getUniqueId() {
+- StringBuffer result = new StringBuffer(getOrignalSessionID());
+- result.append("#-#");
+- result.append(getHost());
+- result.append("#-#");
+- result.append(getContextPath());
+- result.append("#-#");
+- result.append(getMessageNumber());
+- result.append("#-#");
+- result.append(System.currentTimeMillis());
+- return result.toString();
+- }
+-
+- /**
+- * @return Returns the host.
+- */
+- public String getHost() {
+- return host;
+- }
+-
+- /**
+- * @param host The host to set.
+- */
+- public void setHost(String host) {
+- this.host = host;
+- }
+-
+- /**
+- * @return Returns the contextPath.
+- */
+- public String getContextPath() {
+- return contextPath;
+- }
+- /**
+- * @param contextPath The contextPath to set.
+- */
+- public void setContextPath(String contextPath) {
+- this.contextPath = contextPath;
+- }
+- /**
+- * @return Returns the messageNumber.
+- */
+- public int getMessageNumber() {
+- return messageNumber;
+- }
+-
+- /**
+- * @param messageNumber
+- * The messageNumber to set.
+- */
+- public void setMessageNumber(int messageNumber) {
+- this.messageNumber = messageNumber;
+- }
+-
+-
+- /**
+- * @return Returns the backupSessionID.
+- */
+- public String getBackupSessionID() {
+- return backupSessionID;
+- }
+-
+- /**
+- * @param backupSessionID
+- * The backupSessionID to set.
+- */
+- public void setBackupSessionID(String backupSessionID) {
+- this.backupSessionID = backupSessionID;
+- }
+-
+- /**
+- * @return Returns the orignalSessionID.
+- */
+- public String getOrignalSessionID() {
+- return orignalSessionID;
+- }
+-
+- /**
+- * @param orignalSessionID
+- * The orignalSessionID to set.
+- */
+- public void setOrignalSessionID(String orignalSessionID) {
+- this.orignalSessionID = orignalSessionID;
+- }
+-
+- public String toString() {
+- return "SESSIONID-UPDATE#" + getHost() + "." + getContextPath()
+ "#" + getOrignalSessionID() + ":" + getBackupSessionID();
+- }
+-
+-}
+-
+Index: java/org/apache/catalina/ha/session/ClusterManagerBase.java
+===================================================================
+--- java/org/apache/catalina/ha/session/ClusterManagerBase.java (revision 590752)
++++ java/org/apache/catalina/ha/session/ClusterManagerBase.java (working copy)
+@@ -1,75 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.session;
+-
+-import org.apache.catalina.ha.ClusterManager;
+-import java.beans.PropertyChangeListener;
+-import org.apache.catalina.Lifecycle;
+-import org.apache.catalina.session.ManagerBase;
+-import org.apache.catalina.Loader;
+-import java.io.ByteArrayInputStream;
+-import java.io.IOException;
+-import org.apache.catalina.tribes.io.ReplicationStream;
+-import org.apache.catalina.Container;
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- */
+-
+-public abstract class ClusterManagerBase extends ManagerBase implements Lifecycle,
PropertyChangeListener, ClusterManager{
+-
+-
+- public static ClassLoader[] getClassLoaders(Container container) {
+- Loader loader = null;
+- ClassLoader classLoader = null;
+- if (container != null) loader = container.getLoader();
+- if (loader != null) classLoader = loader.getClassLoader();
+- else classLoader = Thread.currentThread().getContextClassLoader();
+- if ( classLoader == Thread.currentThread().getContextClassLoader() ) {
+- return new ClassLoader[] {classLoader};
+- } else {
+- return new ClassLoader[]
{classLoader,Thread.currentThread().getContextClassLoader()};
+- }
+- }
+-
+-
+- public ClassLoader[] getClassLoaders() {
+- return getClassLoaders(container);
+- }
+-
+- /**
+- * Open Stream and use correct ClassLoader (Container) Switch
+- * ThreadClassLoader
+- *
+- * @param data
+- * @return The object input stream
+- * @throws IOException
+- */
+- public ReplicationStream getReplicationStream(byte[] data) throws IOException {
+- return getReplicationStream(data,0,data.length);
+- }
+-
+- public ReplicationStream getReplicationStream(byte[] data, int offset, int length)
throws IOException {
+- ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);
+- return new ReplicationStream(fis, getClassLoaders());
+- }
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/ha/session/SerializablePrincipal.java
+===================================================================
+--- java/org/apache/catalina/ha/session/SerializablePrincipal.java (revision 590752)
++++ java/org/apache/catalina/ha/session/SerializablePrincipal.java (working copy)
+@@ -1,193 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha.session;
+-
+-
+-import java.util.Arrays;
+-import java.util.List;
+-import org.apache.catalina.Realm;
+-
+-
+-/**
+- * 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$ $Date$
+- */
+-import org.apache.catalina.realm.GenericPrincipal;
+-import java.io.ObjectInput;
+-import java.io.ObjectOutput;
+-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);
+- }
+-
+- public static GenericPrincipal readPrincipal(ObjectInput in, Realm realm) throws
java.io.IOException{
+- String name = in.readUTF();
+- boolean hasPwd = in.readBoolean();
+- String pwd = null;
+- if ( hasPwd ) pwd = in.readUTF();
+- int size = in.readInt();
+- String[] roles = new String[size];
+- for ( int i=0; i<size; i++ ) roles[i] = in.readUTF();
+- return new GenericPrincipal(realm,name,pwd,Arrays.asList(roles));
+- }
+-
+- public static void writePrincipal(GenericPrincipal p, ObjectOutput out) throws
java.io.IOException {
+- out.writeUTF(p.getName());
+- out.writeBoolean(p.getPassword()!=null);
+- if ( p.getPassword()!= null ) out.writeUTF(p.getPassword());
+- String[] roles = p.getRoles();
+- if ( roles == null ) roles = new String[0];
+- out.writeInt(roles.length);
+- for ( int i=0; i<roles.length; i++ ) out.writeUTF(roles[i]);
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/ha/session/SessionMessageImpl.java
+===================================================================
+--- java/org/apache/catalina/ha/session/SessionMessageImpl.java (revision 590752)
++++ java/org/apache/catalina/ha/session/SessionMessageImpl.java (working copy)
+@@ -1,159 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.session;
+-
+-
+-import org.apache.catalina.ha.ClusterMessageBase;
+-
+-/**
+- * Session cluster message
+- *
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- *
+- * @version $Revision$ $Date$
+- */
+-public class SessionMessageImpl extends ClusterMessageBase implements SessionMessage,
java.io.Serializable {
+-
+- public SessionMessageImpl() {
+- }
+-
+-
+- /*
+-
+- * Private serializable variables to keep the messages state
+- */
+- private int mEvtType = -1;
+- private byte[] mSession;
+- private String mSessionID;
+-
+- private String mContextName;
+- private long serializationTimestamp;
+- private boolean timestampSet = false ;
+- private String uniqueId;
+-
+-
+- private SessionMessageImpl( String contextName,
+- int eventtype,
+- byte[] session,
+- String sessionID)
+- {
+- mEvtType = eventtype;
+- mSession = session;
+- mSessionID = sessionID;
+- mContextName = contextName;
+- uniqueId = sessionID;
+- }
+-
+- /**
+- * 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_SESSION_DELTA</B><BR>
+- * Send attribute delta (add,update,remove attribute or principal,
...).<BR>
+- * <B>EVT_ALL_SESSION_DATA</B><BR>
+- * Send complete serializes session list<BR>
+- * <B>EVT_ALL_SESSION_TRANSFERCOMPLETE</B><BR>
+- * send that all session state information are transfered
+- * after GET_ALL_SESSION received from this sender.<BR>
+- * @param contextName - the name of the context (application
+- * @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 uniqueID - the id that identifies this message
+- */
+- public SessionMessageImpl( String contextName,
+- int eventtype,
+- byte[] session,
+- String sessionID,
+- String uniqueID)
+- {
+- this(contextName,eventtype,session,sessionID);
+- uniqueId = uniqueID;
+- }
+-
+- /**
+- * 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; }
+-
+- /**
+- * set message send time but only the first setting works (one shot)
+- */
+- public void setTimestamp(long time) {
+- synchronized(this) {
+- if(!timestampSet) {
+- serializationTimestamp=time;
+- timestampSet = true ;
+- }
+- }
+- }
+-
+- public long getTimestamp() { return serializationTimestamp;}
+-
+- /**
+- * clear text event type name (for logging purpose only)
+- * @return the event type in a string representating, useful for debugging
+- */
+- public String getEventTypeString()
+- {
+- switch (mEvtType)
+- {
+- case EVT_SESSION_CREATED : return "SESSION-MODIFIED";
+- case EVT_SESSION_EXPIRED : return "SESSION-EXPIRED";
+- case EVT_SESSION_ACCESSED : return "SESSION-ACCESSED";
+- case EVT_GET_ALL_SESSIONS : return "SESSION-GET-ALL";
+- case EVT_SESSION_DELTA : return "SESSION-DELTA";
+- case EVT_ALL_SESSION_DATA : return "ALL-SESSION-DATA";
+- case EVT_ALL_SESSION_TRANSFERCOMPLETE : return
"SESSION-STATE-TRANSFERED";
+- default : return "UNKNOWN-EVENT-TYPE";
+- }
+- }
+-
+- public String getContextName() {
+- return mContextName;
+- }
+- public String getUniqueId() {
+- return uniqueId;
+- }
+- public void setUniqueId(String uniqueId) {
+- this.uniqueId = uniqueId;
+- }
+-
+- public String toString() {
+- return getEventTypeString() + "#" + getContextName() + "#" +
getSessionID() ;
+- }
+-}
+Index: java/org/apache/catalina/ha/session/Constants.java
+===================================================================
+--- java/org/apache/catalina/ha/session/Constants.java (revision 590752)
++++ java/org/apache/catalina/ha/session/Constants.java (working copy)
+@@ -1,32 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha.session;
+-
+-/**
+- * Manifest constants for the <code>org.apache.catalina.ha.session</code>
+- * package.
+- *
+- * @author Peter Rossbach Pero
+- */
+-
+-public class Constants {
+-
+- public static final String Package = "org.apache.catalina.ha.session";
+-
+-}
+Index: java/org/apache/catalina/ha/session/LocalStrings.properties
+===================================================================
+--- java/org/apache/catalina/ha/session/LocalStrings.properties (revision 590752)
++++ java/org/apache/catalina/ha/session/LocalStrings.properties (working copy)
+@@ -1,111 +0,0 @@
+-# Licensed to the Apache Software Foundation (ASF) under one or more
+-# contributor license agreements. See the NOTICE file distributed with
+-# this work for additional information regarding copyright ownership.
+-# The ASF licenses this file to You under the Apache License, Version 2.0
+-# (the "License"); you may not use this file except in compliance with
+-# the License. You may obtain a copy of the License at
+-#
+-#
http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS,
+-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-# See the License for the specific language governing permissions and
+-# limitations under the License.
+-
+-deltaManager.createSession.ise=createSession: Too many active sessions
+-deltaManager.createSession.newSession=Created a DeltaSession with Id [{0}] Total
count={1}
+-deltaManager.createMessage.access=Manager [{0}]: create session message [{1}] access.
+-deltaManager.createMessage.accessChangePrimary=Manager [{0}]: create session message
[{1}] access to change primary.
+-deltaManager.createMessage.allSessionData=Manager [{0}] send all session data.
+-deltaManager.createMessage.allSessionTransfered=Manager [{0}] send all session data
transfered
+-deltaManager.createMessage.delta=Manager [{0}]: create session message [{1}] delta
request.
+-deltaManager.createMessage.expire=Manager [{0}]: create session message [{1}] expire.
+-deltaManager.createMessage.unableCreateDeltaRequest=Unable to serialize delta request
for sessionid [{0}]
+-deltaManager.dropMessage=Manager [{0}]: Drop message {1} inside GET_ALL_SESSIONS sync
phase start date {2} message date {3}
+-deltaManager.foundMasterMember=Found for context [{0}] the replication master member
[{1}]
+-deltaManager.loading.cnfe=ClassNotFoundException while loading persisted sessions: {0}
+-deltaManager.loading.existing.session=overload existing session {0}
+-deltaManager.loading.ioe=IOException while loading persisted sessions: {0}
+-deltaManager.loading.withContextClassLoader=Manager [{0}]: Loading the object data with
a context class loader.
+-deltaManager.loading.withoutClassLoader=Manager [{0}]: Loading the object data without a
context class loader.
+-deltaManager.managerLoad=Exception loading sessions from persistent storage
+-deltaManager.noCluster=Starting... no cluster associated with this context: [{0}]
+-deltaManager.noMasterMember=Starting... with no other member for context [{0}] at domain
[{1}]
+-deltaManager.noMembers=Manager [{0}]: skipping state transfer. No members active in
cluster group.
+-deltaManager.noSessionState=Manager [{0}]: No session state send at {1} received, timing
out after {2} ms.
+-deltaManager.notStarted=Manager has not yet been started
+-deltaManager.sendMessage.newSession=Manager [{0}] send new session ({1})
+-deltaManager.expireSessions=Manager [{0}] expiring sessions upon shutdown
+-deltaManager.receiveMessage.accessed=Manager [{0}]: received session [{1}] accessed.
+-deltaManager.receiveMessage.createNewSession=Manager [{0}]: received session [{1}]
created.
+-deltaManager.receiveMessage.delta=Manager [{0}]: received session [{1}] delta.
+-deltaManager.receiveMessage.error=Manager [{0}]: Unable to receive message through TCP
channel
+-deltaManager.receiveMessage.eventType=Manager [{0}]: Received SessionMessage of
type=({1}) from [{2}]
+-deltaManager.receiveMessage.expired=Manager [{0}]: received session [{1}] expired.
+-deltaManager.receiveMessage.transfercomplete=Manager [{0}] received from node [{1}:{2}]
session state transfered.
+-deltaManager.receiveMessage.unloadingAfter=Manager [{0}]: unloading sessions complete
+-deltaManager.receiveMessage.unloadingBegin=Manager [{0}]: start unloading sessions
+-deltaManager.receiveMessage.allSessionDataAfter=Manager [{0}]: session state
deserialized
+-deltaManager.receiveMessage.allSessionDataBegin=Manager [{0}]: received session state
data
+-deltaManager.receiveMessage.fromWrongDomain=Manager [{0}]: Received wrong SessionMessage
of type=({1}) from [{2}] with domain [{3}] (localdomain [{4}]
+-deltaManager.registerCluster=Register manager {0} to cluster element {1} with name {2}
+-deltaManager.sessionReceived=Manager [{0}]; session state send at {1} received in {2}
ms.
+-deltaManager.sessionTimeout=Invalid session timeout setting {0}
+-deltaManager.startClustering=Starting clustering manager at {0}
+-deltaManager.stopped=Manager [{0}] is stopping
+-deltaManager.unloading.ioe=IOException while saving persisted sessions: {0}
+-deltaManager.waitForSessionState=Manager [{0}], requesting session state from {1}. This
operation will timeout if no session state has been received within 60 seconds.
+-deltaRequest.showPrincipal=Principal [{0}] is set to session {1}
+-deltaRequest.wrongPrincipalClass=DeltaManager only support GenericPrincipal. Your realm
used principal class {0}.
+-deltaSession.notifying=Notifying cluster of expiration primary={0} sessionId [{1}]
+-deltaSession.valueBound.ex=Session bound listener throw an exception
+-deltaSession.valueBinding.ex=Session binding listener throw an exception
+-deltaSession.valueUnbound.ex=Session unbound listener throw an exception
+-deltaSession.readSession=readObject() loading session [{0}]
+-deltaSession.readAttribute=session [{0}] loading attribute '{1}' with value
'{2}'
+-deltaSession.writeSession=writeObject() storing session [{0}]
+-jvmRoute.cannotFindSession=Can't find session [{0}]
+-jvmRoute.changeSession=Changed session from [{0}] to [{1}]
+-jvmRoute.clusterListener.started=Cluster JvmRouteSessionIDBinderListener started
+-jvmRoute.clusterListener.stopped=Cluster JvmRouteSessionIDBinderListener stopped
+-jvmRoute.configure.warn=Please, setup your JvmRouteBinderValve at host valve, not at
context valve!
+-jvmRoute.contextNotFound=Context [{0}] not found at node [{1}]!
+-jvmRoute.failover=Detected a failover with different jvmRoute - orginal route: [{0}] new
one: [{1}] at session id [{2}]
+-jvmRoute.foundManager=Found Cluster DeltaManager {0} at {1}
+-jvmRoute.hostNotFound=No host found [{0}]
+-jvmRoute.listener.started=SessionID Binder Listener started
+-jvmRoute.listener.stopped=SessionID Binder Listener stopped
+-jvmRoute.lostSession=Lost Session [{0}] at path [{1}]
+-jvmRoute.missingJvmRouteAttribute=No engine jvmRoute attribute configured!
+-jvmRoute.newSessionCookie=Setting cookie with session id [{0}] name: [{1}] path: [{2}]
secure: [{3}]
+-jvmRoute.notFoundManager=Not found Cluster DeltaManager {0} at {1}
+-jvmRoute.receiveMessage.sessionIDChanged=Cluster JvmRouteSessionIDBinderListener
received orginal session ID [{0}] set to new id [{1}] for context path [{2}]
+-jvmRoute.run.already=jvmRoute SessionID receiver run already
+-jvmRoute.skipURLSessionIDs=Skip reassign jvm route check, sessionid comes from URL!
+-jvmRoute.turnoverInfo=Turnover Check time {0} msec
+-jvmRoute.valve.alreadyStarted=jvmRoute backup sessionID correction is started
+-jvmRoute.valve.notStarted=jvmRoute backup sessionID correction run already
+-jvmRoute.valve.started=JvmRouteBinderValve started
+-jvmRoute.valve.stopped=JvmRouteBinderValve stopped
+-jvmRoute.set.orignalsessionid=Set Orginal Session id at request attriute {0} value: {1}
+-standardSession.getId.ise=getId: Session already invalidated
+-standardSession.attributeEvent=Session attribute event listener threw exception
+-standardSession.attributeEvent=Session attribute event listener threw exception
+-standardSession.bindingEvent=Session binding event listener threw exception
+-standardSession.invalidate.ise=invalidate: Session already invalidated
+-standardSession.isNew.ise=isNew: Session already invalidated
+-standardSession.getAttribute.ise=getAttribute: Session already invalidated
+-standardSession.getAttributeNames.ise=getAttributeNames: Session already invalidated
+-standardSession.getCreationTime.ise=getCreationTime: Session already invalidated
+-standardSession.getLastAccessedTime.ise=getLastAccessedTime: Session already
invalidated
+-standardSession.getId.ise=getId: Session already invalidated
+-standardSession.getMaxInactiveInterval.ise=getMaxInactiveInterval: Session already
invalidated
+-standardSession.getValueNames.ise=getValueNames: Session already invalidated
+-standardSession.notSerializable=Cannot serialize session attribute {0} for session {1}
+-standardSession.removeAttribute.ise=removeAttribute: Session already invalidated
+-standardSession.sessionEvent=Session event listener threw exception
+-standardSession.setAttribute.iae=setAttribute: Non-serializable attribute
+-standardSession.setAttribute.ise=setAttribute: Session already invalidated
+-standardSession.setAttribute.namenull=setAttribute: name parameter cannot be null
+-standardSession.sessionCreated=Created Session id = {0}
+Index: java/org/apache/catalina/ha/session/ReplicatedSession.java
+===================================================================
+--- java/org/apache/catalina/ha/session/ReplicatedSession.java (revision 590752)
++++ java/org/apache/catalina/ha/session/ReplicatedSession.java (working copy)
+@@ -1,286 +0,0 @@
+-
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.session;
+-
+-/**
+- * 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
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- * 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 InMemoryReplicationManager 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
+- *
+- */
+-import org.apache.catalina.Manager;
+-import java.io.IOException;
+-import java.io.ObjectInputStream;
+-import java.io.ObjectOutputStream;
+-import java.security.Principal;
+-
+-public class ReplicatedSession extends org.apache.catalina.session.StandardSession
+-implements org.apache.catalina.ha.ClusterSession{
+-
+- private transient Manager mManager = null;
+- protected boolean isDirty = false;
+- private transient long lastAccessWasDistributed = System.currentTimeMillis();
+- private boolean isPrimarySession=true;
+-
+-
+- public ReplicatedSession(Manager manager) {
+- super(manager);
+- mManager = manager;
+- }
+-
+-
+- public boolean isDirty()
+- {
+- return isDirty;
+- }
+-
+- public void setIsDirty(boolean dirty)
+- {
+- isDirty = dirty;
+- }
+-
+-
+- public void setLastAccessWasDistributed(long time) {
+- lastAccessWasDistributed = time;
+- }
+-
+- public long getLastAccessWasDistributed() {
+- return lastAccessWasDistributed;
+- }
+-
+-
+- public void removeAttribute(String name) {
+- setIsDirty(true);
+- super.removeAttribute(name);
+- }
+-
+- /**
+- * see parent description,
+- * plus we also notify other nodes in the cluster
+- */
+- public void removeAttribute(String name, boolean notify) {
+- setIsDirty(true);
+- super.removeAttribute(name,notify);
+- }
+-
+-
+- /**
+- * Sets an attribute and notifies the other nodes in the cluster
+- */
+- public void setAttribute(String name, Object value)
+- {
+- if ( value == null ) {
+- removeAttribute(name);
+- return;
+- }
+- if (!(value instanceof java.io.Serializable))
+- throw new java.lang.IllegalArgumentException("Value for attribute
"+name+" is not serializable.");
+- setIsDirty(true);
+- super.setAttribute(name,value);
+- }
+-
+- public void setMaxInactiveInterval(int interval) {
+- setIsDirty(true);
+- super.setMaxInactiveInterval(interval);
+- }
+-
+-
+- /**
+- * Sets the manager for this session
+- * @param mgr - the servers InMemoryReplicationManager
+- */
+- public void setManager(SimpleTcpReplicationManager 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
+- */
+- public void setPrincipal(Principal principal) {
+- super.setPrincipal(principal);
+- setIsDirty(true);
+- }
+-
+- public void expire() {
+- SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();
+- mgr.sessionInvalidated(getIdInternal());
+- setIsDirty(true);
+- super.expire();
+- }
+-
+- public void invalidate() {
+- SimpleTcpReplicationManager mgr =(SimpleTcpReplicationManager)getManager();
+- mgr.sessionInvalidated(getIdInternal());
+- setIsDirty(true);
+- super.invalidate();
+- }
+-
+-
+- /**
+- * Read a serialized version of the contents of this session object from
+- * the specified object input stream, without requiring that the
+- * StandardSession itself have been serialized.
+- *
+- * @param stream The object input stream to read from
+- *
+- * @exception ClassNotFoundException if an unknown class is specified
+- * @exception IOException if an input/output error occurs
+- */
+- public void readObjectData(ObjectInputStream stream)
+- throws ClassNotFoundException, IOException {
+-
+- super.readObjectData(stream);
+-
+- }
+-
+-
+- /**
+- * Write a serialized version of the contents of this session object to
+- * the specified object output stream, without requiring that the
+- * StandardSession itself have been serialized.
+- *
+- * @param stream The object output stream to write to
+- *
+- * @exception IOException if an input/output error occurs
+- */
+- public void writeObjectData(ObjectOutputStream stream)
+- throws IOException {
+-
+- super.writeObjectData(stream);
+-
+- }
+-
+- public void setId(String id, boolean tellNew) {
+-
+- if ((this.id != null) && (manager != null))
+- manager.remove(this);
+-
+- this.id = id;
+-
+- if (manager != null)
+- manager.add(this);
+- if (tellNew) tellNew();
+- }
+-
+-
+-
+-
+-
+-
+-
+-
+- /**
+- * returns true if this session is the primary session, if that is the
+- * case, the manager can expire it upon timeout.
+- */
+- public boolean isPrimarySession() {
+- return isPrimarySession;
+- }
+-
+- /**
+- * Sets whether this is the primary session or not.
+- * @param primarySession Flag value
+- */
+- public void setPrimarySession(boolean primarySession) {
+- this.isPrimarySession=primarySession;
+- }
+-
+-
+-
+-
+- /**
+- * Implements a log method to log through the manager
+- */
+- protected void log(String message) {
+-
+- if ((mManager != null) && (mManager instanceof
SimpleTcpReplicationManager)) {
+- ((SimpleTcpReplicationManager) mManager).log.debug("ReplicatedSession:
" + message);
+- } else {
+- System.out.println("ReplicatedSession: " + message);
+- }
+-
+- }
+-
+- protected void log(String message, Throwable x) {
+-
+- if ((mManager != null) && (mManager instanceof
SimpleTcpReplicationManager)) {
+- ((SimpleTcpReplicationManager) mManager).log.error("ReplicatedSession:
" + message,x);
+- } else {
+- System.out.println("ReplicatedSession: " + message);
+- x.printStackTrace();
+- }
+-
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("ReplicatedSession id=");
+- buf.append(getIdInternal()).append("
ref=").append(super.toString()).append("\n");
+- java.util.Enumeration e = getAttributeNames();
+- while ( e.hasMoreElements() ) {
+- String name = (String)e.nextElement();
+- Object value = getAttribute(name);
+- buf.append("\tname=").append(name).append(";
value=").append(value).append("\n");
+- }
+-
buf.append("\tLastAccess=").append(getLastAccessedTime()).append("\n");
+- return buf.toString();
+- }
+- public int getAccessCount() {
+- return accessCount.get();
+- }
+- public void setAccessCount(int accessCount) {
+- this.accessCount.set(accessCount);
+- }
+- public long getLastAccessedTime() {
+- return lastAccessedTime;
+- }
+- public void setLastAccessedTime(long lastAccessedTime) {
+- this.lastAccessedTime = lastAccessedTime;
+- }
+- public long getThisAccessedTime() {
+- return thisAccessedTime;
+- }
+- public void setThisAccessedTime(long thisAccessedTime) {
+- this.thisAccessedTime = thisAccessedTime;
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/session/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/ha/session/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/ha/session/mbeans-descriptors.xml (working copy)
+@@ -1,583 +0,0 @@
+-<?xml version="1.0" encoding="UTF-8"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE mbeans-descriptors PUBLIC
+-"-//Apache Software Foundation//DTD Model MBeans Configuration File"
+-"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+-<mbeans-descriptors>
+- <mbean
+- name="JvmRouteBinderValve"
+- description="mod_jk jvmRoute jsessionid cookie backup correction"
+- domain="Catalina"
+- group="Valve"
+- type="org.apache.catalina.ha.session.JvmRouteBinderValve">
+- <attribute
+- name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="info"
+- description="describe version"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="enabled"
+- description="enable a jvm Route check"
+- type="boolean"/>
+- <attribute
+- name="numberOfSessions"
+- description="number of jvmRoute session corrections"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="sessionIdAttribute"
+- description="Name of attribute with sessionid value before turnover a
session"
+- type="java.lang.String"/>
+- <operation
+- name="start"
+- description="Stops the Cluster JvmRouteBinderValve"
+- impact="ACTION"
+- returnType="void"/>
+- <operation
+- name="stop"
+- description="Stops the Cluster JvmRouteBinderValve"
+- impact="ACTION"
+- returnType="void"/>
+- </mbean>
+- <mbean
+- name="JvmRouteSessionIDBinderListener"
+- description="Monitors the jvmRoute activity"
+- domain="Catalina"
+- group="Listener"
+- type="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener">
+- <attribute
+- name="info"
+- description="describe version"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="numberOfSessions"
+- description="number of jvmRoute session corrections"
+- type="long"
+- writeable="false"/>
+- </mbean>
+- <mbean
+- name="DeltaManager"
+- description="Cluster Manager implementation of the Manager interface"
+- domain="Catalina"
+- group="Manager"
+- type="org.apache.catalina.ha.session.DeltaManager">
+- <attribute
+- name="info"
+- description="describe version"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="algorithm"
+- description="The message digest algorithm to be used when generating
+-session identifiers"
+- type="java.lang.String"/>
+- <attribute
+- name="randomFile"
+- description="File source of random - /dev/urandom or a pipe"
+- type="java.lang.String"/>
+- <attribute
+- name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="distributable"
+- description="The distributable flag for Sessions created by this
+-Manager"
+- type="boolean"/>
+- <attribute
+- name="entropy"
+- description="A String initialization parameter used to increase the
+-entropy of the initialization of our random number
+-generator"
+- type="java.lang.String"/>
+- <attribute
+- name="maxActiveSessions"
+- description="The maximum number of active Sessions allowed, or -1
+-for no limit"
+- type="int"/>
+- <attribute
+- name="maxInactiveInterval"
+- description="The default maximum inactive interval for Sessions
+-created by this Manager"
+- type="int"/>
+- <attribute
+- name="processExpiresFrequency"
+- description="The frequency of the manager checks (expiration and
passivation)"
+- type="int"/>
+- <attribute
+- name="sessionIdLength"
+- description="The session id length (in bytes) of Sessions
+-created by this Manager"
+- type="int"/>
+- <attribute
+- name="name"
+- description="The descriptive name of this Manager implementation
+-(for logging)"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="activeSessions"
+- description="Number of active sessions at this moment"
+- type="int"
+- writeable="false"/>
+- <attribute
+- name="sessionCounter"
+- description="Total number of sessions created by this manager"
+- type="int"/>
+- <attribute
+- name="sessionReplaceCounter"
+- description="Total number of replaced sessions that load from external
nodes"
+- type="long"/>
+- <attribute
+- name="maxActive"
+- description="Maximum number of active sessions so far"
+- type="int"/>
+- <attribute
+- name="sessionMaxAliveTime"
+- description="Longest time an expired session had been alive"
+- type="int"/>
+- <attribute
+- name="sessionAverageAliveTime"
+- description="Average time an expired session had been alive"
+- type="int"/>
+- <attribute
+- name="sendClusterDomainOnly"
+- is="true"
+- description="The sendClusterDomainOnly flag send sessions only to members as
same cluster domain"
+- type="boolean"/>
+- <attribute
+- name="rejectedSessions"
+- description="Number of sessions we rejected due to maxActive beeing
reached"
+- type="int"/>
+- <attribute
+- name="expiredSessions"
+- description="Number of sessions that expired ( doesn't include explicit
invalidations )"
+- type="int"/>
+- <attribute
+- name="stateTransferTimeout"
+- description="state transfer timeout in sec"
+- type="int"/>
+- <attribute
+- name="processingTime"
+- description="Time spent doing housekeeping and expiration"
+- type="long"/>
+- <attribute
+- name="duplicates"
+- description="Number of duplicated session ids generated"
+- type="int"/>
+- <attribute
+- name="counterReceive_EVT_GET_ALL_SESSIONS"
+- description="Count receive EVT_GET_ALL_SESSIONS messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterReceive_EVT_ALL_SESSION_DATA"
+- description="Count receive EVT_ALL_SESSION_DATA messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterReceive_EVT_SESSION_CREATED"
+- description="Count receive EVT_SESSION_CREATED messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterReceive_EVT_SESSION_DELTA"
+- description="Count receive EVT_SESSION_DELTA messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterReceive_EVT_SESSION_ACCESSED"
+- description="Count receive EVT_SESSION_ACCESSED messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterReceive_EVT_SESSION_EXPIRED"
+- description="Count receive EVT_SESSION_EXPIRED messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE"
+- description="Count receive EVT_ALL_SESSION_TRANSFERCOMPLETE messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_GET_ALL_SESSIONS"
+- description="Count send EVT_GET_ALL_SESSIONS messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_ALL_SESSION_DATA"
+- description="Count send EVT_ALL_SESSION_DATA messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_SESSION_CREATED"
+- description="Count send EVT_SESSION_CREATED messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_SESSION_DELTA"
+- description="Count send EVT_SESSION_DELTA messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_SESSION_ACCESSED"
+- description="Count send EVT_SESSION_ACCESSED messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_SESSION_EXPIRED"
+- description="Count send EVT_SESSION_EXPIRED messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE"
+- description="Count send EVT_ALL_SESSION_TRANSFERCOMPLETE messages"
+- type="long"
+- writeable="false"/>
+- <attribute
+- name="counterNoStateTransfered"
+- description="Count the failed session transfers noStateTransfered"
+- type="int"
+- writeable="false"/>
+- <attribute
+- name="receivedQueueSize"
+- description="length of receive queue size when session received from other
node"
+- type="int"
+- writeable="false"/>
+- <attribute
+- name="expireSessionsOnShutdown"
+- is="true"
+- description="exipre all sessions cluster wide as one node goes down"
+- type="boolean"/>
+- <attribute
+- name="notifyListenersOnReplication"
+- is="true"
+- description="Send session attribute change events on backup nodes"
+- type="boolean"/>
+- <attribute
+- name="notifySessionListenersOnReplication"
+- is="true"
+- description="Send session start/stop events on backup nodes"
+- type="boolean"/>
+- <attribute
+- name="sendAllSessions"
+- is="true"
+- description="Send all sessions at one big block"
+- type="boolean"/>
+- <attribute
+- name="sendAllSessionsSize"
+- description="session block size when sendAllSessions=false
(default=1000)"
+- type="int"/>
+- <attribute
+- name="sendAllSessionsWaitTime"
+- description="wait time between send session block (default 2 sec)"
+- type="int"/>
+- <operation
+- name="listSessionIds"
+- description="Return the list of active session ids"
+- impact="ACTION"
+- returnType="java.lang.String"> </operation>
+- <operation
+- name="getSessionAttribute"
+- description="Return a session attribute"
+- impact="ACTION"
+- returnType="java.lang.String">
+- <parameter
+- name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- <parameter
+- name="key"
+- description="key of the attribute"
+- type="java.lang.String"/>
+- </operation>
+- <operation
+- name="expireSession"
+- description="Expire a session"
+- impact="ACTION"
+- returnType="void">
+- <parameter
+- name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- </operation>
+- <operation
+- name="getLastAccessedTime"
+- description="Get the last access time"
+- impact="ACTION"
+- returnType="java.lang.String">
+- <parameter
+- name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- </operation>
+- <operation name="getCreationTime"
+- description="Get the creation time"
+- impact="ACTION"
+- returnType="java.lang.String">
+- <parameter name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- </operation>
+- <operation
+- name="expireAllLocalSessions"
+- description="Exipre all active local sessions and replicate the invalid
sessions"
+- impact="ACTION"
+- returnType="void"> </operation>
+- <operation
+- name="processExpires"
+- description="force process to expire sessions"
+- impact="ACTION"
+- returnType="void"> </operation>
+- <operation
+- name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void"> </operation>
+- <operation
+- name="getAllClusterSessions"
+- description="send to oldest cluster member that this node need all cluster
sessions (resync member)"
+- impact="ACTION"
+- returnType="void"> </operation>
+- </mbean>
+- <mbean
+- name="SimpleTcpReplicationManager"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Clustered implementation of the Manager interface"
+- domain="Catalina"
+- group="Manager"
+- type="org.apache.catalina.ha.tcp.SimpleTcpReplicationManager">
+- <attribute
+- name="algorithm"
+- description="The message digest algorithm to be used when generating session
identifiers"
+- type="java.lang.String"/>
+- <attribute
+- name="checkInterval"
+- description="The interval (in seconds) between checks for expired
sessions"
+- type="int"/>
+- <attribute
+- name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="distributable"
+- description="The distributable flag for Sessions created by this
Manager"
+- type="boolean"/>
+- <attribute
+- name="entropy"
+- description="A String initialization parameter used to increase the entropy
of the initialization of our random number generator"
+- type="java.lang.String"/>
+- <attribute
+- name="managedResource"
+- description="The managed resource this MBean is associated with"
+- type="java.lang.Object"/>
+- <attribute
+- name="maxActiveSessions"
+- description="The maximum number of active Sessions allowed, or -1 for no
limit"
+- type="int"/>
+- <attribute
+- name="maxInactiveInterval"
+- description="The default maximum inactive interval for Sessions created by
this Manager"
+- type="int"/>
+- <attribute
+- name="name"
+- description="The descriptive name of this Manager implementation (for
logging)"
+- type="java.lang.String"
+- writeable="false"/>
+- </mbean>
+- <mbean
+- name="BackupManager"
+- description="Cluster Manager implementation of the Manager interface"
+- domain="Catalina"
+- group="Manager"
+- type="org.apache.catalina.ha.session.BackupManager">
+- <attribute
+- name="algorithm"
+- description="The message digest algorithm to be used when generating
+-session identifiers"
+- type="java.lang.String"/>
+- <attribute
+- name="randomFile"
+- description="File source of random - /dev/urandom or a pipe"
+- type="java.lang.String"/>
+- <attribute
+- name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="distributable"
+- description="The distributable flag for Sessions created by this
+-Manager"
+- type="boolean"/>
+- <attribute
+- name="entropy"
+- description="A String initialization parameter used to increase the
+-entropy of the initialization of our random number
+-generator"
+- type="java.lang.String"/>
+- <attribute
+- name="maxActiveSessions"
+- description="The maximum number of active Sessions allowed, or -1
+-for no limit"
+- type="int"/>
+- <attribute
+- name="maxInactiveInterval"
+- description="The default maximum inactive interval for Sessions
+-created by this Manager"
+- type="int"/>
+- <attribute
+- name="processExpiresFrequency"
+- description="The frequency of the manager checks (expiration and
passivation)"
+- type="int"/>
+- <attribute
+- name="sessionIdLength"
+- description="The session id length (in bytes) of Sessions
+-created by this Manager"
+- type="int"/>
+- <attribute
+- name="name"
+- description="The descriptive name of this Manager implementation
+-(for logging)"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute
+- name="pathname"
+- description="Path name of the disk file in which active sessions"
+- type="java.lang.String"/>
+- <attribute
+- name="activeSessions"
+- description="Number of active primary sessions at this moment"
+- type="int"
+- writeable="false"/>
+- <attribute
+- name="activeSessionsFull"
+- description="Number of active sessions at this moment"
+- type="int"
+- writeable="false"/>
+- <attribute
+- name="sessionCounter"
+- description="Total number of sessions created by this manager"
+- type="int"/>
+- <attribute
+- name="maxActive"
+- description="Maximum number of active sessions so far"
+- type="int"/>
+- <attribute
+- name="sessionMaxAliveTime"
+- description="Longest time an expired session had been alive"
+- type="int"/>
+- <attribute
+- name="sessionAverageAliveTime"
+- description="Average time an expired session had been alive"
+- type="int"/>
+- <attribute
+- name="rejectedSessions"
+- description="Number of sessions we rejected due to maxActive beeing
reached"
+- type="int"/>
+- <attribute
+- name="expiredSessions"
+- description="Number of sessions that expired ( doesn't include explicit
invalidations )"
+- type="int"/>
+- <attribute
+- name="processingTime"
+- description="Time spent doing housekeeping and expiration"
+- type="long"/>
+- <attribute
+- name="duplicates"
+- description="Number of duplicated session ids generated"
+- type="int"/>
+- <attribute
+- name="expireSessionsOnShutdown"
+- is="true"
+- description="exipre all sessions cluster wide as one node goes down"
+- type="boolean"/>
+- <attribute
+- name="notifyListenersOnReplication"
+- is="true"
+- description="Send session attribute change events on backup nodes"
+- type="boolean"/>
+- <attribute
+- name="mapSendOptions"
+- description="mapSendOptions"
+- type="int"
+- writeable="false"/>
+- <operation
+- name="listSessionIds"
+- description="Return the list of active primary session ids"
+- impact="ACTION"
+- returnType="java.lang.String"> </operation>
+- <operation
+- name="listSessionIdsFull"
+- description="Return the list of active session ids"
+- impact="ACTION"
+- returnType="java.lang.String"> </operation>
+- <operation
+- name="getSessionAttribute"
+- description="Return a session attribute"
+- impact="ACTION"
+- returnType="java.lang.String">
+- <parameter
+- name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- <parameter
+- name="key"
+- description="key of the attribute"
+- type="java.lang.String"/>
+- </operation>
+- <operation
+- name="expireSession"
+- description="Expire a session"
+- impact="ACTION"
+- returnType="void">
+- <parameter
+- name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- </operation>
+- <operation
+- name="getLastAccessedTime"
+- description="Get the last access time"
+- impact="ACTION"
+- returnType="java.lang.String">
+- <parameter
+- name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- </operation>
+- <operation name="getCreationTime"
+- description="Get the creation time"
+- impact="ACTION"
+- returnType="java.lang.String">
+- <parameter name="sessionId"
+- description="Id of the session"
+- type="java.lang.String"/>
+- </operation>
+-
+- </mbean>
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/ha/session/DeltaManager.java
+===================================================================
+--- java/org/apache/catalina/ha/session/DeltaManager.java (revision 590752)
++++ java/org/apache/catalina/ha/session/DeltaManager.java (working copy)
+@@ -1,1522 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.session;
+-
+-import java.beans.PropertyChangeEvent;
+-import java.io.BufferedOutputStream;
+-import java.io.ByteArrayOutputStream;
+-import java.io.IOException;
+-import java.io.ObjectInputStream;
+-import java.io.ObjectOutputStream;
+-import java.util.ArrayList;
+-import java.util.Date;
+-import java.util.Iterator;
+-
+-import org.apache.catalina.Cluster;
+-import org.apache.catalina.Container;
+-import org.apache.catalina.Context;
+-import org.apache.catalina.Engine;
+-import org.apache.catalina.Host;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.LifecycleListener;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.Valve;
+-import org.apache.catalina.core.StandardContext;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.tcp.ReplicationValve;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.io.ReplicationStream;
+-import org.apache.catalina.util.LifecycleSupport;
+-import org.apache.catalina.util.StringManager;
+-import org.apache.catalina.ha.ClusterManager;
+-
+-/**
+- * The DeltaManager manages replicated sessions by only replicating the deltas
+- * in data. For applications written to handle this, the DeltaManager is the
+- * optimal way of replicating data.
+- *
+- * This code is almost identical to StandardManager with a difference in how it
+- * persists sessions and some modifications to it.
+- *
+- * <b>IMPLEMENTATION NOTE </b>: Correct behavior of session storing and
+- * reloading depends upon external calls to the <code>start()</code> and
+- * <code>stop()</code> methods of this class at the correct times.
+- *
+- * @author Filip Hanik
+- * @author Craig R. McClanahan
+- * @author Jean-Francois Arcand
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-
+-public class DeltaManager extends ClusterManagerBase{
+-
+- // ---------------------------------------------------- Security Classes
+- public static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(DeltaManager.class);
+-
+- /**
+- * The string manager for this package.
+- */
+- protected static StringManager sm = StringManager.getManager(Constants.Package);
+-
+- // ----------------------------------------------------- Instance Variables
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "DeltaManager/2.1";
+-
+- /**
+- * Has this component been started yet?
+- */
+- private boolean started = false;
+-
+- /**
+- * The descriptive name of this Manager implementation (for logging).
+- */
+- protected static String managerName = "DeltaManager";
+- protected String name = null;
+- protected boolean defaultMode = false;
+- private CatalinaCluster cluster = null;
+-
+- /**
+- * cached replication valve cluster container!
+- */
+- private ReplicationValve replicationValve = null ;
+-
+- /**
+- * The lifecycle event support for this component.
+- */
+- protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+-
+- /**
+- * The maximum number of active Sessions allowed, or -1 for no limit.
+- */
+- private int maxActiveSessions = -1;
+- private boolean expireSessionsOnShutdown = false;
+- private boolean notifyListenersOnReplication = true;
+- private boolean notifySessionListenersOnReplication = true;
+- private boolean stateTransfered = false ;
+- private int stateTransferTimeout = 60;
+- private boolean sendAllSessions = true;
+- private boolean sendClusterDomainOnly = true ;
+- private int sendAllSessionsSize = 1000 ;
+-
+- /**
+- * wait time between send session block (default 2 sec)
+- */
+- private int sendAllSessionsWaitTime = 2 * 1000 ;
+- private ArrayList receivedMessageQueue = new ArrayList() ;
+- private boolean receiverQueue = false ;
+- private boolean stateTimestampDrop = true ;
+- private long stateTransferCreateSendTime;
+-
+- // ------------------------------------------------------------------ stats
attributes
+-
+- int rejectedSessions = 0;
+- private long sessionReplaceCounter = 0 ;
+- long processingTime = 0;
+- private long counterReceive_EVT_GET_ALL_SESSIONS = 0 ;
+- private long counterSend_EVT_ALL_SESSION_DATA = 0 ;
+- private long counterReceive_EVT_ALL_SESSION_DATA = 0 ;
+- private long counterReceive_EVT_SESSION_CREATED = 0 ;
+- private long counterReceive_EVT_SESSION_EXPIRED = 0;
+- private long counterReceive_EVT_SESSION_ACCESSED = 0 ;
+- private long counterReceive_EVT_SESSION_DELTA = 0;
+- private long counterSend_EVT_GET_ALL_SESSIONS = 0 ;
+- private long counterSend_EVT_SESSION_CREATED = 0;
+- private long counterSend_EVT_SESSION_DELTA = 0 ;
+- private long counterSend_EVT_SESSION_ACCESSED = 0;
+- private long counterSend_EVT_SESSION_EXPIRED = 0;
+- private int counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;
+- private int counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0 ;
+- private int counterNoStateTransfered = 0 ;
+-
+-
+- // ------------------------------------------------------------- Constructor
+- public DeltaManager() {
+- super();
+- }
+-
+- // ------------------------------------------------------------- Properties
+-
+- /**
+- * Return descriptive information about this Manager implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+- return info;
+- }
+-
+- public void setName(String name) {
+- this.name = name;
+- }
+-
+- /**
+- * Return the descriptive short name of this Manager implementation.
+- */
+- public String getName() {
+- return name;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_GET_ALL_SESSIONS.
+- */
+- public long getCounterSend_EVT_GET_ALL_SESSIONS() {
+- return counterSend_EVT_GET_ALL_SESSIONS;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_SESSION_ACCESSED.
+- */
+- public long getCounterSend_EVT_SESSION_ACCESSED() {
+- return counterSend_EVT_SESSION_ACCESSED;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_SESSION_CREATED.
+- */
+- public long getCounterSend_EVT_SESSION_CREATED() {
+- return counterSend_EVT_SESSION_CREATED;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_SESSION_DELTA.
+- */
+- public long getCounterSend_EVT_SESSION_DELTA() {
+- return counterSend_EVT_SESSION_DELTA;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_SESSION_EXPIRED.
+- */
+- public long getCounterSend_EVT_SESSION_EXPIRED() {
+- return counterSend_EVT_SESSION_EXPIRED;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_ALL_SESSION_DATA.
+- */
+- public long getCounterSend_EVT_ALL_SESSION_DATA() {
+- return counterSend_EVT_ALL_SESSION_DATA;
+- }
+-
+- /**
+- * @return Returns the counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.
+- */
+- public int getCounterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE() {
+- return counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE;
+- }
+-
+- /**
+- * @return Returns the counterReceive_EVT_ALL_SESSION_DATA.
+- */
+- public long getCounterReceive_EVT_ALL_SESSION_DATA() {
+- return counterReceive_EVT_ALL_SESSION_DATA;
+- }
+-
+- /**
+- * @return Returns the counterReceive_EVT_GET_ALL_SESSIONS.
+- */
+- public long getCounterReceive_EVT_GET_ALL_SESSIONS() {
+- return counterReceive_EVT_GET_ALL_SESSIONS;
+- }
+-
+- /**
+- * @return Returns the counterReceive_EVT_SESSION_ACCESSED.
+- */
+- public long getCounterReceive_EVT_SESSION_ACCESSED() {
+- return counterReceive_EVT_SESSION_ACCESSED;
+- }
+-
+- /**
+- * @return Returns the counterReceive_EVT_SESSION_CREATED.
+- */
+- public long getCounterReceive_EVT_SESSION_CREATED() {
+- return counterReceive_EVT_SESSION_CREATED;
+- }
+-
+- /**
+- * @return Returns the counterReceive_EVT_SESSION_DELTA.
+- */
+- public long getCounterReceive_EVT_SESSION_DELTA() {
+- return counterReceive_EVT_SESSION_DELTA;
+- }
+-
+- /**
+- * @return Returns the counterReceive_EVT_SESSION_EXPIRED.
+- */
+- public long getCounterReceive_EVT_SESSION_EXPIRED() {
+- return counterReceive_EVT_SESSION_EXPIRED;
+- }
+-
+-
+- /**
+- * @return Returns the counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.
+- */
+- public int getCounterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE() {
+- return counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE;
+- }
+-
+- /**
+- * @return Returns the processingTime.
+- */
+- public long getProcessingTime() {
+- return processingTime;
+- }
+-
+- /**
+- * @return Returns the sessionReplaceCounter.
+- */
+- public long getSessionReplaceCounter() {
+- return sessionReplaceCounter;
+- }
+-
+- /**
+- * Number of session creations that failed due to maxActiveSessions
+- *
+- * @return The count
+- */
+- public int getRejectedSessions() {
+- return rejectedSessions;
+- }
+-
+- public void setRejectedSessions(int rejectedSessions) {
+- this.rejectedSessions = rejectedSessions;
+- }
+-
+- /**
+- * @return Returns the counterNoStateTransfered.
+- */
+- public int getCounterNoStateTransfered() {
+- return counterNoStateTransfered;
+- }
+-
+- public int getReceivedQueueSize() {
+- return receivedMessageQueue.size() ;
+- }
+-
+- /**
+- * @return Returns the stateTransferTimeout.
+- */
+- public int getStateTransferTimeout() {
+- return stateTransferTimeout;
+- }
+- /**
+- * @param timeoutAllSession The timeout
+- */
+- public void setStateTransferTimeout(int timeoutAllSession) {
+- this.stateTransferTimeout = timeoutAllSession;
+- }
+-
+- /**
+- * is session state transfered complete?
+- *
+- */
+- public boolean getStateTransfered() {
+- return stateTransfered;
+- }
+-
+- /**
+- * set that state ist complete transfered
+- * @param stateTransfered
+- */
+- public void setStateTransfered(boolean stateTransfered) {
+- this.stateTransfered = stateTransfered;
+- }
+-
+- /**
+- * @return Returns the sendAllSessionsWaitTime in msec
+- */
+- public int getSendAllSessionsWaitTime() {
+- return sendAllSessionsWaitTime;
+- }
+-
+- /**
+- * @param sendAllSessionsWaitTime The sendAllSessionsWaitTime to set at msec.
+- */
+- public void setSendAllSessionsWaitTime(int sendAllSessionsWaitTime) {
+- this.sendAllSessionsWaitTime = sendAllSessionsWaitTime;
+- }
+-
+- /**
+- * @return Returns the sendClusterDomainOnly.
+- */
+- public boolean doDomainReplication() {
+- return sendClusterDomainOnly;
+- }
+-
+- /**
+- * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+- */
+- public void setDomainReplication(boolean sendClusterDomainOnly) {
+- this.sendClusterDomainOnly = sendClusterDomainOnly;
+- }
+-
+- /**
+- * @return Returns the stateTimestampDrop.
+- */
+- public boolean isStateTimestampDrop() {
+- return stateTimestampDrop;
+- }
+-
+- /**
+- * @param isTimestampDrop The new flag value
+- */
+- public void setStateTimestampDrop(boolean isTimestampDrop) {
+- this.stateTimestampDrop = isTimestampDrop;
+- }
+-
+- /**
+- * Return the maximum number of active Sessions allowed, or -1 for no limit.
+- */
+- public int getMaxActiveSessions() {
+- return (this.maxActiveSessions);
+- }
+-
+- /**
+- * Set the maximum number of actives Sessions allowed, or -1 for no limit.
+- *
+- * @param max
+- * The new maximum number of sessions
+- */
+- public void setMaxActiveSessions(int max) {
+- int oldMaxActiveSessions = this.maxActiveSessions;
+- this.maxActiveSessions = max;
+- support.firePropertyChange("maxActiveSessions", new
Integer(oldMaxActiveSessions), new Integer(this.maxActiveSessions));
+- }
+-
+- /**
+- *
+- * @return Returns the sendAllSessions.
+- */
+- public boolean isSendAllSessions() {
+- return sendAllSessions;
+- }
+-
+- /**
+- * @param sendAllSessions The sendAllSessions to set.
+- */
+- public void setSendAllSessions(boolean sendAllSessions) {
+- this.sendAllSessions = sendAllSessions;
+- }
+-
+- /**
+- * @return Returns the sendAllSessionsSize.
+- */
+- public int getSendAllSessionsSize() {
+- return sendAllSessionsSize;
+- }
+-
+- /**
+- * @param sendAllSessionsSize The sendAllSessionsSize to set.
+- */
+- public void setSendAllSessionsSize(int sendAllSessionsSize) {
+- this.sendAllSessionsSize = sendAllSessionsSize;
+- }
+-
+- /**
+- * @return Returns the notifySessionListenersOnReplication.
+- */
+- public boolean isNotifySessionListenersOnReplication() {
+- return notifySessionListenersOnReplication;
+- }
+-
+- /**
+- * @param notifyListenersCreateSessionOnReplication The
notifySessionListenersOnReplication to set.
+- */
+- public void setNotifySessionListenersOnReplication(boolean
notifyListenersCreateSessionOnReplication) {
+- this.notifySessionListenersOnReplication =
notifyListenersCreateSessionOnReplication;
+- }
+-
+-
+- public boolean isExpireSessionsOnShutdown() {
+- return expireSessionsOnShutdown;
+- }
+-
+- public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown) {
+- this.expireSessionsOnShutdown = expireSessionsOnShutdown;
+- }
+-
+- public boolean isNotifyListenersOnReplication() {
+- return notifyListenersOnReplication;
+- }
+-
+- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+- this.notifyListenersOnReplication = notifyListenersOnReplication;
+- }
+-
+-
+- /**
+- * @return Returns the defaultMode.
+- */
+- public boolean isDefaultMode() {
+- return defaultMode;
+- }
+- /**
+- * @param defaultMode The defaultMode to set.
+- */
+- public void setDefaultMode(boolean defaultMode) {
+- this.defaultMode = defaultMode;
+- }
+-
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- public void setCluster(CatalinaCluster cluster) {
+- this.cluster = cluster;
+- }
+-
+- /**
+- * Set the Container with which this Manager has been associated. If it is a
+- * Context (the usual case), listen for changes to the session timeout
+- * property.
+- *
+- * @param container
+- * The associated Container
+- */
+- public void setContainer(Container container) {
+- // De-register from the old Container (if any)
+- if ((this.container != null) && (this.container instanceof Context))
+- ((Context) this.container).removePropertyChangeListener(this);
+-
+- // Default processing provided by our superclass
+- super.setContainer(container);
+-
+- // Register with the new Container (if any)
+- if ((this.container != null) && (this.container instanceof Context)) {
+- setMaxInactiveInterval(((Context) this.container).getSessionTimeout() *
60);
+- ((Context) this.container).addPropertyChangeListener(this);
+- }
+-
+- }
+-
+- // --------------------------------------------------------- Public Methods
+-
+- /**
+- * 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
+- *
+- * 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(String sessionId) {
+- return createSession(sessionId, true);
+- }
+-
+- /**
+- * create new session with check maxActiveSessions and send session creation
+- * to other cluster nodes.
+- *
+- * @param distribute
+- * @return The session
+- */
+- public Session createSession(String sessionId, boolean distribute) {
+- if ((maxActiveSessions >= 0) && (sessions.size() >=
maxActiveSessions)) {
+- rejectedSessions++;
+- throw new
IllegalStateException(sm.getString("deltaManager.createSession.ise"));
+- }
+- DeltaSession session = (DeltaSession) super.createSession(sessionId) ;
+- if (distribute) {
+- sendCreateSession(session.getId(), session);
+- }
+- if (log.isDebugEnabled())
+-
log.debug(sm.getString("deltaManager.createSession.newSession",session.getId(),
new Integer(sessions.size())));
+- return (session);
+-
+- }
+-
+- /**
+- * Send create session evt to all backup node
+- * @param sessionId
+- * @param session
+- */
+- protected void sendCreateSession(String sessionId, DeltaSession session) {
+- if(cluster.getMembers().length > 0 ) {
+- SessionMessage msg =
+- new SessionMessageImpl(getName(),
+- SessionMessage.EVT_SESSION_CREATED,
+- null,
+- sessionId,
+- sessionId + "-" +
System.currentTimeMillis());
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.sendMessage.newSession",name, sessionId));
+- msg.setTimestamp(session.getCreationTime());
+- counterSend_EVT_SESSION_CREATED++;
+- send(msg);
+- }
+- }
+-
+- /**
+- * Send messages to other backup member (domain or all)
+- * @param msg Session message
+- */
+- protected void send(SessionMessage msg) {
+- if(cluster != null) {
+- if(doDomainReplication())
+- cluster.sendClusterDomain(msg);
+- else
+- cluster.send(msg);
+- }
+- }
+-
+- /**
+- * Create DeltaSession
+- * @see org.apache.catalina.Manager#createEmptySession()
+- */
+- public Session createEmptySession() {
+- return getNewDeltaSession() ;
+- }
+-
+- /**
+- * Get new session class to be used in the doLoad() method.
+- */
+- protected DeltaSession getNewDeltaSession() {
+- return new DeltaSession(this);
+- }
+-
+- /**
+- * Load Deltarequest from external node
+- * Load the Class at container classloader
+- * @see DeltaRequest#readExternal(java.io.ObjectInput)
+- * @param session
+- * @param data message data
+- * @return The request
+- * @throws ClassNotFoundException
+- * @throws IOException
+- */
+- protected DeltaRequest deserializeDeltaRequest(DeltaSession session, byte[] data)
throws ClassNotFoundException, IOException {
+- ReplicationStream ois = getReplicationStream(data);
+- session.getDeltaRequest().readExternal(ois);
+- ois.close();
+- return session.getDeltaRequest();
+- }
+-
+- /**
+- * serialize DeltaRequest
+- * @see DeltaRequest#writeExternal(java.io.ObjectOutput)
+- *
+- * @param deltaRequest
+- * @return serialized delta request
+- * @throws IOException
+- */
+- protected byte[] serializeDeltaRequest(DeltaRequest deltaRequest) throws IOException
{
+- return deltaRequest.serialize();
+- }
+-
+- /**
+- * Load sessions from other cluster node.
+- * FIXME replace currently sessions with same id without notifcation.
+- * FIXME SSO handling is not really correct with the session replacement!
+- * @exception ClassNotFoundException
+- * if a serialized class cannot be found during the reload
+- * @exception IOException
+- * if an input/output error occurs
+- */
+- protected void deserializeSessions(byte[] data) throws
ClassNotFoundException,IOException {
+-
+- // Initialize our internal data structures
+- //sessions.clear(); //should not do this
+- // Open an input stream to the specified pathname, if any
+- ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
+- ObjectInputStream ois = null;
+- // Load the previously unloaded active sessions
+- try {
+- ois = getReplicationStream(data);
+- Integer count = (Integer) ois.readObject();
+- int n = count.intValue();
+- for (int i = 0; i < n; i++) {
+- DeltaSession session = (DeltaSession) createEmptySession();
+- session.readObjectData(ois);
+- session.setManager(this);
+- session.setValid(true);
+- session.setPrimarySession(false);
+- //in case the nodes in the cluster are out of
+- //time synch, this will make sure that we have the
+- //correct timestamp, isValid returns true, cause
+- // accessCount=1
+- session.access();
+- //make sure that the session gets ready to expire if
+- // needed
+- session.setAccessCount(0);
+- session.resetDeltaRequest();
+- // FIXME How inform other session id cache like SingleSignOn
+- // increment sessionCounter to correct stats report
+- if (findSession(session.getIdInternal()) == null ) {
+- sessionCounter++;
+- } else {
+- sessionReplaceCounter++;
+- // FIXME better is to grap this sessions again !
+- if (log.isWarnEnabled())
log.warn(sm.getString("deltaManager.loading.existing.session",session.getIdInternal()));
+- }
+- add(session);
+- }
+- } catch (ClassNotFoundException e) {
+- log.error(sm.getString("deltaManager.loading.cnfe", e), e);
+- throw e;
+- } catch (IOException e) {
+- log.error(sm.getString("deltaManager.loading.ioe", e), e);
+- throw e;
+- } finally {
+- // Close the input stream
+- try {
+- if (ois != null) ois.close();
+- } catch (IOException f) {
+- // ignored
+- }
+- ois = null;
+- if (originalLoader != null)
Thread.currentThread().setContextClassLoader(originalLoader);
+- }
+-
+- }
+-
+-
+-
+- /**
+- * Save any currently active sessions in the appropriate persistence
+- * mechanism, if any. If persistence is not supported, this method returns
+- * without doing anything.
+- *
+- * @exception IOException
+- * if an input/output error occurs
+- */
+- protected byte[] serializeSessions(Session[] currentSessions) throws IOException {
+-
+- // Open an output stream to the specified pathname, if any
+- ByteArrayOutputStream fos = null;
+- ObjectOutputStream oos = null;
+-
+- try {
+- fos = new ByteArrayOutputStream();
+- oos = new ObjectOutputStream(new BufferedOutputStream(fos));
+- oos.writeObject(new Integer(currentSessions.length));
+- for(int i=0 ; i < currentSessions.length;i++) {
+- ((DeltaSession)currentSessions[i]).writeObjectData(oos);
+- }
+- // Flush and close the output stream
+- oos.flush();
+- } catch (IOException e) {
+- log.error(sm.getString("deltaManager.unloading.ioe", e), e);
+- throw e;
+- } finally {
+- if (oos != null) {
+- try {
+- oos.close();
+- } catch (IOException f) {
+- ;
+- }
+- oos = null;
+- }
+- }
+- // send object data as byte[]
+- return fos.toByteArray();
+- }
+-
+- // ------------------------------------------------------ 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.
+- *
+- * @exception LifecycleException
+- * if this component detects a fatal error that prevents this
+- * component from being used
+- */
+- public void start() throws LifecycleException {
+- if (!initialized) init();
+-
+- // Validate and update our current component state
+- if (started) {
+- return;
+- }
+- started = true;
+- lifecycle.fireLifecycleEvent(START_EVENT, null);
+-
+- // Force initialization of the random number generator
+- generateSessionId();
+-
+- // Load unloaded sessions, if any
+- try {
+- //the channel is already running
+- Cluster cluster = getCluster() ;
+- // stop remove cluster binding
+- //wow, how many nested levels of if statements can we have ;)
+- if(cluster == null) {
+- Container context = getContainer() ;
+- if(context != null && context instanceof Context) {
+- Container host = context.getParent() ;
+- if(host != null && host instanceof Host) {
+- cluster = host.getCluster();
+- if(cluster != null && cluster instanceof
CatalinaCluster) {
+- setCluster((CatalinaCluster) cluster) ;
+- } else {
+- Container engine = host.getParent() ;
+- if(engine != null && engine instanceof Engine) {
+- cluster = engine.getCluster();
+- if(cluster != null && cluster instanceof
CatalinaCluster) {
+- setCluster((CatalinaCluster) cluster) ;
+- }
+- } else {
+- cluster = null ;
+- }
+- }
+- }
+- }
+- }
+- if (cluster == null) {
+- log.error(sm.getString("deltaManager.noCluster", getName()));
+- return;
+- } else {
+- if (log.isInfoEnabled()) {
+- String type = "unknown" ;
+- if( cluster.getContainer() instanceof Host){
+- type = "Host" ;
+- } else if( cluster.getContainer() instanceof Engine){
+- type = "Engine" ;
+- }
+- log.info(sm.getString("deltaManager.registerCluster",
getName(), type, cluster.getClusterName()));
+- }
+- }
+- if (log.isInfoEnabled())
log.info(sm.getString("deltaManager.startClustering", getName()));
+- //to survice context reloads, as only a stop/start is called, not
+- // createManager
+- cluster.registerManager(this);
+-
+- getAllClusterSessions();
+-
+- } catch (Throwable t) {
+- log.error(sm.getString("deltaManager.managerLoad"), t);
+- }
+- }
+-
+- /**
+- * get from first session master the backup from all clustered sessions
+- * @see #findSessionMasterMember()
+- */
+- public synchronized void getAllClusterSessions() {
+- if (cluster != null && cluster.getMembers().length > 0) {
+- long beforeSendTime = System.currentTimeMillis();
+- Member mbr = findSessionMasterMember();
+- if(mbr == null) { // No domain member found
+- return;
+- }
+- SessionMessage msg = new
SessionMessageImpl(this.getName(),SessionMessage.EVT_GET_ALL_SESSIONS, null,
"GET-ALL","GET-ALL-" + getName());
+- // set reference time
+- stateTransferCreateSendTime = beforeSendTime ;
+- // request session state
+- counterSend_EVT_GET_ALL_SESSIONS++;
+- stateTransfered = false ;
+- // FIXME This send call block the deploy thread, when sender waitForAck is
enabled
+- try {
+- synchronized(receivedMessageQueue) {
+- receiverQueue = true ;
+- }
+- cluster.send(msg, mbr);
+- if (log.isWarnEnabled())
log.warn(sm.getString("deltaManager.waitForSessionState",getName(), mbr));
+- // FIXME At sender ack mode this method check only the state transfer
and resend is a problem!
+- waitForSendAllSessions(beforeSendTime);
+- } finally {
+- synchronized(receivedMessageQueue) {
+- for (Iterator iter = receivedMessageQueue.iterator();
iter.hasNext();) {
+- SessionMessage smsg = (SessionMessage) iter.next();
+- if (!stateTimestampDrop) {
+- messageReceived(smsg, smsg.getAddress() != null ? (Member)
smsg.getAddress() : null);
+- } else {
+- if (smsg.getEventType() !=
SessionMessage.EVT_GET_ALL_SESSIONS && smsg.getTimestamp() >=
stateTransferCreateSendTime) {
+- // FIXME handle EVT_GET_ALL_SESSIONS later
+- messageReceived(smsg,smsg.getAddress() != null ?
(Member) smsg.getAddress() : null);
+- } else {
+- if (log.isWarnEnabled()) {
+-
log.warn(sm.getString("deltaManager.dropMessage",getName(),
smsg.getEventTypeString(),new Date(stateTransferCreateSendTime), new
Date(smsg.getTimestamp())));
+- }
+- }
+- }
+- }
+- receivedMessageQueue.clear();
+- receiverQueue = false ;
+- }
+- }
+- } else {
+- if (log.isInfoEnabled())
log.info(sm.getString("deltaManager.noMembers", getName()));
+- }
+- }
+-
+- /**
+- * Register cross context session at replication valve thread local
+- * @param session cross context session
+- */
+- protected void registerSessionAtReplicationValve(DeltaSession session) {
+- if(replicationValve == null) {
+- if(container instanceof StandardContext &&
((StandardContext)container).getCrossContext()) {
+- Cluster cluster = getCluster() ;
+- if(cluster != null && cluster instanceof CatalinaCluster) {
+- Valve[] valves = ((CatalinaCluster)cluster).getValves();
+- if(valves != null && valves.length > 0) {
+- for(int i=0; replicationValve == null && i <
valves.length ; i++ ){
+- if(valves[i] instanceof ReplicationValve) replicationValve =
(ReplicationValve)valves[i] ;
+- }//for
+-
+- if(replicationValve == null && log.isDebugEnabled()) {
+- log.debug("no ReplicationValve found for CrossContext
Support");
+- }//endif
+- }//end if
+- }//endif
+- }//end if
+- }//end if
+- if(replicationValve != null) {
+- replicationValve.registerReplicationSession(session);
+- }
+- }
+-
+- /**
+- * Find the master of the session state
+- * @return master member of sessions
+- */
+- protected Member findSessionMasterMember() {
+- Member mbr = null;
+- Member mbrs[] = cluster.getMembers();
+- if(mbrs.length != 0 ) mbr = mbrs[0];
+- if(mbr == null && log.isWarnEnabled())
log.warn(sm.getString("deltaManager.noMasterMember",getName(), ""));
+- if(mbr != null && log.isDebugEnabled())
log.warn(sm.getString("deltaManager.foundMasterMember",getName(), mbr));
+- return mbr;
+- }
+-
+- /**
+- * Wait that cluster session state is transfer or timeout after 60 Sec
+- * With stateTransferTimeout == -1 wait that backup is transfered (forever mode)
+- */
+- protected void waitForSendAllSessions(long beforeSendTime) {
+- long reqStart = System.currentTimeMillis();
+- long reqNow = reqStart ;
+- boolean isTimeout = false;
+- if(getStateTransferTimeout() > 0) {
+- // wait that state is transfered with timeout check
+- do {
+- try {
+- Thread.sleep(100);
+- } catch (Exception sleep) {
+- //
+- }
+- reqNow = System.currentTimeMillis();
+- isTimeout = ((reqNow - reqStart) > (1000 *
getStateTransferTimeout()));
+- } while ((!getStateTransfered()) && (!isTimeout));
+- } else {
+- if(getStateTransferTimeout() == -1) {
+- // wait that state is transfered
+- do {
+- try {
+- Thread.sleep(100);
+- } catch (Exception sleep) {
+- }
+- } while ((!getStateTransfered()));
+- reqNow = System.currentTimeMillis();
+- }
+- }
+- if (isTimeout || (!getStateTransfered())) {
+- counterNoStateTransfered++ ;
+- log.error(sm.getString("deltaManager.noSessionState",getName(),new
Date(beforeSendTime),new Long(reqNow - beforeSendTime)));
+- } else {
+- if (log.isInfoEnabled())
+-
log.info(sm.getString("deltaManager.sessionReceived",getName(), new
Date(beforeSendTime), new Long(reqNow - beforeSendTime)));
+- }
+- }
+-
+- /**
+- * 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.
+- *
+- * @exception LifecycleException
+- * if this component detects a fatal error that needs to be
+- * reported
+- */
+- public void stop() throws LifecycleException {
+-
+- if (log.isDebugEnabled())
+- log.debug(sm.getString("deltaManager.stopped", getName()));
+-
+-
+- // Validate and update our current component state
+- if (!started)
+- throw new
LifecycleException(sm.getString("deltaManager.notStarted"));
+- lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+- started = false;
+-
+- // Expire all active sessions
+- if (log.isInfoEnabled())
log.info(sm.getString("deltaManager.expireSessions", getName()));
+- Session sessions[] = findSessions();
+- for (int i = 0; i < sessions.length; i++) {
+- DeltaSession session = (DeltaSession) sessions[i];
+- if (!session.isValid())
+- continue;
+- try {
+- session.expire(true, isExpireSessionsOnShutdown());
+- } catch (Throwable ignore) {
+- ;
+- }
+- }
+-
+- // Require a new random number generator if we are restarted
+- this.random = null;
+- getCluster().removeManager(this);
+- replicationValve = null;
+- if (initialized) {
+- destroy();
+- }
+- }
+-
+- // ----------------------------------------- PropertyChangeListener Methods
+-
+- /**
+- * Process property change events from our associated Context.
+- *
+- * @param event
+- * The property change event that has occurred
+- */
+- public void propertyChange(PropertyChangeEvent event) {
+-
+- // Validate the source of this event
+- if (!(event.getSource() instanceof Context))
+- return;
+- // Process a relevant property change
+- if (event.getPropertyName().equals("sessionTimeout")) {
+- try {
+- setMaxInactiveInterval(((Integer) event.getNewValue()).intValue() *
60);
+- } catch (NumberFormatException e) {
+- log.error(sm.getString("deltaManager.sessionTimeout",
event.getNewValue()));
+- }
+- }
+-
+- }
+-
+- // -------------------------------------------------------- Replication
+- // Methods
+-
+- /**
+- * A message was received from another node, this is the callback method to
+- * implement if you are interested in receiving replication messages.
+- *
+- * @param cmsg -
+- * the message received.
+- */
+- public void messageDataReceived(ClusterMessage cmsg) {
+- if (cmsg != null && cmsg instanceof SessionMessage) {
+- SessionMessage msg = (SessionMessage) cmsg;
+- switch (msg.getEventType()) {
+- case SessionMessage.EVT_GET_ALL_SESSIONS:
+- case SessionMessage.EVT_SESSION_CREATED:
+- case SessionMessage.EVT_SESSION_EXPIRED:
+- case SessionMessage.EVT_SESSION_ACCESSED:
+- case SessionMessage.EVT_SESSION_DELTA: {
+- synchronized(receivedMessageQueue) {
+- if(receiverQueue) {
+- receivedMessageQueue.add(msg);
+- return ;
+- }
+- }
+- break;
+- }
+- default: {
+- //we didn't queue, do nothing
+- break;
+- }
+- } //switch
+-
+- messageReceived(msg, msg.getAddress() != null ? (Member) msg.getAddress() :
null);
+- }
+- }
+-
+- /**
+- * When the request has been completed, the replication valve will notify
+- * the manager, and the manager will decide whether any replication is
+- * needed or not. If there is a need for replication, the manager will
+- * create a session message and that will be replicated. The cluster
+- * determines where it gets sent.
+- *
+- * @param sessionId -
+- * the sessionId that just completed.
+- * @return a SessionMessage to be sent,
+- */
+- public ClusterMessage requestCompleted(String sessionId) {
+- try {
+- DeltaSession session = (DeltaSession) findSession(sessionId);
+- DeltaRequest deltaRequest = session.getDeltaRequest();
+- SessionMessage msg = null;
+- boolean isDeltaRequest = false ;
+- synchronized(deltaRequest) {
+- isDeltaRequest = deltaRequest.getSize() > 0 ;
+- if (isDeltaRequest) {
+- counterSend_EVT_SESSION_DELTA++;
+- byte[] data = serializeDeltaRequest(deltaRequest);
+- msg = new SessionMessageImpl(getName(),
+- SessionMessage.EVT_SESSION_DELTA,
+- data,
+- sessionId,
+- sessionId + "-" +
System.currentTimeMillis());
+- session.resetDeltaRequest();
+- }
+- }
+- if(!isDeltaRequest) {
+- if(!session.isPrimarySession()) {
+- counterSend_EVT_SESSION_ACCESSED++;
+- msg = new SessionMessageImpl(getName(),
+- SessionMessage.EVT_SESSION_ACCESSED,
+- null,
+- sessionId,
+- sessionId + "-" +
System.currentTimeMillis());
+- if (log.isDebugEnabled()) {
+-
log.debug(sm.getString("deltaManager.createMessage.accessChangePrimary",getName(),
sessionId));
+- }
+- }
+- } else { // log only outside synch block!
+- if (log.isDebugEnabled()) {
+-
log.debug(sm.getString("deltaManager.createMessage.delta",getName(),
sessionId));
+- }
+- }
+- session.setPrimarySession(true);
+- //check to see if we need to send out an access message
+- if ((msg == null)) {
+- long replDelta = System.currentTimeMillis() -
session.getLastTimeReplicated();
+- if (replDelta > (getMaxInactiveInterval() * 1000)) {
+- counterSend_EVT_SESSION_ACCESSED++;
+- msg = new SessionMessageImpl(getName(),
+- SessionMessage.EVT_SESSION_ACCESSED,
+- null,
+- sessionId,
+- sessionId + "-" +
System.currentTimeMillis());
+- if (log.isDebugEnabled()) {
+-
log.debug(sm.getString("deltaManager.createMessage.access",
getName(),sessionId));
+- }
+- }
+-
+- }
+-
+- //update last replicated time
+- if (msg != null) session.setLastTimeReplicated(System.currentTimeMillis());
+- return msg;
+- } catch (IOException x) {
+-
log.error(sm.getString("deltaManager.createMessage.unableCreateDeltaRequest",sessionId),
x);
+- return null;
+- }
+-
+- }
+- /**
+- * Reset manager statistics
+- */
+- public synchronized void resetStatistics() {
+- processingTime = 0 ;
+- expiredSessions = 0 ;
+- rejectedSessions = 0 ;
+- sessionReplaceCounter = 0 ;
+- counterNoStateTransfered = 0 ;
+- maxActive = getActiveSessions() ;
+- sessionCounter = getActiveSessions() ;
+- counterReceive_EVT_ALL_SESSION_DATA = 0;
+- counterReceive_EVT_GET_ALL_SESSIONS = 0;
+- counterReceive_EVT_SESSION_ACCESSED = 0 ;
+- counterReceive_EVT_SESSION_CREATED = 0 ;
+- counterReceive_EVT_SESSION_DELTA = 0 ;
+- counterReceive_EVT_SESSION_EXPIRED = 0 ;
+- counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;
+- counterSend_EVT_ALL_SESSION_DATA = 0;
+- counterSend_EVT_GET_ALL_SESSIONS = 0;
+- counterSend_EVT_SESSION_ACCESSED = 0 ;
+- counterSend_EVT_SESSION_CREATED = 0 ;
+- counterSend_EVT_SESSION_DELTA = 0 ;
+- counterSend_EVT_SESSION_EXPIRED = 0 ;
+- counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0;
+-
+- }
+-
+- // -------------------------------------------------------- persistence handler
+-
+- public void load() {
+-
+- }
+-
+- public void unload() {
+-
+- }
+-
+- // -------------------------------------------------------- expire
+-
+- /**
+- * send session expired to other cluster nodes
+- *
+- * @param id
+- * session id
+- */
+- protected void sessionExpired(String id) {
+- counterSend_EVT_SESSION_EXPIRED++ ;
+- SessionMessage msg = new
SessionMessageImpl(getName(),SessionMessage.EVT_SESSION_EXPIRED, null, id, id+
"-EXPIRED-MSG");
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.createMessage.expire",getName(), id));
+- send(msg);
+- }
+-
+- /**
+- * Exipre all find sessions.
+- */
+- public void expireAllLocalSessions()
+- {
+- long timeNow = System.currentTimeMillis();
+- Session sessions[] = findSessions();
+- int expireDirect = 0 ;
+- int expireIndirect = 0 ;
+-
+- if(log.isDebugEnabled()) log.debug("Start expire all sessions " +
getName() + " at " + timeNow + " sessioncount " + sessions.length);
+- for (int i = 0; i < sessions.length; i++) {
+- if (sessions[i] instanceof DeltaSession) {
+- DeltaSession session = (DeltaSession) sessions[i];
+- if (session.isPrimarySession()) {
+- if (session.isValid()) {
+- session.expire();
+- expireDirect++;
+- } else {
+- expireIndirect++;
+- }//end if
+- }//end if
+- }//end if
+- }//for
+- long timeEnd = System.currentTimeMillis();
+- if(log.isDebugEnabled()) log.debug("End expire sessions " + getName()
+ " exipre processingTime " + (timeEnd - timeNow) + " expired direct
sessions: " + expireDirect + " expired direct sessions: " +
expireIndirect);
+-
+- }
+-
+- /**
+- * When the manager expires session not tied to a request. The cluster will
+- * periodically ask for a list of sessions that should expire and that
+- * should be sent across the wire.
+- *
+- * @return The invalidated sessions array
+- */
+- public String[] getInvalidatedSessions() {
+- return new String[0];
+- }
+-
+- // -------------------------------------------------------- message receive
+-
+- /**
+- * Test that sender and local domain is the same
+- */
+- protected boolean checkSenderDomain(SessionMessage msg,Member sender) {
+- boolean sameDomain= true;
+- if (!sameDomain && log.isWarnEnabled()) {
+-
log.warn(sm.getString("deltaManager.receiveMessage.fromWrongDomain",
+- new Object[] {getName(),
+- msg.getEventTypeString(),
+- sender,
+- "",
+- "" }));
+- }
+- return sameDomain ;
+- }
+-
+- /**
+- * 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) {
+- if(doDomainReplication() && !checkSenderDomain(msg,sender)) {
+- return;
+- }
+- ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+- try {
+-
+- ClassLoader[] loaders = getClassLoaders();
+- if ( loaders != null && loaders.length > 0)
Thread.currentThread().setContextClassLoader(loaders[0]);
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.eventType",getName(),
msg.getEventTypeString(), sender));
+-
+- switch (msg.getEventType()) {
+- case SessionMessage.EVT_GET_ALL_SESSIONS: {
+- handleGET_ALL_SESSIONS(msg,sender);
+- break;
+- }
+- case SessionMessage.EVT_ALL_SESSION_DATA: {
+- handleALL_SESSION_DATA(msg,sender);
+- break;
+- }
+- case SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE: {
+- handleALL_SESSION_TRANSFERCOMPLETE(msg,sender);
+- break;
+- }
+- case SessionMessage.EVT_SESSION_CREATED: {
+- handleSESSION_CREATED(msg,sender);
+- break;
+- }
+- case SessionMessage.EVT_SESSION_EXPIRED: {
+- handleSESSION_EXPIRED(msg,sender);
+- break;
+- }
+- case SessionMessage.EVT_SESSION_ACCESSED: {
+- handleSESSION_ACCESSED(msg,sender);
+- break;
+- }
+- case SessionMessage.EVT_SESSION_DELTA: {
+- handleSESSION_DELTA(msg,sender);
+- break;
+- }
+- default: {
+- //we didn't recognize the message type, do nothing
+- break;
+- }
+- } //switch
+- } catch (Exception x) {
+-
log.error(sm.getString("deltaManager.receiveMessage.error",getName()), x);
+- } finally {
+- Thread.currentThread().setContextClassLoader(contextLoader);
+- }
+- }
+-
+- // -------------------------------------------------------- message receiver
handler
+-
+-
+- /**
+- * handle receive session state is complete transfered
+- * @param msg
+- * @param sender
+- */
+- protected void handleALL_SESSION_TRANSFERCOMPLETE(SessionMessage msg, Member sender)
{
+- counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE++ ;
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.transfercomplete",getName(),
sender.getHost(), new Integer(sender.getPort())));
+- stateTransferCreateSendTime = msg.getTimestamp() ;
+- stateTransfered = true ;
+- }
+-
+- /**
+- * handle receive session delta
+- * @param msg
+- * @param sender
+- * @throws IOException
+- * @throws ClassNotFoundException
+- */
+- protected void handleSESSION_DELTA(SessionMessage msg, Member sender) throws
IOException, ClassNotFoundException {
+- counterReceive_EVT_SESSION_DELTA++;
+- byte[] delta = msg.getSession();
+- DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+- if (session != null) {
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.delta",getName(),
msg.getSessionID()));
+- DeltaRequest dreq = deserializeDeltaRequest(session, delta);
+- dreq.execute(session, notifyListenersOnReplication);
+- session.setPrimarySession(false);
+- }
+- }
+-
+- /**
+- * handle receive session is access at other node ( primary session is now false)
+- * @param msg
+- * @param sender
+- * @throws IOException
+- */
+- protected void handleSESSION_ACCESSED(SessionMessage msg,Member sender) throws
IOException {
+- counterReceive_EVT_SESSION_ACCESSED++;
+- DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+- if (session != null) {
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.accessed",getName(),
msg.getSessionID()));
+- session.access();
+- session.setPrimarySession(false);
+- session.endAccess();
+- }
+- }
+-
+- /**
+- * handle receive session is expire at other node ( expire session also here)
+- * @param msg
+- * @param sender
+- * @throws IOException
+- */
+- protected void handleSESSION_EXPIRED(SessionMessage msg,Member sender) throws
IOException {
+- counterReceive_EVT_SESSION_EXPIRED++;
+- DeltaSession session = (DeltaSession) findSession(msg.getSessionID());
+- if (session != null) {
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.expired",getName(),
msg.getSessionID()));
+- session.expire(notifySessionListenersOnReplication, false);
+- }
+- }
+-
+- /**
+- * handle receive new session is created at other node (create backup - primary
false)
+- * @param msg
+- * @param sender
+- */
+- protected void handleSESSION_CREATED(SessionMessage msg,Member sender) {
+- counterReceive_EVT_SESSION_CREATED++;
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.createNewSession",getName(),
msg.getSessionID()));
+- DeltaSession session = (DeltaSession) createEmptySession();
+- session.setManager(this);
+- session.setValid(true);
+- session.setPrimarySession(false);
+- session.setCreationTime(msg.getTimestamp());
+- // use container maxInactiveInterval so that session will expire correctly in
case of primary transfer
+- session.setMaxInactiveInterval(getMaxInactiveInterval());
+- session.access();
+- if(notifySessionListenersOnReplication)
+- session.setId(msg.getSessionID());
+- else
+- session.setIdInternal(msg.getSessionID());
+- session.resetDeltaRequest();
+- session.endAccess();
+-
+- }
+-
+- /**
+- * handle receive sessions from other not ( restart )
+- * @param msg
+- * @param sender
+- * @throws ClassNotFoundException
+- * @throws IOException
+- */
+- protected void handleALL_SESSION_DATA(SessionMessage msg,Member sender) throws
ClassNotFoundException, IOException {
+- counterReceive_EVT_ALL_SESSION_DATA++;
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataBegin",getName()));
+- byte[] data = msg.getSession();
+- deserializeSessions(data);
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataAfter",getName()));
+- //stateTransferred = true;
+- }
+-
+- /**
+- * handle receive that other node want all sessions ( restart )
+- * a) send all sessions with one message
+- * b) send session at blocks
+- * After sending send state is complete transfered
+- * @param msg
+- * @param sender
+- * @throws IOException
+- */
+- protected void handleGET_ALL_SESSIONS(SessionMessage msg, Member sender) throws
IOException {
+- counterReceive_EVT_GET_ALL_SESSIONS++;
+- //get a list of all the session from this manager
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.unloadingBegin",
getName()));
+- // Write the number of active sessions, followed by the details
+- // get all sessions and serialize without sync
+- Session[] currentSessions = findSessions();
+- long findSessionTimestamp = System.currentTimeMillis() ;
+- if (isSendAllSessions()) {
+- sendSessions(sender, currentSessions, findSessionTimestamp);
+- } else {
+- // send session at blocks
+- int len = currentSessions.length < getSendAllSessionsSize() ?
currentSessions.length : getSendAllSessionsSize();
+- Session[] sendSessions = new Session[len];
+- for (int i = 0; i < currentSessions.length; i +=
getSendAllSessionsSize()) {
+- len = i + getSendAllSessionsSize() > currentSessions.length ?
currentSessions.length - i : getSendAllSessionsSize();
+- System.arraycopy(currentSessions, i, sendSessions, 0, len);
+- sendSessions(sender, sendSessions,findSessionTimestamp);
+- if (getSendAllSessionsWaitTime() > 0) {
+- try {
+- Thread.sleep(getSendAllSessionsWaitTime());
+- } catch (Exception sleep) {
+- }
+- }//end if
+- }//for
+- }//end if
+-
+- SessionMessage newmsg = new
SessionMessageImpl(name,SessionMessage.EVT_ALL_SESSION_TRANSFERCOMPLETE,
null,"SESSION-STATE-TRANSFERED", "SESSION-STATE-TRANSFERED"+
getName());
+- newmsg.setTimestamp(findSessionTimestamp);
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.createMessage.allSessionTransfered",getName()));
+- counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE++;
+- cluster.send(newmsg, sender);
+- }
+-
+-
+- /**
+- * send a block of session to sender
+- * @param sender
+- * @param currentSessions
+- * @param sendTimestamp
+- * @throws IOException
+- */
+- protected void sendSessions(Member sender, Session[] currentSessions,long
sendTimestamp) throws IOException {
+- byte[] data = serializeSessions(currentSessions);
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.receiveMessage.unloadingAfter",getName()));
+- SessionMessage newmsg = new
SessionMessageImpl(name,SessionMessage.EVT_ALL_SESSION_DATA,
data,"SESSION-STATE", "SESSION-STATE-" + getName());
+- newmsg.setTimestamp(sendTimestamp);
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaManager.createMessage.allSessionData",getName()));
+- counterSend_EVT_ALL_SESSION_DATA++;
+- cluster.send(newmsg, sender);
+- }
+-
+- public ClusterManager cloneFromTemplate() {
+- DeltaManager result = new DeltaManager();
+- result.name = "Clone-from-"+name;
+- result.cluster = cluster;
+- result.replicationValve = replicationValve;
+- result.maxActiveSessions = maxActiveSessions;
+- result.expireSessionsOnShutdown = expireSessionsOnShutdown;
+- result.notifyListenersOnReplication = notifyListenersOnReplication;
+- result.notifySessionListenersOnReplication =
notifySessionListenersOnReplication;
+- result.stateTransferTimeout = stateTransferTimeout;
+- result.sendAllSessions = sendAllSessions;
+- result.sendClusterDomainOnly = sendClusterDomainOnly ;
+- result.sendAllSessionsSize = sendAllSessionsSize;
+- result.sendAllSessionsWaitTime = sendAllSessionsWaitTime ;
+- result.receiverQueue = receiverQueue ;
+- result.stateTimestampDrop = stateTimestampDrop ;
+- result.stateTransferCreateSendTime = stateTransferCreateSendTime;
+- return result;
+- }
+-}
+Index: java/org/apache/catalina/ha/session/SimpleTcpReplicationManager.java
+===================================================================
+--- java/org/apache/catalina/ha/session/SimpleTcpReplicationManager.java (revision
590752)
++++ java/org/apache/catalina/ha/session/SimpleTcpReplicationManager.java (working copy)
+@@ -1,685 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.session;
+-
+-import java.io.IOException;
+-
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.realm.GenericPrincipal;
+-import org.apache.catalina.session.StandardManager;
+-import org.apache.catalina.tribes.io.ReplicationStream;
+-import java.io.ByteArrayInputStream;
+-import org.apache.catalina.Loader;
+-
+-/**
+- * 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:mail@filip.net">Filip Hanik</a>
+- * @author Bela Ban (modifications for synchronous replication)
+- * @version 1.0 for TC 4.0
+- * Description: The InMemoryReplicationManager is a session manager that replicated
+- * session information in memory.
+- * <BR><BR>
+- * The InMemoryReplicationManager 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 InMemoryReplicationManager 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.
+- */
+-public class SimpleTcpReplicationManager extends StandardManager implements
ClusterManager
+-{
+- public static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( SimpleTcpReplicationManager.class );
+-
+- //the channel configuration
+- protected String mChannelConfig = null;
+-
+- //the group name
+- protected String mGroupName = "TomcatReplication";
+-
+- //somehow start() gets called more than once
+- protected boolean mChannelStarted = false;
+-
+- //log to screen
+- protected boolean mPrintToScreen = true;
+-
+- protected boolean defaultMode = false;
+-
+- protected boolean mManagerRunning = false;
+-
+- /** Use synchronous rather than asynchronous replication. Every session modification
(creation, change, removal etc)
+- * will be sent to all members. The call will then wait for max milliseconds, or
forever (if timeout is 0) for
+- * all responses.
+- */
+- protected boolean synchronousReplication=true;
+-
+- /** Set to true if we don't want the sessions to expire on shutdown */
+- protected boolean mExpireSessionsOnShutdown = true;
+-
+- protected boolean useDirtyFlag = false;
+-
+- protected String name;
+-
+- protected boolean distributable = true;
+-
+- protected CatalinaCluster cluster;
+-
+- protected java.util.HashMap invalidatedSessions = new java.util.HashMap();
+-
+- /**
+- * Flag to keep track if the state has been transferred or not
+- * Assumes false.
+- */
+- protected boolean stateTransferred = false;
+- private boolean notifyListenersOnReplication;
+- private boolean sendClusterDomainOnly = true ;
+-
+- /**
+- * Constructor, just calls super()
+- *
+- */
+- public SimpleTcpReplicationManager()
+- {
+- super();
+- }
+-
+- public boolean doDomainReplication() {
+- return sendClusterDomainOnly;
+- }
+-
+- /**
+- * @param sendClusterDomainOnly The sendClusterDomainOnly to set.
+- */
+- public void setDomainReplication(boolean sendClusterDomainOnly) {
+- this.sendClusterDomainOnly = sendClusterDomainOnly;
+- }
+-
+- /**
+- * @return Returns the defaultMode.
+- */
+- public boolean isDefaultMode() {
+- return defaultMode;
+- }
+- /**
+- * @param defaultMode The defaultMode to set.
+- */
+- public void setDefaultMode(boolean defaultMode) {
+- this.defaultMode = defaultMode;
+- }
+-
+- public boolean isManagerRunning()
+- {
+- return mManagerRunning;
+- }
+-
+- public void setUseDirtyFlag(boolean usedirtyflag)
+- {
+- this.useDirtyFlag = usedirtyflag;
+- }
+-
+- public void setExpireSessionsOnShutdown(boolean expireSessionsOnShutdown)
+- {
+- mExpireSessionsOnShutdown = expireSessionsOnShutdown;
+- }
+-
+- public void setCluster(CatalinaCluster cluster) {
+- if(log.isDebugEnabled())
+- log.debug("Cluster associated with SimpleTcpReplicationManager");
+- this.cluster = cluster;
+- }
+-
+- public boolean getExpireSessionsOnShutdown()
+- {
+- return mExpireSessionsOnShutdown;
+- }
+-
+- public void setPrintToScreen(boolean printtoscreen)
+- {
+- if(log.isDebugEnabled())
+- log.debug("Setting screen debug to:"+printtoscreen);
+- mPrintToScreen = printtoscreen;
+- }
+-
+- public void setSynchronousReplication(boolean flag)
+- {
+- synchronousReplication=flag;
+- }
+-
+- /**
+- * Override persistence since they don't go hand in hand with replication for
now.
+- */
+- public void unload() throws IOException {
+- if ( !getDistributable() ) {
+- super.unload();
+- }
+- }
+-
+- /**
+- * 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(String sessionId, boolean notify, boolean setId)
+- {
+-
+- //inherited from the basic manager
+- if ((getMaxActiveSessions() >= 0) &&
+- (sessions.size() >= getMaxActiveSessions()))
+- throw new
IllegalStateException(sm.getString("standardManager.createSession.ise"));
+-
+-
+- Session 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);
+- if(sessionId == null)
+- sessionId = generateSessionId();
+- if ( setId ) session.setId(sessionId);
+- if ( notify && (cluster!=null) ) {
+- ((ReplicatedSession)session).setIsDirty(true);
+- }
+- return (session);
+- }//createSession
+-
+- //=========================================================================
+- // 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(String sessionId)
+- {
+- //create a session and notify the other nodes in the cluster
+- Session session = createSession(sessionId,getDistributable(),true);
+- add(session);
+- return session;
+- }
+-
+- public void sessionInvalidated(String sessionId) {
+- synchronized ( invalidatedSessions ) {
+- invalidatedSessions.put(sessionId, sessionId);
+- }
+- }
+-
+- public String[] getInvalidatedSessions() {
+- synchronized ( invalidatedSessions ) {
+- String[] result = new String[invalidatedSessions.size()];
+- invalidatedSessions.values().toArray(result);
+- return result;
+- }
+-
+- }
+-
+- public ClusterMessage requestCompleted(String sessionId)
+- {
+- if ( !getDistributable() ) {
+- log.warn("Received requestCompleted message, although this
context["+
+- getName()+"] is not distributable. Ignoring message");
+- return null;
+- }
+- try
+- {
+- if ( invalidatedSessions.get(sessionId) != null ) {
+- synchronized ( invalidatedSessions ) {
+- invalidatedSessions.remove(sessionId);
+- SessionMessage msg = new SessionMessageImpl(name,
+- SessionMessage.EVT_SESSION_EXPIRED,
+- null,
+- sessionId,
+- sessionId);
+- return msg;
+- }
+- } else {
+- ReplicatedSession session = (ReplicatedSession) findSession(
+- sessionId);
+- if (session != null) {
+- //return immediately if the session is not dirty
+- if (useDirtyFlag && (!session.isDirty())) {
+- //but before we return doing nothing,
+- //see if we should send
+- //an updated last access message so that
+- //sessions across cluster dont expire
+- long interval = session.getMaxInactiveInterval();
+- long lastaccdist = System.currentTimeMillis() -
+- session.getLastAccessWasDistributed();
+- if ( ((interval*1000) / lastaccdist)< 3 ) {
+- SessionMessage accmsg = new SessionMessageImpl(name,
+- SessionMessage.EVT_SESSION_ACCESSED,
+- null,
+- sessionId,
+- sessionId);
+-
session.setLastAccessWasDistributed(System.currentTimeMillis());
+- return accmsg;
+- }
+- return null;
+- }
+-
+- session.setIsDirty(false);
+- if (log.isDebugEnabled()) {
+- try {
+- log.debug("Sending session to cluster=" +
session);
+- }
+- catch (Exception ignore) {}
+- }
+- SessionMessage msg = new SessionMessageImpl(name,
+- SessionMessage.EVT_SESSION_CREATED,
+- writeSession(session),
+- session.getIdInternal(),
+- session.getIdInternal());
+- return msg;
+- } //end if
+- }//end if
+- }
+- catch (Exception x )
+- {
+- log.error("Unable to replicate session",x);
+- }
+- return null;
+- }
+-
+- /**
+- * 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 )
+- {
+- try
+- {
+- java.io.ByteArrayOutputStream session_data = new
java.io.ByteArrayOutputStream();
+- java.io.ObjectOutputStream session_out = new
java.io.ObjectOutputStream(session_data);
+- session_out.flush();
+- boolean hasPrincipal = session.getPrincipal() != null;
+- session_out.writeBoolean(hasPrincipal);
+- if ( hasPrincipal )
+- {
+-
session_out.writeObject(SerializablePrincipal.createPrincipal((GenericPrincipal)session.getPrincipal()));
+- }//end if
+- ((ReplicatedSession)session).writeObjectData(session_out);
+- return session_data.toByteArray();
+-
+- }
+- catch ( Exception x )
+- {
+- log.error("Failed to serialize the session!",x);
+- }
+- return null;
+- }
+-
+- /**
+- * Open Stream and use correct ClassLoader (Container) Switch
+- * ThreadClassLoader
+- *
+- * @param data
+- * @return The object input stream
+- * @throws IOException
+- */
+- public ReplicationStream getReplicationStream(byte[] data) throws IOException {
+- return getReplicationStream(data,0,data.length);
+- }
+-
+- public ReplicationStream getReplicationStream(byte[] data, int offset, int length)
throws IOException {
+- ByteArrayInputStream fis =null;
+- ReplicationStream ois = null;
+- Loader loader = null;
+- ClassLoader classLoader = null;
+- //fix to be able to run the DeltaManager
+- //stand alone without a container.
+- //use the Threads context class loader
+- if (container != null)
+- loader = container.getLoader();
+- if (loader != null)
+- classLoader = loader.getClassLoader();
+- else
+- classLoader = Thread.currentThread().getContextClassLoader();
+- //end fix
+- fis = new ByteArrayInputStream(data, offset, length);
+- if ( classLoader == Thread.currentThread().getContextClassLoader() ) {
+- ois = new ReplicationStream(fis, new ClassLoader[] {classLoader});
+- } else {
+- ois = new ReplicationStream(fis, new ClassLoader[]
{classLoader,Thread.currentThread().getContextClassLoader()});
+- }
+- return ois;
+- }
+-
+-
+-
+-
+- /**
+- * 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, String sessionId )
+- {
+- try
+- {
+- ReplicationStream session_in = getReplicationStream(data);
+-
+- Session session = sessionId!=null?this.findSession(sessionId):null;
+- boolean isNew = (session==null);
+- //clear the old values from the existing session
+- if ( session!=null ) {
+- ReplicatedSession rs = (ReplicatedSession)session;
+- rs.expire(false); //cleans up the previous values, since we are not
doing removes
+- session = null;
+- }//end if
+-
+- if (session==null) {
+- session = createSession(null,false, false);
+- sessions.remove(session.getIdInternal());
+- }
+-
+-
+- boolean hasPrincipal = session_in.readBoolean();
+- SerializablePrincipal p = null;
+- if ( hasPrincipal )
+- p = (SerializablePrincipal)session_in.readObject();
+- ((ReplicatedSession)session).readObjectData(session_in);
+- if ( hasPrincipal )
+- session.setPrincipal(p.getPrincipal(getContainer().getRealm()));
+- ((ReplicatedSession)session).setId(sessionId,isNew);
+- ReplicatedSession rsession = (ReplicatedSession)session;
+- rsession.setAccessCount(1);
+- session.setManager(this);
+- session.setValid(true);
+- rsession.setLastAccessedTime(System.currentTimeMillis());
+- rsession.setThisAccessedTime(System.currentTimeMillis());
+- ((ReplicatedSession)session).setAccessCount(0);
+- session.setNew(false);
+- if(log.isTraceEnabled())
+- log.trace("Session loaded id="+sessionId +
+- " actualId="+session.getId()+
+- "
exists="+this.sessions.containsKey(sessionId)+
+- " valid="+rsession.isValid());
+- return session;
+-
+- }
+- catch ( Exception x )
+- {
+- log.error("Failed to deserialize the session!",x);
+- }
+- return null;
+- }
+-
+- public String getName() {
+- return this.name;
+- }
+- /**
+- * 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);
+- }
+- }
+-
+- /**
+- * 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);
+- }
+- }
+-
+- public void setDistributable(boolean dist) {
+- this.distributable = dist;
+- }
+-
+- public boolean getDistributable() {
+- return distributable;
+- }
+-
+- /**
+- * 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);
+- }
+- }
+-
+- public void messageDataReceived(ClusterMessage cmsg) {
+- try {
+- if ( cmsg instanceof SessionMessage ) {
+- SessionMessage msg = (SessionMessage)cmsg;
+- messageReceived(msg,
+- msg.getAddress() != null ? (Member) msg.getAddress() :
null);
+- }
+- } catch(Throwable ex){
+- log.error("InMemoryReplicationManager.messageDataReceived()",
ex);
+- }//catch
+- }
+-
+- public boolean isStateTransferred() {
+- return stateTransferred;
+- }
+-
+- public void setName(String name) {
+- this.name = name;
+- }
+- public boolean isNotifyListenersOnReplication() {
+- return notifyListenersOnReplication;
+- }
+- public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
+- this.notifyListenersOnReplication = notifyListenersOnReplication;
+- }
+-
+-
+- /*
+- * @see org.apache.catalina.ha.ClusterManager#getCluster()
+- */
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- public ClusterManager cloneFromTemplate() {
+- throw new UnsupportedOperationException();
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/session/JvmRouteSessionIDBinderListener.java
+===================================================================
+--- java/org/apache/catalina/ha/session/JvmRouteSessionIDBinderListener.java (revision
590752)
++++ java/org/apache/catalina/ha/session/JvmRouteSessionIDBinderListener.java (working
copy)
+@@ -1,167 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.session;
+-
+-import java.io.IOException;
+-
+-import org.apache.catalina.Container;
+-import org.apache.catalina.Context;
+-import org.apache.catalina.Engine;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.core.StandardEngine;
+-import org.apache.catalina.ha.*;
+-
+-/**
+- * Receive SessionID cluster change from other backup node after primary session
+- * node is failed.
+- *
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-public class JvmRouteSessionIDBinderListener extends ClusterListener {
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- protected static final String info =
"org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener/1.1";
+-
+- //--Instance Variables--------------------------------------
+-
+-
+- protected boolean started = false;
+-
+- /**
+- * number of session that goes to this cluster node
+- */
+- private long numberOfSessions = 0;
+-
+- //--Constructor---------------------------------------------
+-
+- public JvmRouteSessionIDBinderListener() {
+- }
+-
+- //--Logic---------------------------------------------------
+-
+- /**
+- * Return descriptive information about this implementation.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- /**
+- * @return Returns the numberOfSessions.
+- */
+- public long getNumberOfSessions() {
+- return numberOfSessions;
+- }
+-
+- /**
+- * Add this Mover as Cluster Listener ( receiver)
+- *
+- * @throws LifecycleException
+- */
+- public void start() throws LifecycleException {
+- if (started)
+- return;
+- getCluster().addClusterListener(this);
+- started = true;
+- if (log.isInfoEnabled())
+- log.info(sm.getString("jvmRoute.clusterListener.started"));
+- }
+-
+- /**
+- * Remove this from Cluster Listener
+- *
+- * @throws LifecycleException
+- */
+- public void stop() throws LifecycleException {
+- started = false;
+- getCluster().removeClusterListener(this);
+- if (log.isInfoEnabled())
+- log.info(sm.getString("jvmRoute.clusterListener.stopped"));
+- }
+-
+- /**
+- * Callback from the cluster, when a message is received, The cluster will
+- * broadcast it invoking the messageReceived on the receiver.
+- *
+- * @param msg
+- * ClusterMessage - the message received from the cluster
+- */
+- public void messageReceived(ClusterMessage msg) {
+- if (msg instanceof SessionIDMessage && msg != null) {
+- SessionIDMessage sessionmsg = (SessionIDMessage) msg;
+- if (log.isDebugEnabled())
+- log.debug(sm.getString(
+- "jvmRoute.receiveMessage.sessionIDChanged",
sessionmsg
+- .getOrignalSessionID(), sessionmsg
+- .getBackupSessionID(), sessionmsg
+- .getContextPath()));
+- Container container = getCluster().getContainer();
+- Container host = null ;
+- if(container instanceof Engine) {
+- host = container.findChild(sessionmsg.getHost());
+- } else {
+- host = container ;
+- }
+- if (host != null) {
+- Context context = (Context) host.findChild(sessionmsg
+- .getContextPath());
+- if (context != null) {
+- try {
+- Session session = context.getManager().findSession(
+- sessionmsg.getOrignalSessionID());
+- if (session != null) {
+- session.setId(sessionmsg.getBackupSessionID());
+- } else if (log.isInfoEnabled())
+- log.info(sm.getString("jvmRoute.lostSession",
+- sessionmsg.getOrignalSessionID(),
+- sessionmsg.getContextPath()));
+- } catch (IOException e) {
+- log.error(e);
+- }
+-
+- } else if (log.isErrorEnabled())
+- log.error(sm.getString("jvmRoute.contextNotFound",
+- sessionmsg.getContextPath(), ((StandardEngine) host
+- .getParent()).getJvmRoute()));
+- } else if (log.isErrorEnabled())
+- log.error(sm.getString("jvmRoute.hostNotFound",
sessionmsg.getContextPath()));
+- }
+- return;
+- }
+-
+- /**
+- * Accept only SessionIDMessages
+- *
+- * @param msg
+- * ClusterMessage
+- * @return boolean - returns true to indicate that messageReceived should be
+- * invoked. If false is returned, the messageReceived method will
+- * not be invoked.
+- */
+- public boolean accept(ClusterMessage msg) {
+- return (msg instanceof SessionIDMessage);
+- }
+-}
+-
+Index: java/org/apache/catalina/ha/session/SessionMessage.java
+===================================================================
+--- java/org/apache/catalina/ha/session/SessionMessage.java (revision 590752)
++++ java/org/apache/catalina/ha/session/SessionMessage.java (working copy)
+@@ -1,104 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.session;
+-import org.apache.catalina.ha.ClusterMessage;
+-
+-/**
+- *
+- * <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>
+- *
+- */
+-
+-public interface SessionMessage extends ClusterMessage, 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
+- */
+- public static final int EVT_SESSION_EXPIRED = 2;
+-
+- /**
+- * 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_SESSION_DELTA = 13;
+-
+- /**
+- * When a session state is transferred, this is the event.
+- */
+- public static final int EVT_ALL_SESSION_DATA = 12;
+-
+- /**
+- * When a session state is complete transferred, this is the event.
+- */
+- public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE = 14;
+-
+-
+-
+- public String getContextName();
+-
+- public String getEventTypeString();
+-
+- /**
+- * returns the event type
+- * @return one of the event types EVT_XXXX
+- */
+- public int getEventType();
+- /**
+- * @return the serialized data for the session
+- */
+- public byte[] getSession();
+- /**
+- * @return the session ID for the session
+- */
+- public String getSessionID();
+-
+-
+-
+-}//SessionMessage
+Index: java/org/apache/catalina/ha/session/ClusterSessionListener.java
+===================================================================
+--- java/org/apache/catalina/ha/session/ClusterSessionListener.java (revision 590752)
++++ java/org/apache/catalina/ha/session/ClusterSessionListener.java (working copy)
+@@ -1,108 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.session;
+-
+-import java.util.Map;
+-
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.*;
+-
+-/**
+- * Receive replicated SessionMessage form other cluster node.
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-public class ClusterSessionListener extends ClusterListener {
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- protected static final String info =
"org.apache.catalina.session.ClusterSessionListener/1.1";
+-
+- //--Constructor---------------------------------------------
+-
+- public ClusterSessionListener() {
+- }
+-
+- //--Logic---------------------------------------------------
+-
+- /**
+- * Return descriptive information about this implementation.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- /**
+- * Callback from the cluster, when a message is received, The cluster will
+- * broadcast it invoking the messageReceived on the receiver.
+- *
+- * @param myobj
+- * ClusterMessage - the message received from the cluster
+- */
+- public void messageReceived(ClusterMessage myobj) {
+- if (myobj != null && myobj instanceof SessionMessage) {
+- SessionMessage msg = (SessionMessage) myobj;
+- String ctxname = msg.getContextName();
+- //check if the message is a EVT_GET_ALL_SESSIONS,
+- //if so, wait until we are fully started up
+- Map managers = cluster.getManagers() ;
+- if (ctxname == null) {
+- java.util.Iterator i = managers.keySet().iterator();
+- while (i.hasNext()) {
+- String key = (String) i.next();
+- ClusterManager mgr = (ClusterManager) managers.get(key);
+- if (mgr != null)
+- mgr.messageDataReceived(msg);
+- else {
+- //this happens a lot before the system has started
+- // up
+- if (log.isDebugEnabled())
+- log.debug("Context manager doesn't exist:"
+- + key);
+- }
+- }
+- } else {
+- ClusterManager mgr = (ClusterManager) managers.get(ctxname);
+- if (mgr != null)
+- mgr.messageDataReceived(msg);
+- else if (log.isWarnEnabled())
+- log.warn("Context manager doesn't exist:" + ctxname);
+- }
+- }
+- return;
+- }
+-
+- /**
+- * Accept only SessionMessage
+- *
+- * @param msg
+- * ClusterMessage
+- * @return boolean - returns true to indicate that messageReceived should be
+- * invoked. If false is returned, the messageReceived method will
+- * not be invoked.
+- */
+- public boolean accept(ClusterMessage msg) {
+- return (msg instanceof SessionMessage);
+- }
+-}
+-
+Index: java/org/apache/catalina/ha/session/JvmRouteBinderValve.java
+===================================================================
+--- java/org/apache/catalina/ha/session/JvmRouteBinderValve.java (revision 590752)
++++ java/org/apache/catalina/ha/session/JvmRouteBinderValve.java (working copy)
+@@ -1,544 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.session;
+-
+-import java.io.IOException;
+-
+-import javax.servlet.ServletException;
+-import javax.servlet.http.Cookie;
+-
+-import org.apache.catalina.Container;
+-import org.apache.catalina.Context;
+-import org.apache.catalina.Engine;
+-import org.apache.catalina.Globals;
+-import org.apache.catalina.Host;
+-import org.apache.catalina.Lifecycle;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.LifecycleListener;
+-import org.apache.catalina.Manager;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.ClusterValve;
+-import org.apache.catalina.connector.Request;
+-import org.apache.catalina.connector.Response;
+-import org.apache.catalina.session.ManagerBase;
+-import org.apache.catalina.util.LifecycleSupport;
+-import org.apache.catalina.util.StringManager;
+-import org.apache.catalina.valves.ValveBase;
+-
+-/**
+- * Valve to handle Tomcat jvmRoute takeover using mod_jk module after node
+- * failure. After a node crashed the next request going to other cluster node.
+- * Now the answering from apache is slower ( make some error handshaking. Very
+- * bad with apache at my windows.). We rewrite now the cookie jsessionid
+- * information to the backup cluster node. After the next response all client
+- * request goes direct to the backup node. The change sessionid send also to all
+- * other cluster nodes. Well, now the session stickyness work directly to the
+- * backup node and traffic don't go back too restarted cluster nodes!
+- *
+- * At all cluster node you must configure the as ClusterListener since 5.5.10
+- * {@link org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener
JvmRouteSessionIDBinderListener}
+- * or before with
+- * org.apache.catalina.ha.session.JvmRouteSessionIDBinderListenerLifecycle.
+- *
+- * Add this Valve to your host definition at conf/server.xml .
+- *
+- * Since 5.5.10 as direct cluster valve:<br/>
+- * <pre>
+- * <Cluster>
+- * <Valve
className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
+- * </Cluster>
+- * </pre>
+- * <br />
+- * Before 5.5.10 as Host element:<br/>
+- * <pre>
+- * <Hostr>
+- * <Valve
className="org.apache.catalina.ha.session.JvmRouteBinderValve" />
+- * </Hostr>
+- * </pre>
+- *
+- * Trick:<br/>
+- * You can enable this mod_jk turnover mode via JMX before you drop a node to all backup
nodes!
+- * Set enable true on all JvmRouteBinderValve backups, disable worker at mod_jk
+- * and then drop node and restart it! Then enable mod_jk Worker and disable
JvmRouteBinderValves again.
+- * This use case means that only requested session are migrated.
+- *
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-public class JvmRouteBinderValve extends ValveBase implements ClusterValve, Lifecycle {
+-
+- /*--Static Variables----------------------------------------*/
+- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+- .getLog(JvmRouteBinderValve.class);
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- protected static final String info =
"org.apache.catalina.ha.session.JvmRouteBinderValve/1.2";
+-
+- /*--Instance Variables--------------------------------------*/
+-
+- /**
+- * the cluster
+- */
+- protected CatalinaCluster cluster;
+-
+- /**
+- * The string manager for this package.
+- */
+- protected StringManager sm = StringManager.getManager(Constants.Package);
+-
+- /**
+- * Has this component been started yet?
+- */
+- protected boolean started = false;
+-
+- /**
+- * enabled this component
+- */
+- protected boolean enabled = true;
+-
+- /**
+- * number of session that no at this tomcat instanz hosted
+- */
+- protected long numberOfSessions = 0;
+-
+- protected String sessionIdAttribute =
"org.apache.catalina.ha.session.JvmRouteOrignalSessionID";
+-
+- /**
+- * The lifecycle event support for this component.
+- */
+- protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+-
+- /*--Logic---------------------------------------------------*/
+-
+- /**
+- * Return descriptive information about this implementation.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- /**
+- * set session id attribute to failed node for request.
+- *
+- * @return Returns the sessionIdAttribute.
+- */
+- public String getSessionIdAttribute() {
+- return sessionIdAttribute;
+- }
+-
+- /**
+- * get name of failed reqeust session attribute
+- *
+- * @param sessionIdAttribute
+- * The sessionIdAttribute to set.
+- */
+- public void setSessionIdAttribute(String sessionIdAttribute) {
+- this.sessionIdAttribute = sessionIdAttribute;
+- }
+-
+- /**
+- * @return Returns the number of migrated sessions.
+- */
+- public long getNumberOfSessions() {
+- return numberOfSessions;
+- }
+-
+- /**
+- * @return Returns the enabled.
+- */
+- public boolean getEnabled() {
+- return enabled;
+- }
+-
+- /**
+- * @param enabled
+- * The enabled to set.
+- */
+- public void setEnabled(boolean enabled) {
+- this.enabled = enabled;
+- }
+-
+- /**
+- * Detect possible the JVMRoute change at cluster backup node..
+- *
+- * @param request
+- * tomcat request being processed
+- * @param response
+- * tomcat response being processed
+- * @exception IOException
+- * if an input/output error has occurred
+- * @exception ServletException
+- * if a servlet error has occurred
+- */
+- public void invoke(Request request, Response response) throws IOException,
+- ServletException {
+-
+- if (getEnabled()
+- && getCluster() != null
+- && request.getContext() != null
+- && request.getContext().getDistributable() ) {
+- // valve cluster can access manager - other cluster handle turnover
+- // at host level - hopefully!
+- Manager manager = request.getContext().getManager();
+- if (manager != null && manager instanceof ClusterManager
+- &&
getCluster().getManager(((ClusterManager)manager).getName()) != null)
+- handlePossibleTurnover(request, response);
+- }
+- // Pass this request on to the next valve in our pipeline
+- getNext().invoke(request, response);
+- }
+-
+- /**
+- * handle possible session turn over.
+- *
+- * @see JvmRouteBinderValve#handleJvmRoute(Request, Response, String, String)
+- * @param request current request
+- * @param response current response
+- */
+- protected void handlePossibleTurnover(Request request, Response response) {
+- Session session = request.getSessionInternal(false);
+- if (session != null) {
+- long t1 = System.currentTimeMillis();
+- String jvmRoute = getLocalJvmRoute(request);
+- if (jvmRoute == null) {
+- if (log.isDebugEnabled())
+-
log.debug(sm.getString("jvmRoute.missingJvmRouteAttribute"));
+- return;
+- }
+- handleJvmRoute( request, response,session.getIdInternal(), jvmRoute);
+- if (log.isDebugEnabled()) {
+- long t2 = System.currentTimeMillis();
+- long time = t2 - t1;
+- log.debug(sm.getString("jvmRoute.turnoverInfo", new
Long(time)));
+- }
+- }
+- }
+-
+- /**
+- * get jvmroute from engine
+- *
+- * @param request current request
+- * @return return jvmRoute from ManagerBase or null
+- */
+- protected String getLocalJvmRoute(Request request) {
+- Manager manager = getManager(request);
+- if(manager instanceof ManagerBase)
+- return ((ManagerBase) manager).getJvmRoute();
+- return null ;
+- }
+-
+- /**
+- * get Cluster DeltaManager
+- *
+- * @param request current request
+- * @return manager or null
+- */
+- protected Manager getManager(Request request) {
+- Manager manager = request.getContext().getManager();
+- if (log.isDebugEnabled()) {
+- if(manager != null)
+- log.debug(sm.getString("jvmRoute.foundManager", manager,
request.getContext().getName()));
+- else
+- log.debug(sm.getString("jvmRoute.notFoundManager", manager,
request.getContext().getName()));
+- }
+- return manager;
+- }
+-
+- /**
+- * @return Returns the cluster.
+- */
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- /**
+- * @param cluster The cluster to set.
+- */
+- public void setCluster(CatalinaCluster cluster) {
+- this.cluster = cluster;
+- }
+-
+- /**
+- * Handle jvmRoute stickyness after tomcat instance failed. After this
+- * correction a new Cookie send to client with new jvmRoute and the
+- * SessionID change propage to the other cluster nodes.
+- *
+- * @param request current request
+- * @param response
+- * Tomcat Response
+- * @param sessionId
+- * request SessionID from Cookie
+- * @param localJvmRoute
+- * local jvmRoute
+- */
+- protected void handleJvmRoute(
+- Request request, Response response,String sessionId, String localJvmRoute)
{
+- // get requested jvmRoute.
+- String requestJvmRoute = null;
+- int index = sessionId.indexOf(".");
+- if (index > 0) {
+- requestJvmRoute = sessionId
+- .substring(index + 1, sessionId.length());
+- }
+- if (requestJvmRoute != null && !requestJvmRoute.equals(localJvmRoute))
{
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("jvmRoute.failover", requestJvmRoute,
+- localJvmRoute, sessionId));
+- }
+- // OK - turnover the session ?
+- String newSessionID = sessionId.substring(0, index) + "."
+- + localJvmRoute;
+- Session catalinaSession = null;
+- try {
+- catalinaSession = getManager(request).findSession(sessionId);
+- } catch (IOException e) {
+- // Hups!
+- }
+- if (catalinaSession != null) {
+- changeSessionID(request, response, sessionId, newSessionID,
+- catalinaSession);
+- numberOfSessions++;
+- } else {
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("jvmRoute.cannotFindSession",
+- sessionId));
+- }
+- }
+- }
+- }
+-
+- /**
+- * change session id and send to all cluster nodes
+- *
+- * @param request current request
+- * @param response current response
+- * @param sessionId
+- * original session id
+- * @param newSessionID
+- * new session id for node migration
+- * @param catalinaSession
+- * current session with original session id
+- */
+- protected void changeSessionID(Request request,
+- Response response, String sessionId, String newSessionID, Session
catalinaSession) {
+- lifecycle.fireLifecycleEvent("Before session migration",
+- catalinaSession);
+- request.setRequestedSessionId(newSessionID);
+- catalinaSession.setId(newSessionID);
+- if (catalinaSession instanceof DeltaSession)
+- ((DeltaSession) catalinaSession).resetDeltaRequest();
+- if(request.isRequestedSessionIdFromCookie()) setNewSessionCookie(request,
response,newSessionID);
+- // set orginal sessionid at request, to allow application detect the
+- // change
+- if (sessionIdAttribute != null &&
!"".equals(sessionIdAttribute)) {
+- if (log.isDebugEnabled()) {
+-
log.debug(sm.getString("jvmRoute.set.orignalsessionid",sessionIdAttribute,sessionId));
+- }
+- request.setAttribute(sessionIdAttribute, sessionId);
+- }
+- // now sending the change to all other clusternode!
+- ClusterManager manager = (ClusterManager)catalinaSession.getManager();
+- sendSessionIDClusterBackup(manager,request,sessionId, newSessionID);
+- lifecycle
+- .fireLifecycleEvent("After session migration",
catalinaSession);
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("jvmRoute.changeSession", sessionId,
+- newSessionID));
+- }
+- }
+-
+- /**
+- * Send the changed Sessionid to all clusternodes.
+- *
+- * @see JvmRouteSessionIDBinderListener#messageReceived(ClusterMessage)
+- * @param manager
+- * ClusterManager
+- * @param sessionId
+- * current failed sessionid
+- * @param newSessionID
+- * new session id, bind to the new cluster node
+- */
+- protected void sendSessionIDClusterBackup(ClusterManager manager,Request
request,String sessionId,
+- String newSessionID) {
+- SessionIDMessage msg = new SessionIDMessage();
+- msg.setOrignalSessionID(sessionId);
+- msg.setBackupSessionID(newSessionID);
+- Context context = request.getContext();
+- msg.setContextPath(context.getPath());
+- msg.setHost(context.getParent().getName());
+- if(manager.doDomainReplication())
+- cluster.sendClusterDomain(msg);
+- else
+- cluster.send(msg);
+- }
+-
+- /**
+- * Sets a new cookie for the given session id and response and see
+- * {@link
org.apache.catalina.connector.Request#configureSessionCookie(javax.servlet.http.Cookie)}
+- *
+- * @param request current request
+- * @param response Tomcat Response
+- * @param sessionId The session id
+- */
+- protected void setNewSessionCookie(Request request,
+- Response response, String sessionId) {
+- if (response != null) {
+- Context context = request.getContext();
+- if (context.getCookies()) {
+- // set a new session cookie
+- Cookie newCookie = new Cookie(Globals.SESSION_COOKIE_NAME,
+- sessionId);
+- newCookie.setMaxAge(-1);
+- String contextPath = null;
+- if (!response.getConnector().getEmptySessionPath()
+- && (context != null)) {
+- contextPath = context.getEncodedPath();
+- }
+- if ((contextPath != null) && (contextPath.length() > 0)) {
+- newCookie.setPath(contextPath);
+- } else {
+- newCookie.setPath("/");
+- }
+- if (request.isSecure()) {
+- newCookie.setSecure(true);
+- }
+- if (log.isDebugEnabled()) {
+- log.debug(sm.getString("jvmRoute.newSessionCookie",
+- sessionId, Globals.SESSION_COOKIE_NAME, newCookie
+- .getPath(), new Boolean(newCookie
+- .getSecure())));
+- }
+- response.addCookie(newCookie);
+- }
+- }
+- }
+-
+- // ------------------------------------------------------ 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 add
+- */
+- 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.
+- *
+- * @exception LifecycleException
+- * if this component detects a fatal error that prevents this
+- * component from being used
+- */
+- public void start() throws LifecycleException {
+-
+- // Validate and update our current component state
+- if (started)
+- throw new LifecycleException(sm
+- .getString("jvmRoute.valve.alreadyStarted"));
+- lifecycle.fireLifecycleEvent(START_EVENT, null);
+- started = true;
+- if (cluster == null) {
+- Container hostContainer = getContainer();
+- // compatibility with JvmRouteBinderValve version 1.1
+- // ( setup at context.xml or context.xml.default )
+- if (!(hostContainer instanceof Host)) {
+- if (log.isWarnEnabled())
+- log.warn(sm.getString("jvmRoute.configure.warn"));
+- hostContainer = hostContainer.getParent();
+- }
+- if (hostContainer instanceof Host
+- && ((Host) hostContainer).getCluster() != null) {
+- cluster = (CatalinaCluster) ((Host) hostContainer).getCluster();
+- } else {
+- Container engine = hostContainer.getParent() ;
+- if (engine instanceof Engine
+- && ((Engine) engine).getCluster() != null) {
+- cluster = (CatalinaCluster) ((Engine) engine).getCluster();
+- }
+- }
+- }
+- if (cluster == null) {
+- throw new RuntimeException("No clustering support at container "
+- + container.getName());
+- }
+-
+- if (log.isInfoEnabled())
+- log.info(sm.getString("jvmRoute.valve.started"));
+-
+- }
+-
+- /**
+- * 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.
+- *
+- * @exception LifecycleException
+- * if this component detects a fatal error that needs to be
+- * reported
+- */
+- public void stop() throws LifecycleException {
+-
+- // Validate and update our current component state
+- if (!started)
+- throw new LifecycleException(sm
+- .getString("jvmRoute.valve.notStarted"));
+- lifecycle.fireLifecycleEvent(STOP_EVENT, null);
+- started = false;
+- cluster = null;
+- numberOfSessions = 0;
+- if (log.isInfoEnabled())
+- log.info(sm.getString("jvmRoute.valve.stopped"));
+-
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/session/DeltaSession.java
+===================================================================
+--- java/org/apache/catalina/ha/session/DeltaSession.java (revision 590752)
++++ java/org/apache/catalina/ha/session/DeltaSession.java (working copy)
+@@ -1,755 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.session;
+-
+-import java.io.Externalizable;
+-import java.io.IOException;
+-import java.io.NotSerializableException;
+-import java.io.ObjectInput;
+-import java.io.ObjectOutput;
+-import java.io.Serializable;
+-import java.security.Principal;
+-import java.util.ArrayList;
+-import java.util.Enumeration;
+-import java.util.HashMap;
+-import java.util.Hashtable;
+-import java.util.concurrent.locks.Lock;
+-import java.util.concurrent.locks.ReentrantReadWriteLock;
+-import javax.servlet.http.HttpSession;
+-import javax.servlet.http.HttpSessionContext;
+-
+-import org.apache.catalina.Manager;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterSession;
+-import org.apache.catalina.realm.GenericPrincipal;
+-import org.apache.catalina.session.StandardSession;
+-import org.apache.catalina.tribes.io.ReplicationStream;
+-import org.apache.catalina.tribes.tipis.ReplicatedMapEntry;
+-import org.apache.catalina.util.Enumerator;
+-import org.apache.catalina.util.StringManager;
+-import org.apache.catalina.session.StandardManager;
+-import org.apache.catalina.session.ManagerBase;
+-import java.util.concurrent.atomic.AtomicInteger;
+-
+-/**
+- *
+- * Similar to the StandardSession except that this session will keep
+- * track of deltas during a request.
+- *
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- */
+-
+-public class DeltaSession extends StandardSession implements
Externalizable,ClusterSession,ReplicatedMapEntry {
+-
+- public static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(DeltaSession.class);
+-
+- /**
+- * The string manager for this package.
+- */
+- protected static StringManager sm = StringManager.getManager(Constants.Package);
+-
+- // ----------------------------------------------------- Instance Variables
+-
+- /**
+- * only the primary session will expire, or be able to expire due to
+- * inactivity. This is set to false as soon as I receive this session over
+- * the wire in a session message. That means that someone else has made a
+- * request on another server.
+- */
+- private transient boolean isPrimarySession = true;
+-
+- /**
+- * The delta request contains all the action info
+- *
+- */
+- private transient DeltaRequest deltaRequest = null;
+-
+- /**
+- * Last time the session was replicatd, used for distributed expiring of
+- * session
+- */
+- private transient long lastTimeReplicated = System.currentTimeMillis();
+-
+-
+- protected Lock diffLock = new ReentrantReadWriteLock().writeLock();
+-
+- private long version;
+-
+- // ----------------------------------------------------------- Constructors
+-
+- /**
+- * Construct a new Session associated with the specified Manager.
+- *
+- * @param manager
+- * The manager with which this Session is associated
+- */
+- public DeltaSession() {
+- this(null);
+- }
+-
+- public DeltaSession(Manager manager) {
+- super(manager);
+- this.resetDeltaRequest();
+- }
+-
+- // ----------------------------------------------------- ReplicatedMapEntry
+-
+- /**
+- * Has the object changed since last replication
+- * and is not in a locked state
+- * @return boolean
+- */
+- public boolean isDirty() {
+- return getDeltaRequest().getSize()>0;
+- }
+-
+- /**
+- * If this returns true, the map will extract the diff using getDiff()
+- * Otherwise it will serialize the entire object.
+- * @return boolean
+- */
+- public boolean isDiffable() {
+- return true;
+- }
+-
+- /**
+- * Returns a diff and sets the dirty map to false
+- * @return byte[]
+- * @throws IOException
+- */
+- public byte[] getDiff() throws IOException {
+- return getDeltaRequest().serialize();
+- }
+-
+- public ClassLoader[] getClassLoaders() {
+- if ( manager instanceof BackupManager ) return
((BackupManager)manager).getClassLoaders();
+- else if ( manager instanceof ClusterManagerBase ) return
((ClusterManagerBase)manager).getClassLoaders();
+- else if ( manager instanceof StandardManager ) {
+- StandardManager sm = (StandardManager)manager;
+- return ClusterManagerBase.getClassLoaders(sm.getContainer());
+- } else if ( manager instanceof ManagerBase ) {
+- ManagerBase mb = (ManagerBase)manager;
+- return ClusterManagerBase.getClassLoaders(mb.getContainer());
+- }//end if
+- return null;
+- }
+-
+- /**
+- * Applies a diff to an existing object.
+- * @param diff byte[]
+- * @param offset int
+- * @param length int
+- * @throws IOException
+- */
+- public void applyDiff(byte[] diff, int offset, int length) throws IOException,
ClassNotFoundException {
+- ReplicationStream stream =
((ClusterManager)getManager()).getReplicationStream(diff,offset,length);
+- getDeltaRequest().readExternal(stream);
+- ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+- try {
+- ClassLoader[] loaders = getClassLoaders();
+- if ( loaders != null && loaders.length >0 )
Thread.currentThread().setContextClassLoader(loaders[0]);
+- getDeltaRequest().execute(this);
+- }finally {
+- Thread.currentThread().setContextClassLoader(contextLoader);
+- }
+- }
+-
+- /**
+- * Resets the current diff state and resets the dirty flag
+- */
+- public void resetDiff() {
+- resetDeltaRequest();
+- }
+-
+- /**
+- * Lock during serialization
+- */
+- public void lock() {
+- diffLock.lock();
+- }
+-
+- /**
+- * Unlock after serialization
+- */
+- public void unlock() {
+- diffLock.unlock();
+- }
+-
+- public void setOwner(Object owner) {
+- if ( owner instanceof ClusterManager && getManager()==null) {
+- ClusterManager cm = (ClusterManager)owner;
+- this.setManager(cm);
+- this.setValid(true);
+- this.setPrimarySession(false);
+- this.access();
+- this.resetDeltaRequest();
+- this.endAccess();
+- }
+- }
+- // ----------------------------------------------------- Session Properties
+-
+- /**
+- * returns true if this session is the primary session, if that is the case,
+- * the manager can expire it upon timeout.
+- */
+- public boolean isPrimarySession() {
+- return isPrimarySession;
+- }
+-
+- /**
+- * Sets whether this is the primary session or not.
+- *
+- * @param primarySession
+- * Flag value
+- */
+- public void setPrimarySession(boolean primarySession) {
+- this.isPrimarySession = primarySession;
+- }
+-
+- /**
+- * Set the session identifier for this session without notify listeners.
+- *
+- * @param id
+- * The new session identifier
+- */
+- public void setIdInternal(String id) {
+- this.id = id;
+- resetDeltaRequest();
+- }
+-
+- /**
+- * Set the session identifier for this session.
+- *
+- * @param id
+- * The new session identifier
+- */
+- public void setId(String id) {
+- super.setId(id);
+- resetDeltaRequest();
+- }
+-
+-
+-
+- /**
+- * Return the last client access time without invalidation check
+- * @see #getLastAccessedTime().
+- */
+- public long getLastAccessedTimeInternal() {
+- return (this.lastAccessedTime);
+- }
+-
+-
+-
+- public void setMaxInactiveInterval(int interval) {
+- this.setMaxInactiveInterval(interval,true);
+- }
+- public void setMaxInactiveInterval(int interval, boolean addDeltaRequest) {
+- super.maxInactiveInterval = interval;
+- if (isValid && interval == 0) {
+- expire();
+- } else {
+- if (addDeltaRequest && (deltaRequest != null))
+- deltaRequest.setMaxInactiveInterval(interval);
+- }
+- }
+-
+- /**
+- * Set the <code>isNew</code> flag for this session.
+- *
+- * @param isNew
+- * The new value for the <code>isNew</code> flag
+- */
+- public void setNew(boolean isNew) {
+- setNew(isNew, true);
+- }
+-
+- public void setNew(boolean isNew, boolean addDeltaRequest) {
+- super.setNew(isNew);
+- if (addDeltaRequest && (deltaRequest != null))
+- deltaRequest.setNew(isNew);
+- }
+-
+- /**
+- * 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
+- */
+- public void setPrincipal(Principal principal) {
+- setPrincipal(principal, true);
+- }
+-
+- public void setPrincipal(Principal principal, boolean addDeltaRequest) {
+- try {
+- lock();
+- super.setPrincipal(principal);
+- if (addDeltaRequest && (deltaRequest != null))
+- deltaRequest.setPrincipal(principal);
+- } finally {
+- unlock();
+- }
+- }
+-
+- /**
+- * Return the <code>isValid</code> flag for this session.
+- */
+- public boolean isValid() {
+- if (this.expiring) {
+- return true;
+- }
+- if (!this.isValid) {
+- return false;
+- }
+- if (ACTIVITY_CHECK && accessCount.get() > 0) {
+- return true;
+- }
+- if (maxInactiveInterval >= 0) {
+- long timeNow = System.currentTimeMillis();
+- int timeIdle = (int) ( (timeNow - thisAccessedTime) / 1000L);
+- if (isPrimarySession()) {
+- if (timeIdle >= maxInactiveInterval) {
+- expire(true);
+- }
+- } else {
+- if (timeIdle >= (2 * maxInactiveInterval)) {
+- //if the session has been idle twice as long as allowed,
+- //the primary session has probably crashed, and no other
+- //requests are coming in. that is why we do this. otherwise
+- //we would have a memory leak
+- expire(true, false);
+- }
+- }
+- }
+- return (this.isValid);
+- }
+-
+- // ------------------------------------------------- Session Public Methods
+-
+- /**
+- * Perform the internal processing required to invalidate this session,
+- * without triggering an exception if the session has already expired.
+- *
+- * @param notify
+- * Should we notify listeners about the demise of this session?
+- */
+- public void expire(boolean notify) {
+- expire(notify, true);
+- }
+-
+- public void expire(boolean notify, boolean notifyCluster) {
+- String expiredId = getIdInternal();
+- super.expire(notify);
+-
+- if (notifyCluster) {
+- if (log.isDebugEnabled())
+- log.debug(sm.getString("deltaSession.notifying",
+- ((ClusterManager)manager).getName(),
+- new Boolean(isPrimarySession()),
+- expiredId));
+- if ( manager instanceof DeltaManager ) {
+- ( (DeltaManager) manager).sessionExpired(expiredId);
+- }
+- }
+- }
+-
+- /**
+- * Release all object references, and initialize instance variables, in
+- * preparation for reuse of this object.
+- */
+- public void recycle() {
+- super.recycle();
+- deltaRequest.clear();
+- }
+-
+-
+- /**
+- * Return a string representation of this object.
+- */
+- public String toString() {
+- StringBuffer sb = new StringBuffer();
+- sb.append("DeltaSession[");
+- sb.append(id);
+- sb.append("]");
+- return (sb.toString());
+- }
+-
+- // ------------------------------------------------ Session Package Methods
+-
+- public synchronized void readExternal(ObjectInput in) throws
IOException,ClassNotFoundException {
+- readObjectData(in);
+- }
+-
+-
+- /**
+- * Read a serialized version of the contents of this session object from the
+- * specified object input stream, without requiring that the StandardSession
+- * itself have been serialized.
+- *
+- * @param stream
+- * The object input stream to read from
+- *
+- * @exception ClassNotFoundException
+- * if an unknown class is specified
+- * @exception IOException
+- * if an input/output error occurs
+- */
+- public void readObjectData(ObjectInput stream) throws ClassNotFoundException,
IOException {
+- readObject(stream);
+- }
+-
+- /**
+- * Write a serialized version of the contents of this session object to the
+- * specified object output stream, without requiring that the
+- * StandardSession itself have been serialized.
+- *
+- * @param stream
+- * The object output stream to write to
+- *
+- * @exception IOException
+- * if an input/output error occurs
+- */
+- public void writeObjectData(ObjectOutput stream) throws IOException {
+- writeObject(stream);
+- }
+-
+- public void resetDeltaRequest() {
+- if (deltaRequest == null) {
+- deltaRequest = new DeltaRequest(getIdInternal(), false);
+- } else {
+- deltaRequest.reset();
+- deltaRequest.setSessionId(getIdInternal());
+- }
+- }
+-
+- public DeltaRequest getDeltaRequest() {
+- if (deltaRequest == null) resetDeltaRequest();
+- return deltaRequest;
+- }
+-
+- // ------------------------------------------------- HttpSession Properties
+-
+- // ----------------------------------------------HttpSession Public Methods
+-
+-
+-
+- /**
+- * 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
+- * Should we notify interested listeners that this attribute is
+- * being removed?
+- *
+- * @exception IllegalStateException
+- * if this method is called on an invalidated session
+- */
+- public void removeAttribute(String name, boolean notify) {
+- removeAttribute(name, notify, true);
+- }
+-
+- public void removeAttribute(String name, boolean notify,boolean addDeltaRequest) {
+- // Validate our current state
+- if (!isValid()) throw new
IllegalStateException(sm.getString("standardSession.removeAttribute.ise"));
+- removeAttributeInternal(name, notify, addDeltaRequest);
+- }
+-
+- /**
+- * 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
+- *
+- * @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
+- */
+- public void setAttribute(String name, Object value) {
+- setAttribute(name, value, true, true);
+- }
+-
+- public void setAttribute(String name, Object value, boolean notify,boolean
addDeltaRequest) {
+-
+- // Name cannot be null
+- if (name == null) throw new
IllegalArgumentException(sm.getString("standardSession.setAttribute.namenull"));
+-
+- // Null value is the same as removeAttribute()
+- if (value == null) {
+- removeAttribute(name);
+- return;
+- }
+-
+- try {
+- lock();
+- super.setAttribute(name,value, notify);
+- if (addDeltaRequest && (deltaRequest != null))
deltaRequest.setAttribute(name, value);
+- } finally {
+- unlock();
+- }
+- }
+-
+- // -------------------------------------------- HttpSession Private Methods
+-
+- /**
+- * Read a serialized version of this session object from the specified
+- * object input stream.
+- * <p>
+- * <b>IMPLEMENTATION NOTE </b>: The reference to the owning Manager is
not
+- * restored by this method, and must be set explicitly.
+- *
+- * @param stream
+- * The input stream to read from
+- *
+- * @exception ClassNotFoundException
+- * if an unknown class is specified
+- * @exception IOException
+- * if an input/output error occurs
+- */
+- private void readObject(ObjectInput stream) throws ClassNotFoundException,
IOException {
+-
+- // Deserialize the scalar instance variables (except Manager)
+- authType = null; // Transient only
+- creationTime = ( (Long) stream.readObject()).longValue();
+- lastAccessedTime = ( (Long) stream.readObject()).longValue();
+- maxInactiveInterval = ( (Integer) stream.readObject()).intValue();
+- isNew = ( (Boolean) stream.readObject()).booleanValue();
+- isValid = ( (Boolean) stream.readObject()).booleanValue();
+- thisAccessedTime = ( (Long) stream.readObject()).longValue();
+- version = ( (Long) stream.readObject()).longValue();
+- boolean hasPrincipal = stream.readBoolean();
+- principal = null;
+- if (hasPrincipal) {
+- principal =
SerializablePrincipal.readPrincipal(stream,getManager().getContainer().getRealm());
+- }
+-
+- // setId((String) stream.readObject());
+- id = (String) stream.readObject();
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaSession.readSession", id));
+-
+- // Deserialize the attribute count and attribute values
+- if (attributes == null) attributes = new Hashtable();
+- int n = ( (Integer) stream.readObject()).intValue();
+- boolean isValidSave = isValid;
+- isValid = true;
+- for (int i = 0; i < n; i++) {
+- String name = (String) stream.readObject();
+- Object value = (Object) stream.readObject();
+- if ( (value instanceof String) && (value.equals(NOT_SERIALIZED)))
+- continue;
+- attributes.put(name, value);
+- }
+- isValid = isValidSave;
+-
+- if (listeners == null) {
+- listeners = new ArrayList();
+- }
+-
+- if (notes == null) {
+- notes = new Hashtable();
+- }
+- activate();
+- }
+-
+- public synchronized void writeExternal(ObjectOutput out ) throws java.io.IOException
{
+- writeObject(out);
+- }
+-
+-
+- /**
+- * Write a serialized version of this session object to the specified object
+- * output stream.
+- * <p>
+- * <b>IMPLEMENTATION NOTE </b>: The owning Manager will not be stored in
the
+- * serialized representation of this Session. After calling
+- * <code>readObject()</code>, you must set the associated Manager
+- * explicitly.
+- * <p>
+- * <b>IMPLEMENTATION NOTE </b>: Any attribute that is not Serializable
will
+- * be unbound from the session, with appropriate actions if it implements
+- * HttpSessionBindingListener. If you do not want any such attributes, be
+- * sure the <code>distributable</code> property of the associated
Manager
+- * is set to <code>true</code>.
+- *
+- * @param stream
+- * The output stream to write to
+- *
+- * @exception IOException
+- * if an input/output error occurs
+- */
+- private void writeObject(ObjectOutput stream) throws IOException {
+- // Write the scalar instance variables (except Manager)
+- stream.writeObject(new Long(creationTime));
+- stream.writeObject(new Long(lastAccessedTime));
+- stream.writeObject(new Integer(maxInactiveInterval));
+- stream.writeObject(new Boolean(isNew));
+- stream.writeObject(new Boolean(isValid));
+- stream.writeObject(new Long(thisAccessedTime));
+- stream.writeObject(new Long(version));
+- stream.writeBoolean(getPrincipal() != null);
+- if (getPrincipal() != null) {
+- SerializablePrincipal.writePrincipal((GenericPrincipal) principal,stream);
+- }
+-
+- stream.writeObject(id);
+- if (log.isDebugEnabled())
log.debug(sm.getString("deltaSession.writeSession", id));
+-
+- // Accumulate the names of serializable and non-serializable attributes
+- String keys[] = keys();
+- ArrayList saveNames = new ArrayList();
+- ArrayList saveValues = new ArrayList();
+- for (int i = 0; i < keys.length; i++) {
+- Object value = null;
+- value = attributes.get(keys[i]);
+- if (value == null)
+- continue;
+- else if (value instanceof Serializable) {
+- saveNames.add(keys[i]);
+- saveValues.add(value);
+- }
+- }
+-
+- // Serialize the attribute count and the Serializable attributes
+- int n = saveNames.size();
+- stream.writeObject(new Integer(n));
+- for (int i = 0; i < n; i++) {
+- stream.writeObject( (String) saveNames.get(i));
+- try {
+- stream.writeObject(saveValues.get(i));
+- } catch (NotSerializableException e) {
+-
log.error(sm.getString("standardSession.notSerializable",saveNames.get(i), id),
e);
+- stream.writeObject(NOT_SERIALIZED);
+- log.error(" storing attribute '" + saveNames.get(i)+
"' with value NOT_SERIALIZED");
+- }
+- }
+-
+- }
+-
+- // -------------------------------------------------------- Private Methods
+-
+-
+-
+- /**
+- * Return the value of an attribute without a check for validity.
+- */
+- protected Object getAttributeInternal(String name) {
+- return (attributes.get(name));
+- }
+-
+- protected void removeAttributeInternal(String name, boolean notify,
+- boolean addDeltaRequest) {
+- try {
+- lock();
+- // Remove this attribute from our collection
+- Object value = attributes.get(name);
+- if (value == null) return;
+-
+- super.removeAttributeInternal(name,notify);
+- if (addDeltaRequest && (deltaRequest != null))
deltaRequest.removeAttribute(name);
+-
+- }finally {
+- unlock();
+- }
+- }
+-
+- protected long getLastTimeReplicated() {
+- return lastTimeReplicated;
+- }
+-
+- public long getVersion() {
+- return version;
+- }
+-
+- protected void setLastTimeReplicated(long lastTimeReplicated) {
+- this.lastTimeReplicated = lastTimeReplicated;
+- }
+-
+- public void setVersion(long version) {
+- this.version = version;
+- }
+-
+- protected void setAccessCount(int count) {
+- if ( accessCount == null && ACTIVITY_CHECK ) accessCount = new
AtomicInteger();
+- if ( accessCount != null ) super.accessCount.set(count);
+- }
+-}
+-
+-// -------------------------------------------------------------- Private Class
+-
+-/**
+- * This class is a dummy implementation of the
<code>HttpSessionContext</code>
+- * interface, to conform to the requirement that such an object be returned when
+- * <code>HttpSession.getSessionContext()</code> is called.
+- *
+- * @author Craig R. McClanahan
+- *
+- * @deprecated As of Java Servlet API 2.1 with no replacement. The interface
+- * will be removed in a future version of this API.
+- */
+-
+-final class StandardSessionContext
+- implements HttpSessionContext {
+-
+- private HashMap dummy = new HashMap();
+-
+- /**
+- * Return the session identifiers of all sessions defined within this
+- * context.
+- *
+- * @deprecated As of Java Servlet API 2.1 with no replacement. This method
+- * must return an empty <code>Enumeration</code> and will
be
+- * removed in a future version of the API.
+- */
+- public Enumeration getIds() {
+- return (new Enumerator(dummy));
+- }
+-
+- /**
+- * Return the <code>HttpSession</code> associated with the specified
+- * session identifier.
+- *
+- * @param id
+- * Session identifier for which to look up a session
+- *
+- * @deprecated As of Java Servlet API 2.1 with no replacement. This method
+- * must return null and will be removed in a future version of
+- * the API.
+- */
+- public HttpSession getSession(String id) {
+- return (null);
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/context/ReplicatedContext.java
+===================================================================
+--- java/org/apache/catalina/ha/context/ReplicatedContext.java (revision 590752)
++++ java/org/apache/catalina/ha/context/ReplicatedContext.java (working copy)
+@@ -1,204 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha.context;
+-
+-import org.apache.catalina.core.StandardContext;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.tribes.tipis.ReplicatedMap;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.Loader;
+-import org.apache.catalina.core.ApplicationContext;
+-import org.apache.catalina.Globals;
+-import javax.servlet.ServletContext;
+-import java.util.AbstractMap;
+-import org.apache.catalina.tribes.tipis.AbstractReplicatedMap;
+-import java.util.ArrayList;
+-import java.util.Iterator;
+-import javax.servlet.ServletContextAttributeListener;
+-import javax.servlet.ServletContextAttributeEvent;
+-import org.apache.catalina.LifecycleEvent;
+-import org.apache.catalina.LifecycleListener;
+-import java.util.Enumeration;
+-import java.util.concurrent.ConcurrentHashMap;
+-import org.apache.catalina.util.Enumerator;
+-import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
+-import org.apache.catalina.ha.session.DeltaSession;
+-
+-/**
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class ReplicatedContext extends StandardContext implements
LifecycleListener,MapOwner {
+- private int mapSendOptions = Channel.SEND_OPTIONS_DEFAULT;
+- public static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( ReplicatedContext.class );
+- protected boolean startComplete = false;
+- protected static long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
+-
+- public void lifecycleEvent(LifecycleEvent event) {
+- if ( event.getType() == AFTER_START_EVENT )
+- startComplete = true;
+- }
+-
+- public synchronized void start() throws LifecycleException {
+- if ( this.started ) return;
+- super.addLifecycleListener(this);
+- try {
+- CatalinaCluster catclust = (CatalinaCluster)this.getCluster();
+- if (this.context == null) this.context = new
ReplApplContext(this.getBasePath(), this);
+- if ( catclust != null ) {
+- ReplicatedMap map = new
ReplicatedMap(this,catclust.getChannel(),DEFAULT_REPL_TIMEOUT,
+- getName(),getClassLoaders());
+- map.setChannelSendOptions(mapSendOptions);
+- ((ReplApplContext)this.context).setAttributeMap(map);
+- if (getAltDDName() != null) context.setAttribute(Globals.ALT_DD_ATTR,
getAltDDName());
+- }
+- super.start();
+- } catch ( Exception x ) {
+- log.error("Unable to start ReplicatedContext",x);
+- throw new LifecycleException("Failed to start
ReplicatedContext",x);
+- }
+- }
+-
+- public synchronized void stop() throws LifecycleException
+- {
+- ReplicatedMap map =
(ReplicatedMap)((ReplApplContext)this.context).getAttributeMap();
+- if ( map!=null ) {
+- map.breakdown();
+- }
+- if ( !this.started ) return;
+- try {
+- super.lifecycle.removeLifecycleListener(this);
+- } catch ( Exception x ){
+- log.error("Unable to stop ReplicatedContext",x);
+- throw new LifecycleException("Failed to stop
ReplicatedContext",x);
+- } finally {
+- this.startComplete = false;
+- super.stop();
+- }
+-
+-
+- }
+-
+-
+- public void setMapSendOptions(int mapSendOptions) {
+- this.mapSendOptions = mapSendOptions;
+- }
+-
+- public int getMapSendOptions() {
+- return mapSendOptions;
+- }
+-
+- public ClassLoader[] getClassLoaders() {
+- Loader loader = null;
+- ClassLoader classLoader = null;
+- loader = this.getLoader();
+- if (loader != null) classLoader = loader.getClassLoader();
+- if ( classLoader == null ) classLoader =
Thread.currentThread().getContextClassLoader();
+- if ( classLoader == Thread.currentThread().getContextClassLoader() ) {
+- return new ClassLoader[] {classLoader};
+- } else {
+- return new ClassLoader[]
{classLoader,Thread.currentThread().getContextClassLoader()};
+- }
+- }
+-
+- public ServletContext getServletContext() {
+- if (context == null) {
+- context = new ReplApplContext(getBasePath(), this);
+- if (getAltDDName() != null)
+- context.setAttribute(Globals.ALT_DD_ATTR,getAltDDName());
+- }
+-
+- return ((ReplApplContext)context).getFacade();
+-
+- }
+-
+-
+- protected static class ReplApplContext extends ApplicationContext {
+- protected ConcurrentHashMap tomcatAttributes = new ConcurrentHashMap();
+-
+- public ReplApplContext(String basePath, ReplicatedContext context) {
+- super(basePath,context);
+- }
+-
+- protected ReplicatedContext getParent() {
+- return (ReplicatedContext)getContext();
+- }
+-
+- protected ServletContext getFacade() {
+- return super.getFacade();
+- }
+-
+- public AbstractMap getAttributeMap() {
+- return (AbstractMap)this.attributes;
+- }
+- public void setAttributeMap(AbstractMap map) {
+- this.attributes = map;
+- }
+-
+- public void removeAttribute(String name) {
+- tomcatAttributes.remove(name);
+- //do nothing
+- super.removeAttribute(name);
+- }
+-
+- public void setAttribute(String name, Object value) {
+- if ( (!getParent().startComplete) ||
"org.apache.jasper.runtime.JspApplicationContextImpl".equals(name) ){
+- tomcatAttributes.put(name,value);
+- } else
+- super.setAttribute(name,value);
+- }
+-
+- public Object getAttribute(String name) {
+- if (tomcatAttributes.containsKey(name) )
+- return tomcatAttributes.get(name);
+- else
+- return super.getAttribute(name);
+- }
+-
+- public Enumeration getAttributeNames() {
+- return new MultiEnumeration(new Enumeration[] {super.getAttributeNames(),new
Enumerator(tomcatAttributes.keySet(), true)});
+- }
+-
+- }
+-
+- protected static class MultiEnumeration implements Enumeration {
+- Enumeration[] e=null;
+- public MultiEnumeration(Enumeration[] lists) {
+- e = lists;
+- }
+- public boolean hasMoreElements() {
+- for ( int i=0; i<e.length; i++ ) {
+- if ( e[i].hasMoreElements() ) return true;
+- }
+- return false;
+- }
+- public Object nextElement() {
+- for ( int i=0; i<e.length; i++ ) {
+- if ( e[i].hasMoreElements() ) return e[i].nextElement();
+- }
+- return null;
+-
+- }
+- }
+-
+- public void objectMadePrimay(Object key, Object value) {
+- //noop
+- }
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/ha/tcp/LocalStrings.properties
+===================================================================
+--- java/org/apache/catalina/ha/tcp/LocalStrings.properties (revision 590752)
++++ java/org/apache/catalina/ha/tcp/LocalStrings.properties (working copy)
+@@ -1,87 +0,0 @@
+-# Licensed to the Apache Software Foundation (ASF) under one or more
+-# contributor license agreements. See the NOTICE file distributed with
+-# this work for additional information regarding copyright ownership.
+-# The ASF licenses this file to You under the Apache License, Version 2.0
+-# (the "License"); you may not use this file except in compliance with
+-# the License. You may obtain a copy of the License at
+-#
+-#
http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS,
+-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-# See the License for the specific language governing permissions and
+-# limitations under the License.
+-
+-AsyncSocketSender.create.thread=Create sender [{0}:{1,number,integer}] queue thread to
tcp background replication
+-AsyncSocketSender.queue.message=Queue message to [{0}:{1,number,integer}] id=[{2}]
size={3}
+-AsyncSocketSender.send.error=Unable to asynchronously send session with id=[{0}] -
message will be ignored.
+-AsyncSocketSender.queue.empty=Queue in sender [{0}:{1,number,integer}] returned null
element!
+-cluster.mbean.register.already=MBean {0} already registered!
+-FastAsyncSocketSender.setThreadPriority=[{0}:{1,number,integer}] set priority to {2}
+-FastAsyncSocketSender.min.exception=[{0}:{1,number,integer}] new priority {2} <
MIN_PRIORITY
+-FastAsyncSocketSender.max.exception=[{0}:{1,number,integer}] new priority {2} >
MAX_PRIORITY
+-IDataSender.ack.eof=EOF reached at local port [{0}:{1,number,integer}]
+-IDataSender.ack.receive=Got ACK at local port [{0}:{1,number,integer}]
+-IDataSender.ack.missing=Unable to read acknowledgement from [{0}:{1,number,integer}] in
{2,number,integer} ms. Disconnecting socket, and trying again.
+-IDataSender.ack.read=Read wait ack char '{2}' [{0}:{1,number,integer}]
+-IDataSender.ack.start=Waiting for ACK message [{0}:{1,number,integer}]
+-IDataSender.ack.wrong=Missing correct ACK after 10 bytes read at local port
[{0}:{1,number,integer}]
+-IDataSender.closeSocket=Sender close socket to [{0}:{1,number,integer}] (close count
{2,number,integer})
+-IDataSender.connect=Sender connect to [{0}:{1,number,integer}] (connect count
{2,number,integer})
+-IDataSender.create=Create sender [{0}:{1,number,integer}]
+-IDataSender.disconnect=Sender disconnect from [{0}:{1,number,integer}] (disconnect count
{2,number,integer})
+-IDataSender.message.disconnect=Message transfered: Sender can't disconnect from
[{0}:{1,number,integer}]
+-IDataSender.message.create=Message transfered: Sender can't create current socket
[{0}:{1,number,integer}]
+-IDataSender.openSocket=Sender open socket to [{0}:{1,number,integer}] (open count
{2,number,integer})
+-IDataSender.openSocket.failure=Open sender socket [{0}:{1,number,integer}] failure!
(open failure count {2,number,integer})
+-IDataSender.send.again=Send data again to [{0}:{1,number,integer}]
+-IDataSender.send.crash=Send message crashed [{0}:{1,number,integer}] type=[{2}],
id=[{3}]
+-IDataSender.send.message=Send message to [{0}:{1,number,integer}] id=[{2}]
size={3,number,integer}
+-IDataSender.send.lost=Message lost: [{0}:{1,number,integer}] type=[{2}], id=[{3}]
+-IDataSender.senderModes.Configured=Configured a data replication sender for mode {0}
+-IDataSender.senderModes.Instantiate=Can't instantiate a data replication sender of
class {0}
+-IDataSender.senderModes.Missing=Can't configure a data replication sender for mode
{0}
+-IDataSender.senderModes.Resources=Can't load data replication sender mapping list
+-IDataSender.stats=Send stats from [{0}:{1,number,integer}], Nr of bytes
sent={2,number,integer} over {3} = {4,number,integer} bytes/request, processing time
{5,number,integer} msec, avg processing time {6,number,integer} msec
+-PoolSocketSender.senderQueue.sender.failed=PoolSocketSender create new sender to
[{0}:{1,number,integer}] failed
+-PoolSocketSender.noMoreSender=No socket sender available for client
[{0}:{1,number,integer}] did it disappeared?
+-ReplicationTransmitter.getProperty=get property {0}
+-ReplicationTransmitter.setProperty=set property {0}: {1} old value {2}
+-ReplicationTransmitter.started=Start ClusterSender at cluster {0} with name {1}
+-ReplicationTransmitter.stopped=Stopped ClusterSender at cluster {0} with name {1}
+-ReplicationValve.crossContext.add=add Cross Context session replication container to
replicationValve threadlocal
+-ReplicationValve.crossContext.registerSession=register Cross context session id={0} from
context {1}
+-ReplicationValve.crossContext.remove=remove Cross Context session replication container
from replicationValve threadlocal
+-ReplicationValve.crossContext.sendDelta=send Cross Context session delta from context
{0}.
+-ReplicationValve.filter.loading=Loading request filters={0}
+-ReplicationValve.filter.token=Request filter={0}
+-ReplicationValve.filter.token.failure=Unable to compile filter={0}
+-ReplicationValve.invoke.uri=Invoking replication request on {0}
+-ReplicationValve.nocluster=No cluster configured for this request.
+-ReplicationValve.resetDeltaRequest=Cluster is standalone: reset Session Request Delta at
context {0}
+-ReplicationValve.send.failure=Unable to perform replication request.
+-ReplicationValve.send.invalid.failure=Unable to send session [id={0}] invalid message
over cluster.
+-ReplicationValve.session.found=Context {0}: Found session {1} but it isn't a
ClusterSession.
+-ReplicationValve.session.indicator=Context {0}: Primarity of session {0} in request
attribute {1} is {2}.
+-ReplicationValve.session.invalid=Context {0}: Requested session {1} is invalid, removed
or not replicated at this node.
+-ReplicationValve.stats=Average request time= {0} ms for Cluster overhead time={1} ms for
{2} requests {3} filter requests {4} send requests {5} cross context requests (Request={6}
ms Cluster={7} ms).
+-SimpleTcpCluster.event.log=Cluster receive listener event {0} with data {1}
+-SimpleTcpCluster.getProperty=get property {0}
+-SimpleTcpCluster.setProperty=set property {0}: {1} old value {2}
+-SimpleTcpCluster.default.addClusterListener=Add Default ClusterListener at cluster {0}
+-SimpleTcpCluster.default.addClusterValves=Add Default ClusterValves at cluster {0}
+-SimpleTcpCluster.default.addClusterReceiver=Add Default ClusterReceiver at cluster {0}
+-SimpleTcpCluster.default.addClusterSender=Add Default ClusterSender at cluster {0}
+-SimpleTcpCluster.default.addMembershipService=Add Default Membership Service at cluster
{0}
+-SimpleTcpCluster.log.receive=RECEIVE {0,date}:{0,time} {1,number} {2}:{3,number,integer}
{4} {5}
+-SimpleTcpCluster.log.send=SEND {0,date}:{0,time} {1,number} {2}:{3,number,integer} {4}
+-SimpleTcpCluster.log.send.all=SEND {0,date}:{0,time} {1,number} - {2}
+-SocketReplictionListener.allreadyExists=ServerSocket [{0}:{1}] allready started!
+-SocketReplictionListener.accept.failure=ServerSocket [{0}:{1}] - Exception to start
thread or accept server socket
+-SocketReplictionListener.open=Open Socket at [{0}:{1}]
+-SocketReplictionListener.openclose.failure=ServerSocket [{0}:{1}] - Exception to open or
close server socket
+-SocketReplictionListener.portbusy=Port busy at [{0}:{i}] - reason [{2}]
+-SocketReplictionListener.serverSocket.notExists=Fatal error: Receiver socket not bound
address={0} port={1} maxport={2}
+-SocketReplictionListener.timeout=Receiver ServerSocket no started [{0}:{1}] - reason:
timeout={2} or listen={3}
+-SocketReplictionListener.unlockSocket.failure=UnLocksocket failure at ServerSocket
[{0:{1}]
+Index: java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java
+===================================================================
+--- java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java (revision 590752)
++++ java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java (working copy)
+@@ -1,941 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.tcp;
+-
+-import java.beans.PropertyChangeSupport;
+-import java.io.Serializable;
+-import java.util.ArrayList;
+-import java.util.HashMap;
+-import java.util.Iterator;
+-import java.util.List;
+-import java.util.Map;
+-
+-import org.apache.catalina.Container;
+-import org.apache.catalina.Context;
+-import org.apache.catalina.Engine;
+-import org.apache.catalina.Host;
+-import org.apache.catalina.Lifecycle;
+-import org.apache.catalina.LifecycleEvent;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.LifecycleListener;
+-import org.apache.catalina.Manager;
+-import org.apache.catalina.Valve;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterListener;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.ClusterValve;
+-import org.apache.catalina.ha.session.DeltaManager;
+-import org.apache.catalina.ha.util.IDynamicProperty;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import org.apache.catalina.tribes.group.GroupChannel;
+-import org.apache.catalina.util.LifecycleSupport;
+-import org.apache.catalina.util.StringManager;
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-import org.apache.tomcat.util.IntrospectionUtils;
+-import org.apache.catalina.ha.session.ClusterSessionListener;
+-import org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor;
+-import org.apache.catalina.tribes.group.interceptors.TcpFailureDetector;
+-import org.apache.catalina.ha.session.JvmRouteBinderValve;
+-import org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener;
+-
+-/**
+- * A <b>Cluster </b> implementation using simple multicast. Responsible for
+- * setting up a cluster and provides callers with a valid multicast
+- * receiver/sender.
+- *
+- * FIXME remove install/remove/start/stop context dummys
+- * FIXME wrote testcases
+- *
+- * @author Filip Hanik
+- * @author Remy Maucherat
+- * @author Peter Rossbach
+- * @version $Revision$, $Date$
+- */
+-public class SimpleTcpCluster
+- implements CatalinaCluster, Lifecycle, LifecycleListener, IDynamicProperty,
+- MembershipListener, ChannelListener{
+-
+- public static Log log = LogFactory.getLog(SimpleTcpCluster.class);
+-
+- // ----------------------------------------------------- Instance Variables
+-
+- /**
+- * Descriptive information about this component implementation.
+- */
+- protected static final String info = "SimpleTcpCluster/2.2";
+-
+- public static final String BEFORE_MEMBERREGISTER_EVENT =
"before_member_register";
+-
+- public static final String AFTER_MEMBERREGISTER_EVENT =
"after_member_register";
+-
+- public static final String BEFORE_MANAGERREGISTER_EVENT =
"before_manager_register";
+-
+- public static final String AFTER_MANAGERREGISTER_EVENT =
"after_manager_register";
+-
+- public static final String BEFORE_MANAGERUNREGISTER_EVENT =
"before_manager_unregister";
+-
+- public static final String AFTER_MANAGERUNREGISTER_EVENT =
"after_manager_unregister";
+-
+- public static final String BEFORE_MEMBERUNREGISTER_EVENT =
"before_member_unregister";
+-
+- public static final String AFTER_MEMBERUNREGISTER_EVENT =
"after_member_unregister";
+-
+- public static final String SEND_MESSAGE_FAILURE_EVENT =
"send_message_failure";
+-
+- public static final String RECEIVE_MESSAGE_FAILURE_EVENT =
"receive_message_failure";
+-
+- /**
+- * Group channel.
+- */
+- protected Channel channel = new GroupChannel();
+-
+-
+- /**
+- * Name for logging purpose
+- */
+- protected String clusterImpName = "SimpleTcpCluster";
+-
+- /**
+- * The string manager for this package.
+- */
+- protected StringManager sm = StringManager.getManager(Constants.Package);
+-
+- /**
+- * The cluster name to join
+- */
+- protected String clusterName ;
+-
+- /**
+- * call Channel.heartbeat() at container background thread
+- * @see org.apache.catalina.tribes.group.GroupChannel#heartbeat()
+- */
+- protected boolean heartbeatBackgroundEnabled =false ;
+-
+- /**
+- * The Container associated with this Cluster.
+- */
+- protected Container container = null;
+-
+- /**
+- * The lifecycle event support for this component.
+- */
+- protected LifecycleSupport lifecycle = new LifecycleSupport(this);
+-
+- /**
+- * Has this component been started?
+- */
+- protected boolean started = false;
+-
+- /**
+- * The property change support for this component.
+- */
+- protected PropertyChangeSupport support = new PropertyChangeSupport(this);
+-
+- /**
+- * The context name <->manager association for distributed contexts.
+- */
+- protected Map managers = new HashMap();
+-
+- protected ClusterManager managerTemplate = new DeltaManager();
+-
+- private List valves = new ArrayList();
+-
+- private org.apache.catalina.ha.ClusterDeployer clusterDeployer;
+-
+- /**
+- * Listeners of messages
+- */
+- protected List clusterListeners = new ArrayList();
+-
+- /**
+- * Comment for <code>notifyLifecycleListenerOnFailure</code>
+- */
+- private boolean notifyLifecycleListenerOnFailure = false;
+-
+- /**
+- * dynamic sender <code>properties</code>
+- */
+- private Map properties = new HashMap();
+-
+- private int channelSendOptions = Channel.SEND_OPTIONS_ASYNCHRONOUS;
+-
+- // ------------------------------------------------------------- Properties
+-
+- public SimpleTcpCluster() {
+- }
+-
+- /**
+- * Return descriptive information about this Cluster implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+- return (info);
+- }
+-
+- /**
+- * Return heartbeat enable flag (default false)
+- * @return the heartbeatBackgroundEnabled
+- */
+- public boolean isHeartbeatBackgroundEnabled() {
+- return heartbeatBackgroundEnabled;
+- }
+-
+- /**
+- * enabled that container backgroundThread call heartbeat at channel
+- * @param heartbeatBackgroundEnabled the heartbeatBackgroundEnabled to set
+- */
+- public void setHeartbeatBackgroundEnabled(boolean heartbeatBackgroundEnabled) {
+- this.heartbeatBackgroundEnabled = heartbeatBackgroundEnabled;
+- }
+-
+- /**
+- * 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) {
+- this.clusterName = 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() {
+- if(clusterName == null && container != null)
+- return container.getName() ;
+- return 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);
+- }
+-
+- /**
+- * @return Returns the notifyLifecycleListenerOnFailure.
+- */
+- public boolean isNotifyLifecycleListenerOnFailure() {
+- return notifyLifecycleListenerOnFailure;
+- }
+-
+- /**
+- * @param notifyListenerOnFailure
+- * The notifyLifecycleListenerOnFailure to set.
+- */
+- public void setNotifyLifecycleListenerOnFailure(
+- boolean notifyListenerOnFailure) {
+- boolean oldNotifyListenerOnFailure = this.notifyLifecycleListenerOnFailure;
+- this.notifyLifecycleListenerOnFailure = notifyListenerOnFailure;
+- support.firePropertyChange("notifyLifecycleListenerOnFailure",
+- oldNotifyListenerOnFailure,
+- this.notifyLifecycleListenerOnFailure);
+- }
+-
+- /**
+- * @deprecated use getManagerTemplate().getClass().getName() instead.
+- * @return String
+- */
+- public String getManagerClassName() {
+- return managerTemplate.getClass().getName();
+- }
+-
+- /**
+- * @deprecated use nested <Manager> element inside the cluster config
instead.
+- * @param managerClassName String
+- */
+- public void setManagerClassName(String managerClassName) {
+- log.warn("setManagerClassName is deprecated, use nested <Manager>
element inside the <Cluster> element instead, this request will be ignored.");
+- }
+-
+- /**
+- * Add cluster valve
+- * Cluster Valves are only add to container when cluster is started!
+- * @param valve The new cluster Valve.
+- */
+- public void addValve(Valve valve) {
+- if (valve instanceof ClusterValve && (!valves.contains(valve)))
+- valves.add(valve);
+- }
+-
+- /**
+- * get all cluster valves
+- * @return current cluster valves
+- */
+- public Valve[] getValves() {
+- return (Valve[]) valves.toArray(new Valve[valves.size()]);
+- }
+-
+- /**
+- * Get the cluster listeners associated with this cluster. If this Array has
+- * no listeners registered, a zero-length array is returned.
+- */
+- public ClusterListener[] findClusterListeners() {
+- if (clusterListeners.size() > 0) {
+- ClusterListener[] listener = new ClusterListener[clusterListeners.size()];
+- clusterListeners.toArray(listener);
+- return listener;
+- } else
+- return new ClusterListener[0];
+-
+- }
+-
+- /**
+- * add cluster message listener and register cluster to this listener
+- *
+- * @see
org.apache.catalina.ha.CatalinaCluster#addClusterListener(org.apache.catalina.ha.MessageListener)
+- */
+- public void addClusterListener(ClusterListener listener) {
+- if (listener != null && !clusterListeners.contains(listener)) {
+- clusterListeners.add(listener);
+- listener.setCluster(this);
+- }
+- }
+-
+- /**
+- * remove message listener and deregister Cluster from listener
+- *
+- * @see
org.apache.catalina.ha.CatalinaCluster#removeClusterListener(org.apache.catalina.ha.MessageListener)
+- */
+- public void removeClusterListener(ClusterListener listener) {
+- if (listener != null) {
+- clusterListeners.remove(listener);
+- listener.setCluster(null);
+- }
+- }
+-
+- /**
+- * get current Deployer
+- */
+- public org.apache.catalina.ha.ClusterDeployer getClusterDeployer() {
+- return clusterDeployer;
+- }
+-
+- /**
+- * set a new Deployer, must be set before cluster started!
+- */
+- public void setClusterDeployer(
+- org.apache.catalina.ha.ClusterDeployer clusterDeployer) {
+- this.clusterDeployer = clusterDeployer;
+- }
+-
+- public void setChannel(Channel channel) {
+- this.channel = channel;
+- }
+-
+- public void setManagerTemplate(ClusterManager managerTemplate) {
+- this.managerTemplate = managerTemplate;
+- }
+-
+- public void setChannelSendOptions(int channelSendOptions) {
+- this.channelSendOptions = channelSendOptions;
+- }
+-
+- /**
+- * has members
+- */
+- protected boolean hasMembers = false;
+- public boolean hasMembers() {
+- return hasMembers;
+- }
+-
+- /**
+- * Get all current cluster members
+- * @return all members or empty array
+- */
+- public Member[] getMembers() {
+- return channel.getMembers();
+- }
+-
+- /**
+- * Return the member that represents this node.
+- *
+- * @return Member
+- */
+- public Member getLocalMember() {
+- return channel.getLocalMember(true);
+- }
+-
+- // ------------------------------------------------------------- dynamic
+- // manager property handling
+-
+- /**
+- * JMX hack to direct use at jconsole
+- *
+- * @param name
+- * @param value
+- */
+- public boolean setProperty(String name, String value) {
+- return setProperty(name, (Object) value);
+- }
+-
+- /**
+- * set config attributes with reflect and propagate to all managers
+- *
+- * @param name
+- * @param value
+- */
+- public boolean setProperty(String name, Object value) {
+- properties.put(name, value);
+- return false;
+- }
+-
+- /**
+- * get current config
+- *
+- * @param key
+- * @return The property
+- */
+- public Object getProperty(String key) {
+- if (log.isTraceEnabled())
+- log.trace(sm.getString("SimpleTcpCluster.getProperty", key));
+- return properties.get(key);
+- }
+-
+- /**
+- * Get all properties keys
+- *
+- * @return An iterator over the property names.
+- */
+- public Iterator getPropertyNames() {
+- return properties.keySet().iterator();
+- }
+-
+- /**
+- * remove a configured property.
+- *
+- * @param key
+- */
+- public void removeProperty(String key) {
+- properties.remove(key);
+- }
+-
+- /**
+- * transfer properties from cluster configuration to subelement bean.
+- * @param prefix
+- * @param bean
+- */
+- protected void transferProperty(String prefix, Object bean) {
+- if (prefix != null) {
+- for (Iterator iter = getPropertyNames(); iter.hasNext();) {
+- String pkey = (String) iter.next();
+- if (pkey.startsWith(prefix)) {
+- String key = pkey.substring(prefix.length() + 1);
+- Object value = getProperty(pkey);
+- IntrospectionUtils.setProperty(bean, key, value.toString());
+- }
+- }
+- }
+- }
+-
+- // --------------------------------------------------------- Public Methods
+-
+- /**
+- * @return Returns the managers.
+- */
+- public Map getManagers() {
+- return managers;
+- }
+-
+- public Channel getChannel() {
+- return channel;
+- }
+-
+- public ClusterManager getManagerTemplate() {
+- return managerTemplate;
+- }
+-
+- public int getChannelSendOptions() {
+- return channelSendOptions;
+- }
+-
+- /**
+- * Create new Manager without add to cluster (comes with start the manager)
+- *
+- * @param name
+- * Context Name of this manager
+- * @see org.apache.catalina.Cluster#createManager(java.lang.String)
+- * @see #addManager(String, Manager)
+- * @see DeltaManager#start()
+- */
+- public synchronized Manager createManager(String name) {
+- if (log.isDebugEnabled()) log.debug("Creating ClusterManager for context
" + name + " using class " + getManagerClassName());
+- Manager manager = null;
+- try {
+- manager = managerTemplate.cloneFromTemplate();
+- ((ClusterManager)manager).setName(name);
+- } catch (Exception x) {
+- log.error("Unable to clone cluster manager, defaulting to
org.apache.catalina.ha.session.DeltaManager", x);
+- manager = new org.apache.catalina.ha.session.DeltaManager();
+- } finally {
+- if ( manager != null && (manager instanceof ClusterManager))
((ClusterManager)manager).setCluster(this);
+- }
+- return manager;
+- }
+-
+- public void registerManager(Manager manager) {
+-
+- if (! (manager instanceof ClusterManager)) {
+- log.warn("Manager [ " + manager + "] does not implement
ClusterManager, addition to cluster has been aborted.");
+- return;
+- }
+- ClusterManager cmanager = (ClusterManager) manager ;
+- cmanager.setDistributable(true);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(BEFORE_MANAGERREGISTER_EVENT, manager);
+- String clusterName = getManagerName(cmanager.getName(), manager);
+- cmanager.setName(clusterName);
+- cmanager.setCluster(this);
+- cmanager.setDefaultMode(false);
+-
+- managers.put(clusterName, manager);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(AFTER_MANAGERREGISTER_EVENT, manager);
+- }
+-
+- /**
+- * remove an application form cluster replication bus
+- *
+- * @see
org.apache.catalina.ha.CatalinaCluster#removeManager(java.lang.String,Manager)
+- */
+- public void removeManager(Manager manager) {
+- if (manager != null && manager instanceof ClusterManager ) {
+- ClusterManager cmgr = (ClusterManager) manager;
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(BEFORE_MANAGERUNREGISTER_EVENT,manager);
+- managers.remove(getManagerName(cmgr.getName(),manager));
+- cmgr.setCluster(null);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(AFTER_MANAGERUNREGISTER_EVENT, manager);
+- }
+- }
+-
+- /**
+- * @param name
+- * @param manager
+- * @return
+- */
+- public String getManagerName(String name, Manager manager) {
+- String clusterName = name ;
+- if ( clusterName == null ) clusterName = manager.getContainer().getName();
+- if(getContainer() instanceof Engine) {
+- Container context = manager.getContainer() ;
+- if(context != null && context instanceof Context) {
+- Container host = ((Context)context).getParent();
+- if(host != null && host instanceof Host &&
clusterName!=null && !(clusterName.indexOf("#")>=0))
+- clusterName = host.getName() +"#" + clusterName ;
+- }
+- }
+- return clusterName;
+- }
+-
+- /*
+- * Get Manager
+- *
+- * @see org.apache.catalina.ha.CatalinaCluster#getManager(java.lang.String)
+- */
+- public Manager getManager(String name) {
+- return (Manager) managers.get(name);
+- }
+-
+- // ------------------------------------------------------ Lifecycle Methods
+-
+- /**
+- * Execute a periodic task, such as reloading, etc. This method will be
+- * invoked inside the classloading context of this container. Unexpected
+- * throwables will be caught and logged.
+- * @see org.apache.catalina.ha.deploy.FarmWarDeployer#backgroundProcess()
+- * @see org.apache.catalina.tribes.group.GroupChannel#heartbeat()
+- * @see org.apache.catalina.tribes.group.GroupChannel.HeartbeatThread#run()
+- *
+- */
+- public void backgroundProcess() {
+- if (clusterDeployer != null) clusterDeployer.backgroundProcess();
+-
+- //send a heartbeat through the channel
+- if ( isHeartbeatBackgroundEnabled() && channel !=null )
channel.heartbeat();
+- }
+-
+- /**
+- * 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);
+- }
+-
+- /**
+- * Use as base to handle start/stop/periodic Events from host. Currently
+- * only log the messages as trace level.
+- *
+- * @see
org.apache.catalina.LifecycleListener#lifecycleEvent(org.apache.catalina.LifecycleEvent)
+- */
+- public void lifecycleEvent(LifecycleEvent lifecycleEvent) {
+- if (log.isTraceEnabled())
+- log.trace(sm.getString("SimpleTcpCluster.event.log",
lifecycleEvent.getType(), lifecycleEvent.getData()));
+- }
+-
+- // ------------------------------------------------------ public
+-
+- /**
+- * 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 {
+- if (started)
+- throw new
LifecycleException(sm.getString("cluster.alreadyStarted"));
+- if (log.isInfoEnabled()) log.info("Cluster is about to start");
+-
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, this);
+- try {
+- checkDefaults();
+- registerClusterValve();
+- channel.addMembershipListener(this);
+- channel.addChannelListener(this);
+- channel.start(channel.DEFAULT);
+- if (clusterDeployer != null) clusterDeployer.start();
+- this.started = true;
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(AFTER_START_EVENT, this);
+- } catch (Exception x) {
+- log.error("Unable to start cluster.", x);
+- throw new LifecycleException(x);
+- }
+- }
+-
+- protected void checkDefaults() {
+- if ( clusterListeners.size() == 0 ) {
+- addClusterListener(new JvmRouteSessionIDBinderListener());
+- addClusterListener(new ClusterSessionListener());
+- }
+- if ( valves.size() == 0 ) {
+- addValve(new JvmRouteBinderValve());
+- addValve(new ReplicationValve());
+- }
+- if ( clusterDeployer != null ) clusterDeployer.setCluster(this);
+- if ( channel == null ) channel = new GroupChannel();
+- if ( channel instanceof GroupChannel &&
!((GroupChannel)channel).getInterceptors().hasNext()) {
+- channel.addInterceptor(new MessageDispatch15Interceptor());
+- channel.addInterceptor(new TcpFailureDetector());
+- }
+- }
+-
+- /**
+- * register all cluster valve to host or engine
+- * @throws Exception
+- * @throws ClassNotFoundException
+- */
+- protected void registerClusterValve() throws Exception {
+- if(container != null ) {
+- for (Iterator iter = valves.iterator(); iter.hasNext();) {
+- ClusterValve valve = (ClusterValve) iter.next();
+- if (log.isDebugEnabled())
+- log.debug("Invoking addValve on " + getContainer()
+- + " with class=" + valve.getClass().getName());
+- if (valve != null) {
+- IntrospectionUtils.callMethodN(getContainer(),
"addValve",
+- new Object[] { valve },
+- new Class[] { org.apache.catalina.Valve.class });
+-
+- }
+- valve.setCluster(this);
+- }
+- }
+- }
+-
+- /**
+- * unregister all cluster valve to host or engine
+- * @throws Exception
+- * @throws ClassNotFoundException
+- */
+- protected void unregisterClusterValve() throws Exception {
+- for (Iterator iter = valves.iterator(); iter.hasNext();) {
+- ClusterValve valve = (ClusterValve) iter.next();
+- if (log.isDebugEnabled())
+- log.debug("Invoking removeValve on " + getContainer()
+- + " with class=" + valve.getClass().getName());
+- if (valve != null) {
+- IntrospectionUtils.callMethodN(getContainer(),
"removeValve",
+- new Object[] { valve }, new Class[] {
org.apache.catalina.Valve.class });
+- }
+- valve.setCluster(this);
+- }
+- }
+-
+- /**
+- * Gracefully terminate the active cluster component.<br/>
+- * This will disconnect the cluster communication channel, stop the
+- * listener and deregister the valves from host or engine.<br/><br/>
+- * <b>Note:</b><br/>The sub elements receiver, sender,
membership,
+- * listener or valves are not removed. You can easily start the cluster again.
+- *
+- * @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 {
+-
+- if (!started)
+- throw new
IllegalStateException(sm.getString("cluster.notStarted"));
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, this);
+-
+- if (clusterDeployer != null) clusterDeployer.stop();
+- this.managers.clear();
+- try {
+- if ( clusterDeployer != null ) clusterDeployer.setCluster(null);
+- channel.stop(Channel.DEFAULT);
+- channel.removeChannelListener(this);
+- channel.removeMembershipListener(this);
+- this.unregisterClusterValve();
+- } catch (Exception x) {
+- log.error("Unable to stop cluster valve.", x);
+- }
+- started = false;
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, this);
+- }
+-
+-
+-
+-
+- /**
+- * send message to all cluster members
+- * @param msg message to transfer
+- *
+- * @see
org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)
+- */
+- public void send(ClusterMessage msg) {
+- send(msg, null);
+- }
+-
+- /**
+- * send message to all cluster members same cluster domain
+- *
+- * @see
org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)
+- */
+- public void sendClusterDomain(ClusterMessage msg) {
+- send(msg,null);
+- }
+-
+-
+- /**
+- * send a cluster message to one member
+- *
+- * @param msg message to transfer
+- * @param dest Receiver member
+- * @see
org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage,
+- * org.apache.catalina.ha.Member)
+- */
+- public void send(ClusterMessage msg, Member dest) {
+- try {
+- msg.setAddress(getLocalMember());
+- if (dest != null) {
+- if (!getLocalMember().equals(dest)) {
+- channel.send(new Member[] {dest}, msg,channelSendOptions);
+- } else
+- log.error("Unable to send message to local member " +
msg);
+- } else {
+- if (channel.getMembers().length>0)
+- channel.send(channel.getMembers(),msg,channelSendOptions);
+- else if (log.isDebugEnabled())
+- log.debug("No members in cluster, ignoring
message:"+msg);
+- }
+- } catch (Exception x) {
+- log.error("Unable to send message through cluster sender.", x);
+- }
+- }
+-
+- /**
+- * New cluster member is registered
+- *
+- * @see
org.apache.catalina.ha.MembershipListener#memberAdded(org.apache.catalina.ha.Member)
+- */
+- public void memberAdded(Member member) {
+- try {
+- hasMembers = channel.hasMembers();
+- if (log.isInfoEnabled()) log.info("Replication member added:" +
member);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(BEFORE_MEMBERREGISTER_EVENT, member);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(AFTER_MEMBERREGISTER_EVENT, member);
+- } catch (Exception x) {
+- log.error("Unable to connect to replication system.", x);
+- }
+-
+- }
+-
+- /**
+- * Cluster member is gone
+- *
+- * @see
org.apache.catalina.ha.MembershipListener#memberDisappeared(org.apache.catalina.ha.Member)
+- */
+- public void memberDisappeared(Member member) {
+- try {
+- hasMembers = channel.hasMembers();
+- if (log.isInfoEnabled()) log.info("Received member disappeared:" +
member);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(BEFORE_MEMBERUNREGISTER_EVENT, member);
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(AFTER_MEMBERUNREGISTER_EVENT, member);
+- } catch (Exception x) {
+- log.error("Unable remove cluster node from replication system.",
x);
+- }
+- }
+-
+- // --------------------------------------------------------- receiver
+- // messages
+-
+- /**
+- * notify all listeners from receiving a new message is not ClusterMessage
+- * emitt Failure Event to LifecylceListener
+- *
+- * @param message
+- * receveived Message
+- */
+- public boolean accept(Serializable msg, Member sender) {
+- return (msg instanceof ClusterMessage);
+- }
+-
+-
+- public void messageReceived(Serializable message, Member sender) {
+- ClusterMessage fwd = (ClusterMessage)message;
+- fwd.setAddress(sender);
+- messageReceived(fwd);
+- }
+-
+- public void messageReceived(ClusterMessage message) {
+-
+- long start = 0;
+- if (log.isDebugEnabled() && message != null)
+- log.debug("Assuming clocks are synched: Replication for "
+- + message.getUniqueId() + " took="
+- + (System.currentTimeMillis() - (message).getTimestamp())
+- + " ms.");
+-
+- //invoke all the listeners
+- boolean accepted = false;
+- if (message != null) {
+- for (Iterator iter = clusterListeners.iterator(); iter.hasNext();) {
+- ClusterListener listener = (ClusterListener) iter.next();
+- if (listener.accept(message)) {
+- accepted = true;
+- listener.messageReceived(message);
+- }
+- }
+- }
+- if (!accepted && log.isDebugEnabled()) {
+- if (notifyLifecycleListenerOnFailure) {
+- Member dest = message.getAddress();
+- // Notify our interested LifecycleListeners
+- lifecycle.fireLifecycleEvent(RECEIVE_MESSAGE_FAILURE_EVENT,
+- new SendMessageData(message, dest, null));
+- }
+- log.debug("Message " + message.toString() + " from type
"
+- + message.getClass().getName()
+- + " transfered but no listener registered");
+- }
+- return;
+- }
+-
+- // --------------------------------------------------------- Logger
+-
+- public Log getLogger() {
+- return log;
+- }
+-
+-
+-
+-
+- // ------------------------------------------------------------- deprecated
+-
+- /**
+- *
+- * @see org.apache.catalina.Cluster#setProtocol(java.lang.String)
+- */
+- public void setProtocol(String protocol) {
+- }
+-
+- /**
+- * @see org.apache.catalina.Cluster#getProtocol()
+- */
+- public String getProtocol() {
+- return null;
+- }
+-}
+Index: java/org/apache/catalina/ha/tcp/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/ha/tcp/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/ha/tcp/mbeans-descriptors.xml (working copy)
+@@ -1,1054 +0,0 @@
+-<?xml version="1.0" encoding="UTF-8"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE mbeans-descriptors PUBLIC
+- "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+- "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+-<mbeans-descriptors>
+-
+- <mbean name="SimpleTcpCluster"
+- description="Tcp Cluster implementation"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.tcp.SimpleTcpCluster">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="notifyLifecycleListenerOnFailure"
+- description="notify lifecycleListener from message transfer
failure"
+- is="true"
+- type="boolean"/>
+- <attribute name="heartbeatBackgroundEnabled"
+- description="enable that container background thread call channel
heartbeat, default is that channel mangage heartbeat itself."
+- is="true"
+- type="boolean"/>
+- <attribute name="clusterName"
+- description="name of cluster"
+- type="java.lang.String"/>
+- <attribute name="managerClassName"
+- description="session mananager classname"
+- type="java.lang.String"/>
+- <attribute name="clusterLogName"
+- description="Name of cluster transfer log device"
+- type="java.lang.String"/>
+- <attribute name="doClusterLog"
+- is="true"
+- description="enable cluster log transfer logging"
+- type="boolean"/>
+- <operation name="setProperty"
+- description="set a property to all cluster managers (with prefix
'manager.')"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="key"
+- description="Property name"
+- type="java.lang.String"/>
+- <parameter name="value"
+- description="Property value"
+- type="java.lang.String"/>
+- </operation>
+-
+- <operation name="send"
+- description="send message to all cluster members"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="message"
+- description="replication message"
+- type="org.apache.catalina.ha.ClusterMessage"/>
+- </operation>
+-
+- <operation name="sendClusterDomain"
+- description="send message to all cluster members with same
domain"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="message"
+- description="replication message"
+- type="org.apache.catalina.ha.ClusterMessage"/>
+- </operation>
+-
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="ClusterReceiverBase"
+- description="Tcp Cluster ReplicationListener implementation"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.tcp.ClusterReceiverBase">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="tcpListenAddress"
+- description="tcp listener address"
+- type="java.lang.String"/>
+- <attribute name="tcpListenPort"
+- description="tcp listener port"
+- type="int"/>
+- <attribute name="tcpThreadCount"
+- description="number of tcp listener worker threads"
+- type="int"/>
+- <attribute name="tcpSelectorTimeout"
+- description="tcp listener Selector timeout"
+- type="long"/>
+- <attribute name="nrOfMsgsReceived"
+- description="number of messages received from other nodes"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedTime"
+- description="total time message send time"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedProcessingTime"
+- description="received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minReceivedProcessingTime"
+- description="minimal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgReceivedProcessingTime"
+- description="received processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxReceivedProcessingTime"
+- description="maximal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doReceivedProcessingStats"
+- description="create received processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="avgTotalReceivedBytes"
+- description="received totalReceivedBytes / nrOfMsgsReceived"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalReceivedBytes"
+- description="number of bytes received"
+- type="long"
+- writeable="false"/>
+- <attribute name="sendAck"
+- description="send ack after data received"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="compress"
+- description="data received compressed"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="doListen"
+- description="is port really started"
+- is="true"
+- type="boolean"
+- writeable="false" />
+-
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="SocketReplicationListener"
+- description="Tcp Cluster SocketReplicationListener implementation"
+- domain="Catalina"
+- group="Cluster"
+-
type="org.apache.catalina.ha.tcp.SocketReplicationListener">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="tcpListenAddress"
+- description="tcp listener address"
+- type="java.lang.String"/>
+- <attribute name="tcpListenPort"
+- description="tcp listener port"
+- type="int"/>
+- <attribute name="tcpListenMaxPort"
+- description="max tcp listen used port"
+- type="int"/>
+- <attribute name="tcpListenTimeout"
+- description="max tcp listen timeout (sec) wait for ServerSocket
start"
+- type="int"/>
+- <attribute name="nrOfMsgsReceived"
+- description="number of messages received from other nodes"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedTime"
+- description="total time message send time"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedProcessingTime"
+- description="received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minReceivedProcessingTime"
+- description="minimal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgReceivedProcessingTime"
+- description="received processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxReceivedProcessingTime"
+- description="maximal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doReceivedProcessingStats"
+- description="create received processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="avgTotalReceivedBytes"
+- description="received totalReceivedBytes / nrOfMsgsReceived"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalReceivedBytes"
+- description="number of bytes received"
+- type="long"
+- writeable="false"/>
+- <attribute name="sendAck"
+- description="send ack after data received"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="compress"
+- description="data received compressed"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="doListen"
+- description="is port really started"
+- is="true"
+- type="boolean"
+- writeable="false" />
+-
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="ReplicationTransmitter"
+- description="Tcp replication transmitter"
+- domain="Catalina"
+- group="ClusterSender"
+- type="org.apache.catalina.ha.tcp.ReplicationTransmitter">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="replicationMode"
+- description="replication mode
(synchnous,pooled.asynchnous,fastasyncqueue)"
+- type="java.lang.String"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="autoConnect"
+- description="is sender disabled, fork a new socket"
+- is="true"
+- type="boolean" />
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doTransmitterProcessingStats"
+- description="create processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="failureCounter"
+- description="number of wrong transfers"
+- type="long"
+- writeable="false"/>
+- <attribute name="senderObjectNames"
+- description="get all sender object names"
+- type="[Ljavax.management.ObjectName;"
+- writeable="false"/>
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check all sender connection for close socket
(keepalive)"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- </mbean>
+-
+- <mbean name="AsyncSocketSender"
+- description="Async Cluster Sender"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.AsyncSocketSender">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long"/>
+- <attribute name="queueSize"
+- writeable="false"
+- description="queue size"
+- type="int"/>
+- <attribute name="queuedNrOfBytes"
+- writeable="false"
+- description="number of bytes over all queued messages"
+- type="long"/>
+- <attribute name="messageTransferStarted"
+- description="message is in transfer"
+- type="boolean"
+- is="true"
+- writeable="false"/>
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="keepAliveCount"
+- description="keep Alive request count"
+- type="int"
+- writeable="false"/>
+- <attribute name="keepAliveConnectTime"
+- description="Connect time for keep alive"
+- type="long"
+- writeable="false"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doProcessingStats"
+- description="create processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="waitAckTime"
+- description="sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minWaitAckTime"
+- description="minimal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgWaitAckTime"
+- description="waitAck time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxWaitAckTime"
+- description="maximal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doWaitAckStats"
+- description="create waitAck time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenCounter"
+- description="counts open socket (KeepAlive and connects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenFailureCounter"
+- description="counts open socket failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketCloseCounter"
+- description="counts closed socket (KeepAlive and disconnects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="missingAckCounter"
+- description="counts missing ack"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataResendCounter"
+- description="counts data resends"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataFailureCounter"
+- description="counts data send failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="inQueueCounter"
+- description="counts all queued messages"
+- type="long"
+- writeable="false"/>
+- <attribute name="outQueueCounter"
+- description="counts all successfully sended messages"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="connect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="disconnect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check connection for close socket"
+- impact="ACTION"
+- returnType="boolean">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="FastAsyncSocketSender"
+- description="Fast Async Cluster Sender"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.FastAsyncSocketSender">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="threadPriority"
+- description="change queue thread priority"
+- type="int"/>
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long" />
+- <attribute name="queueSize"
+- writeable="false"
+- description="queue size"
+- type="int"/>
+- <attribute name="queuedNrOfBytes"
+- writeable="false"
+- description="number of bytes over all queued messages"
+- type="long"/>
+- <attribute name="messageTransferStarted"
+- description="message is in transfer"
+- type="boolean"
+- is="true"
+- writeable="false"/>
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="queueAddWaitTimeout"
+- description="add wait timeout (default 10000 msec)"
+- type="long"/>
+- <attribute name="queueRemoveWaitTimeout"
+- description="remove wait timeout (default 30000 msec)"
+- type="long"/>
+- <attribute name="maxQueueLength"
+- description="max queue length"
+- type="int"/>
+- <attribute name="queueTimeWait"
+- description="remember queue wait times"
+- is="true"
+- type="boolean"/>
+- <attribute name="queueCheckLock"
+- description="check to lost locks"
+- is="true"
+- type="boolean"/>
+- <attribute name="queueDoStats"
+- description="activated queue stats"
+- is="true"
+- type="boolean"/>
+- <attribute name="keepAliveCount"
+- description="keep Alive request count"
+- type="int"
+- writeable="false"/>
+- <attribute name="keepAliveConnectTime"
+- description="Connect time for keep alive"
+- type="long"
+- writeable="false"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doProcessingStats"
+- description="create Processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="waitAckTime"
+- description="sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minWaitAckTime"
+- description="minimal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgWaitAckTime"
+- description="waitAck time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxWaitAckTime"
+- description="maximal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doWaitAckStats"
+- description="create waitAck time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenCounter"
+- description="counts open socket (KeepAlive and connects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenFailureCounter"
+- description="counts open socket failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketCloseCounter"
+- description="counts closed socket (KeepAlive and disconnects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="missingAckCounter"
+- description="counts missing ack"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataResendCounter"
+- description="counts data resends"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataFailureCounter"
+- description="counts data send failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="inQueueCounter"
+- description="counts all queued messages"
+- type="long"
+- writeable="false"/>
+- <attribute name="outQueueCounter"
+- description="counts all successfully sended messages"
+- type="long"
+- writeable="false"/>
+- <attribute name="queueAddWaitTime"
+- description="queue add wait time (tomcat thread waits)"
+- type="long"
+- writeable="false"/>
+- <attribute name="queueRemoveWaitTime"
+- description="queue remove wait time (queue thread waits)"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="connect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="disconnect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check connection for close socket"
+- impact="ACTION"
+- returnType="boolean">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="PooledSocketSender"
+- description="Pooled Cluster Sender"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.PooledSocketSender">
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="maxPoolSocketLimit"
+- description="Max parallel sockets"
+- type="int"/>
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="start Queue to connect to ohter replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="stop Queue to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="SocketSender"
+- description="Sync Cluster Sender"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.SocketSender">
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="messageTransferStarted"
+- description="message is in transfer"
+- type="boolean"
+- is="true"
+- writeable="false"/>
+- <attribute name="keepAliveCount"
+- description="keep Alive request count"
+- type="int"
+- writeable="false"/>
+- <attribute name="keepAliveConnectTime"
+- description="Connect time for keep alive"
+- type="long"
+- writeable="false"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doProcessingStats"
+- description="create Processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="waitAckTime"
+- description="sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minWaitAckTime"
+- description="minimal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgWaitAckTime"
+- description="waitAck time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxWaitAckTime"
+- description="maximal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doWaitAckStats"
+- description="create waitAck time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketCloseCounter"
+- description="counts closed socket (KeepAlive and disconnects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenFailureCounter"
+- description="counts open socket failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenCounter"
+- description="counts open socket (KeepAlive and connects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="missingAckCounter"
+- description="counts missing ack"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataResendCounter"
+- description="counts data resends"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataFailureCounter"
+- description="counts data send failures"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="connect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="disconnect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check connection for close socket"
+- impact="ACTION"
+- returnType="boolean">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="ReplicationValve"
+- description="Valve for simple tcp replication"
+- domain="Catalina"
+- group="Valve"
+- type="org.apache.catalina.ha.tcp.ReplicationValve">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="filter"
+- description="resource filter to disable session replication check"
+- type="java.lang.String"/>
+- <attribute name="primaryIndicator"
+- is="true"
+- description="set indicator that request processing is at primary session
node"
+- type="boolean"/>
+- <attribute name="primaryIndicatorName"
+- description="Request attribute name to indicate that request processing
is at primary session node"
+- type="java.lang.String"/>
+- <attribute name="doProcessingStats"
+- is="true"
+- description="active statistics counting"
+- type="boolean"/>
+- <attribute name="nrOfRequests"
+- description="number of replicated requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="nrOfFilterRequests"
+- description="number of filtered requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="nrOfSendRequests"
+- description="number of send requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="nrOfCrossContextSendRequests"
+- description="number of send cross context session requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalRequestTime"
+- description="total replicated request time"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalSendTime"
+- description="total replicated send time"
+- type="long"
+- writeable="false"/>
+- <attribute name="lastSendTime"
+- description="last replicated request time"
+- type="long"
+- writeable="false"/>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+-
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/ha/tcp/ReplicationValve.java
+===================================================================
+--- java/org/apache/catalina/ha/tcp/ReplicationValve.java (revision 590752)
++++ java/org/apache/catalina/ha/tcp/ReplicationValve.java (working copy)
+@@ -1,658 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.tcp;
+-
+-import java.io.IOException;
+-import java.util.StringTokenizer;
+-import java.util.regex.Pattern;
+-import java.util.ArrayList;
+-import java.util.List;
+-import java.util.Iterator;
+-import javax.servlet.ServletException;
+-
+-import org.apache.catalina.Manager;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.Context;
+-import org.apache.catalina.core.StandardContext;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.ClusterSession;
+-import org.apache.catalina.ha.ClusterValve;
+-import org.apache.catalina.ha.session.DeltaManager;
+-import org.apache.catalina.ha.session.DeltaSession;
+-import org.apache.catalina.connector.Request;
+-import org.apache.catalina.connector.Response;
+-import org.apache.catalina.util.StringManager;
+-import org.apache.catalina.valves.ValveBase;
+-
+-/**
+- * <p>Implementation of a Valve that logs interesting contents from the
+- * specified Request (before processing) and the corresponding Response
+- * (after processing). It is especially useful in debugging problems
+- * related to headers and cookies.</p>
+- *
+- * <p>This Valve may be attached to any Container, depending on the granularity
+- * of the logging you wish to perform.</p>
+- *
+- * <p>primaryIndicator=true, then the request attribute
<i>org.apache.catalina.ha.tcp.isPrimarySession.</i>
+- * is set true, when request processing is at sessions primary node.
+- * </p>
+- *
+- * @author Craig R. McClanahan
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-
+-public class ReplicationValve
+- extends ValveBase implements ClusterValve {
+-
+- private static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog( ReplicationValve.class );
+-
+- // ----------------------------------------------------- Instance Variables
+-
+- /**
+- * The descriptive information related to this implementation.
+- */
+- private static final String info =
+- "org.apache.catalina.ha.tcp.ReplicationValve/2.0";
+-
+-
+- /**
+- * The StringManager for this package.
+- */
+- protected static StringManager sm =
+- StringManager.getManager(Constants.Package);
+-
+- private CatalinaCluster cluster = null ;
+-
+- /**
+- * holds file endings to not call for like images and others
+- */
+- protected java.util.regex.Pattern[] reqFilters = new java.util.regex.Pattern[0];
+-
+- /**
+- * Orginal filter
+- */
+- protected String filter ;
+-
+- /**
+- * crossContext session container
+- */
+- protected ThreadLocal crossContextSessions = new ThreadLocal() ;
+-
+- /**
+- * doProcessingStats (default = off)
+- */
+- protected boolean doProcessingStats = false;
+-
+- protected long totalRequestTime = 0;
+- protected long totalSendTime = 0;
+- protected long nrOfRequests = 0;
+- protected long lastSendTime = 0;
+- protected long nrOfFilterRequests = 0;
+- protected long nrOfSendRequests = 0;
+- protected long nrOfCrossContextSendRequests = 0;
+-
+- /**
+- * must primary change indicator set
+- */
+- protected boolean primaryIndicator = false ;
+-
+- /**
+- * Name of primary change indicator as request attribute
+- */
+- protected String primaryIndicatorName =
"org.apache.catalina.ha.tcp.isPrimarySession";
+-
+- // ------------------------------------------------------------- Properties
+-
+- public ReplicationValve() {
+- }
+-
+- /**
+- * Return descriptive information about this Valve implementation.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- /**
+- * @return Returns the cluster.
+- */
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- /**
+- * @param cluster The cluster to set.
+- */
+- public void setCluster(CatalinaCluster cluster) {
+- this.cluster = cluster;
+- }
+-
+- /**
+- * @return Returns the filter
+- */
+- public String getFilter() {
+- return filter ;
+- }
+-
+- /**
+- * compile filter string to regular expressions
+- * @see Pattern#compile(java.lang.String)
+- * @param filter
+- * The filter to set.
+- */
+- public void setFilter(String filter) {
+- if (log.isDebugEnabled())
+- log.debug(sm.getString("ReplicationValve.filter.loading",
filter));
+- this.filter = filter;
+- StringTokenizer t = new StringTokenizer(filter, ";");
+- this.reqFilters = new Pattern[t.countTokens()];
+- int i = 0;
+- while (t.hasMoreTokens()) {
+- String s = t.nextToken();
+- if (log.isTraceEnabled())
+- log.trace(sm.getString("ReplicationValve.filter.token", s));
+- try {
+- reqFilters[i++] = Pattern.compile(s);
+- } catch (Exception x) {
+-
log.error(sm.getString("ReplicationValve.filter.token.failure",
+- s), x);
+- }
+- }
+- }
+-
+- /**
+- * @return Returns the primaryIndicator.
+- */
+- public boolean isPrimaryIndicator() {
+- return primaryIndicator;
+- }
+-
+- /**
+- * @param primaryIndicator The primaryIndicator to set.
+- */
+- public void setPrimaryIndicator(boolean primaryIndicator) {
+- this.primaryIndicator = primaryIndicator;
+- }
+-
+- /**
+- * @return Returns the primaryIndicatorName.
+- */
+- public String getPrimaryIndicatorName() {
+- return primaryIndicatorName;
+- }
+-
+- /**
+- * @param primaryIndicatorName The primaryIndicatorName to set.
+- */
+- public void setPrimaryIndicatorName(String primaryIndicatorName) {
+- this.primaryIndicatorName = primaryIndicatorName;
+- }
+-
+- /**
+- * Calc processing stats
+- */
+- public boolean doStatistics() {
+- return doProcessingStats;
+- }
+-
+- /**
+- * Set Calc processing stats
+- * @see #resetStatistics()
+- */
+- public void setStatistics(boolean doProcessingStats) {
+- this.doProcessingStats = doProcessingStats;
+- }
+-
+- /**
+- * @return Returns the lastSendTime.
+- */
+- public long getLastSendTime() {
+- return lastSendTime;
+- }
+-
+- /**
+- * @return Returns the nrOfRequests.
+- */
+- public long getNrOfRequests() {
+- return nrOfRequests;
+- }
+-
+- /**
+- * @return Returns the nrOfFilterRequests.
+- */
+- public long getNrOfFilterRequests() {
+- return nrOfFilterRequests;
+- }
+-
+- /**
+- * @return Returns the nrOfCrossContextSendRequests.
+- */
+- public long getNrOfCrossContextSendRequests() {
+- return nrOfCrossContextSendRequests;
+- }
+-
+- /**
+- * @return Returns the nrOfSendRequests.
+- */
+- public long getNrOfSendRequests() {
+- return nrOfSendRequests;
+- }
+-
+- /**
+- * @return Returns the totalRequestTime.
+- */
+- public long getTotalRequestTime() {
+- return totalRequestTime;
+- }
+-
+- /**
+- * @return Returns the totalSendTime.
+- */
+- public long getTotalSendTime() {
+- return totalSendTime;
+- }
+-
+- /**
+- * @return Returns the reqFilters.
+- */
+- protected java.util.regex.Pattern[] getReqFilters() {
+- return reqFilters;
+- }
+-
+- /**
+- * @param reqFilters The reqFilters to set.
+- */
+- protected void setReqFilters(java.util.regex.Pattern[] reqFilters) {
+- this.reqFilters = reqFilters;
+- }
+-
+-
+- // --------------------------------------------------------- Public Methods
+-
+- /**
+- * Register all cross context sessions inside endAccess.
+- * Use a list with contains check, that the Portlet API can include a lot of
fragments from same or
+- * different applications with session changes.
+- *
+- * @param session cross context session
+- */
+- public void registerReplicationSession(DeltaSession session) {
+- List sessions = (List)crossContextSessions.get();
+- if(sessions != null) {
+- if(!sessions.contains(session)) {
+- if(log.isDebugEnabled())
+-
log.debug(sm.getString("ReplicationValve.crossContext.registerSession",
+- session.getIdInternal(),
+- session.getManager().getContainer().getName()));
+- sessions.add(session);
+- }
+- }
+- }
+-
+- /**
+- * 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
+- */
+- 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());
+- }
+- getNext().invoke(request, response);
+- Manager manager = request.getContext().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);
+- }
+- }
+- }
+-
+-
+- /**
+- * reset the active statitics
+- */
+- public void resetStatistics() {
+- totalRequestTime = 0 ;
+- totalSendTime = 0 ;
+- lastSendTime = 0 ;
+- nrOfFilterRequests = 0 ;
+- nrOfRequests = 0 ;
+- nrOfSendRequests = 0;
+- nrOfCrossContextSendRequests = 0;
+- }
+-
+- /**
+- * Return a String rendering of this object.
+- */
+- public String toString() {
+-
+- StringBuffer sb = new StringBuffer("ReplicationValve[");
+- if (container != null)
+- sb.append(container.getName());
+- sb.append("]");
+- return (sb.toString());
+-
+- }
+-
+- // --------------------------------------------------------- Protected Methods
+-
+- /**
+- * @param request
+- * @param totalstart
+- * @param isCrossContext
+- * @param clusterManager
+- * @param containerCluster
+- */
+- protected void sendReplicationMessage(Request request, long totalstart, boolean
isCrossContext, ClusterManager clusterManager, CatalinaCluster containerCluster) {
+- //this happens after the request
+- long start = 0;
+- if(doStatistics()) {
+- start = System.currentTimeMillis();
+- }
+- try {
+- // send invalid sessions
+- // DeltaManager returns String[0]
+- if (!(clusterManager instanceof DeltaManager))
+- sendInvalidSessions(clusterManager, containerCluster);
+- // send replication
+- sendSessionReplicationMessage(request, clusterManager, containerCluster);
+- if(isCrossContext)
+- sendCrossContextSession(containerCluster);
+- } catch (Exception x) {
+- // FIXME we have a lot of sends, but the trouble with one node stops the
correct replication to other nodes!
+- log.error(sm.getString("ReplicationValve.send.failure"), x);
+- } finally {
+- // FIXME this stats update are not cheap!!
+- if(doStatistics()) {
+- updateStats(totalstart,start);
+- }
+- }
+- }
+-
+- /**
+- * Send all changed cross context sessions to backups
+- * @param containerCluster
+- */
+- protected void sendCrossContextSession(CatalinaCluster containerCluster) {
+- Object sessions = crossContextSessions.get();
+- if(sessions != null && sessions instanceof List
+- && ((List)sessions).size() >0) {
+- for(Iterator iter = ((List)sessions).iterator(); iter.hasNext() ;) {
+- Session session = (Session)iter.next();
+- if(log.isDebugEnabled())
+-
log.debug(sm.getString("ReplicationValve.crossContext.sendDelta",
+- session.getManager().getContainer().getName() ));
+-
sendMessage(session,(ClusterManager)session.getManager(),containerCluster);
+- if(doStatistics()) {
+- nrOfCrossContextSendRequests++;
+- }
+- }
+- }
+- }
+-
+- /**
+- * Fix memory leak for long sessions with many changes, when no backup member
exists!
+- * @param request current request after responce is generated
+- * @param isCrossContext check crosscontext threadlocal
+- */
+- protected void resetReplicationRequest(Request request, boolean isCrossContext) {
+- Session contextSession = request.getSessionInternal(false);
+- if(contextSession != null & contextSession instanceof DeltaSession){
+- resetDeltaRequest(contextSession);
+- ((DeltaSession)contextSession).setPrimarySession(true);
+- }
+- if(isCrossContext) {
+- Object sessions = crossContextSessions.get();
+- if(sessions != null && sessions instanceof List
+- && ((List)sessions).size() >0) {
+- Iterator iter = ((List)sessions).iterator();
+- for(; iter.hasNext() ;) {
+- Session session = (Session)iter.next();
+- resetDeltaRequest(session);
+- if(session instanceof DeltaSession)
+- ((DeltaSession)contextSession).setPrimarySession(true);
+-
+- }
+- }
+- }
+- }
+-
+- /**
+- * Reset DeltaRequest from session
+- * @param session HttpSession from current request or cross context session
+- */
+- protected void resetDeltaRequest(Session session) {
+- if(log.isDebugEnabled()) {
+- log.debug(sm.getString("ReplicationValve.resetDeltaRequest" ,
+- session.getManager().getContainer().getName() ));
+- }
+- ((DeltaSession)session).resetDeltaRequest();
+- }
+-
+- /**
+- * Send Cluster Replication Request
+- * @param request current request
+- * @param manager session manager
+- * @param cluster replication cluster
+- */
+- protected void sendSessionReplicationMessage(Request request,
+- ClusterManager manager, CatalinaCluster cluster) {
+- Session session = request.getSessionInternal(false);
+- if (session != null) {
+- String uri = request.getDecodedRequestURI();
+- // request without session change
+- if (!isRequestWithoutSessionChange(uri)) {
+- if (log.isDebugEnabled())
+- log.debug(sm.getString("ReplicationValve.invoke.uri",
uri));
+- sendMessage(session,manager,cluster);
+- } else
+- if(doStatistics())
+- nrOfFilterRequests++;
+- }
+-
+- }
+-
+- /**
+- * Send message delta message from request session
+- * @param request current request
+- * @param manager session manager
+- * @param cluster replication cluster
+- */
+- protected void sendMessage(Session session,
+- ClusterManager manager, CatalinaCluster cluster) {
+- String id = session.getIdInternal();
+- if (id != null) {
+- send(manager, cluster, id);
+- }
+- }
+-
+- /**
+- * 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) {
+- if(manager.doDomainReplication()) {
+- cluster.sendClusterDomain(msg);
+- } else {
+- cluster.send(msg);
+- }
+- if(doStatistics())
+- nrOfSendRequests++;
+- }
+- }
+-
+- /**
+- * check for session invalidations
+- * @param manager
+- * @param cluster
+- */
+- protected void sendInvalidSessions(ClusterManager manager, CatalinaCluster cluster)
{
+- String[] invalidIds=manager.getInvalidatedSessions();
+- if ( invalidIds.length > 0 ) {
+- for ( int i=0;i<invalidIds.length; i++ ) {
+- try {
+- send(manager,cluster,invalidIds[i]);
+- } catch ( Exception x ) {
+-
log.error(sm.getString("ReplicationValve.send.invalid.failure",invalidIds[i]),x);
+- }
+- }
+- }
+- }
+-
+- /**
+- * is request without possible session change
+- * @param uri The request uri
+- * @return True if no session change
+- */
+- protected boolean isRequestWithoutSessionChange(String uri) {
+-
+- boolean filterfound = false;
+-
+- for (int i = 0; (i < reqFilters.length) && (!filterfound); i++) {
+- java.util.regex.Matcher matcher = reqFilters[i].matcher(uri);
+- filterfound = matcher.matches();
+- }
+- return filterfound;
+- }
+-
+- /**
+- * protocol cluster replications stats
+- * @param requestTime
+- * @param clusterTime
+- */
+- protected void updateStats(long requestTime, long clusterTime) {
+- synchronized(this) {
+- lastSendTime=System.currentTimeMillis();
+- totalSendTime+=lastSendTime - clusterTime;
+- totalRequestTime+=lastSendTime - requestTime;
+- nrOfRequests++;
+- }
+- if(log.isInfoEnabled()) {
+- if ( (nrOfRequests % 100) == 0 ) {
+- log.info(sm.getString("ReplicationValve.stats",
+- new Object[]{
+- new Long(totalRequestTime/nrOfRequests),
+- new Long(totalSendTime/nrOfRequests),
+- new Long(nrOfRequests),
+- new Long(nrOfSendRequests),
+- new Long(nrOfCrossContextSendRequests),
+- new Long(nrOfFilterRequests),
+- new Long(totalRequestTime),
+- new Long(totalSendTime)}));
+- }
+- }
+- }
+-
+-
+- /**
+- * Mark Request that processed at primary node with attribute
+- * primaryIndicatorName
+- *
+- * @param request
+- * @throws IOException
+- */
+- protected void createPrimaryIndicator(Request request) throws IOException {
+- String id = request.getRequestedSessionId();
+- if ((id != null) && (id.length() > 0)) {
+- Manager manager = request.getContext().getManager();
+- Session session = manager.findSession(id);
+- if (session instanceof ClusterSession) {
+- ClusterSession cses = (ClusterSession) session;
+- if (cses != null) {
+- if (log.isDebugEnabled())
+- log.debug(sm.getString(
+- "ReplicationValve.session.indicator",
request.getContext().getName(),id,
+- primaryIndicatorName, cses.isPrimarySession()));
+- request.setAttribute(primaryIndicatorName,
cses.isPrimarySession()?Boolean.TRUE:Boolean.FALSE);
+- }
+- } else {
+- if (log.isDebugEnabled()) {
+- if (session != null) {
+- log.debug(sm.getString(
+- "ReplicationValve.session.found",
request.getContext().getName(),id));
+- } else {
+- log.debug(sm.getString(
+- "ReplicationValve.session.invalid",
request.getContext().getName(),id));
+- }
+- }
+- }
+- }
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/tcp/SendMessageData.java
+===================================================================
+--- java/org/apache/catalina/ha/tcp/SendMessageData.java (revision 590752)
++++ java/org/apache/catalina/ha/tcp/SendMessageData.java (working copy)
+@@ -1,82 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.tcp;
+-
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-public class SendMessageData {
+-
+- private Object message ;
+- private Member destination ;
+- private Exception exception ;
+-
+-
+- /**
+- * @param message
+- * @param destination
+- * @param exception
+- */
+- public SendMessageData(Object message, Member destination,
+- Exception exception) {
+- super();
+- this.message = message;
+- this.destination = destination;
+- this.exception = exception;
+- }
+-
+- /**
+- * @return Returns the destination.
+- */
+- public Member getDestination() {
+- return destination;
+- }
+- /**
+- * @param destination The destination to set.
+- */
+- public void setDestination(Member destination) {
+- this.destination = destination;
+- }
+- /**
+- * @return Returns the exception.
+- */
+- public Exception getException() {
+- return exception;
+- }
+- /**
+- * @param exception The exception to set.
+- */
+- public void setException(Exception exception) {
+- this.exception = exception;
+- }
+- /**
+- * @return Returns the message.
+- */
+- public Object getMessage() {
+- return message;
+- }
+- /**
+- * @param message The message to set.
+- */
+- public void setMessage(Object message) {
+- this.message = message;
+- }
+-}
+Index: java/org/apache/catalina/ha/tcp/Constants.java
+===================================================================
+--- java/org/apache/catalina/ha/tcp/Constants.java (revision 590752)
++++ java/org/apache/catalina/ha/tcp/Constants.java (working copy)
+@@ -1,33 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha.tcp;
+-
+-/**
+- * Manifest constants for the <code>org.apache.catalina.ha.tcp</code>
+- * package.
+- *
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-
+-public class Constants {
+-
+- public static final String Package = "org.apache.catalina.ha.tcp";
+-
+-}
+Index: java/org/apache/catalina/ha/ClusterManager.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterManager.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterManager.java (working copy)
+@@ -1,112 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha;
+-
+-
+-import org.apache.catalina.Manager;
+-import java.io.IOException;
+-import org.apache.catalina.tribes.io.ReplicationStream;
+-
+-
+-/**
+- * The common interface used by all cluster manager.
+- * This is so that we can have a more pluggable way
+- * of swapping session managers for different algorithms.
+- *
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- */
+-public interface ClusterManager extends Manager {
+-
+- /**
+- * A message was received from another node, this
+- * is the callback method to implement if you are interested in
+- * receiving replication messages.
+- * @param msg - the message received.
+- */
+- public void messageDataReceived(ClusterMessage msg);
+-
+- /**
+- * When the request has been completed, the replication valve
+- * will notify the manager, and the manager will decide whether
+- * any replication is needed or not.
+- * If there is a need for replication, the manager will
+- * create a session message and that will be replicated.
+- * The cluster determines where it gets sent.
+- * @param sessionId - the sessionId that just completed.
+- * @return a SessionMessage to be sent.
+- */
+- public ClusterMessage requestCompleted(String sessionId);
+-
+- /**
+- * When the manager expires session not tied to a request.
+- * The cluster will periodically ask for a list of sessions
+- * that should expire and that should be sent across the wire.
+- * @return String[] The invalidated sessions
+- */
+- public String[] getInvalidatedSessions();
+-
+- /**
+- * Return the name of the manager, at host /context name and at engine
hostname+/context.
+- * @return String
+- * @since 5.5.10
+- */
+- public String getName();
+-
+- /**
+- * Set the name of the manager, at host /context name and at engine
hostname+/context
+- * @param name
+- * @since 5.5.10
+- */
+- public void setName(String name);
+-
+- public CatalinaCluster getCluster();
+-
+- public void setCluster(CatalinaCluster cluster);
+-
+- /**
+- * @return Manager send only to same cluster domain.
+- * @since 5.5.10
+- */
+- public boolean doDomainReplication();
+-
+- /**
+- * @param sendClusterDomainOnly Flag value.
+- * @since 5.5.10
+- */
+- public void setDomainReplication(boolean domainReplication);
+-
+- /**
+- * @param mode The mode
+- * @since 5.5.10
+- */
+- public void setDefaultMode(boolean mode);
+-
+- /**
+- * @since 5.5.10
+- */
+- public boolean isDefaultMode();
+-
+- public ReplicationStream getReplicationStream(byte[] data) throws IOException;
+-
+- public ReplicationStream getReplicationStream(byte[] data, int offset, int length)
throws IOException;
+-
+- public boolean isNotifyListenersOnReplication();
+-
+- public ClusterManager cloneFromTemplate();
+-}
+Index: java/org/apache/catalina/ha/Constants.java
+===================================================================
+--- java/org/apache/catalina/ha/Constants.java (revision 590752)
++++ java/org/apache/catalina/ha/Constants.java (working copy)
+@@ -1,31 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha;
+-
+-/**
+- * Manifest constants for the <code>org.apache.catalina.ha</code>
+- * package.
+- *
+- * @author Bip Thelin
+- * @version $Revision$, $Date$
+- */
+-
+-public final class Constants {
+- public static final String Package = "org.apache.catalina.ha";
+-}
+Index: java/org/apache/catalina/ha/deploy/FarmWarDeployer.java
+===================================================================
+--- java/org/apache/catalina/ha/deploy/FarmWarDeployer.java (revision 590752)
++++ java/org/apache/catalina/ha/deploy/FarmWarDeployer.java (working copy)
+@@ -1,750 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.deploy;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.net.URL;
+-import java.util.HashMap;
+-import javax.management.MBeanServer;
+-import javax.management.ObjectName;
+-
+-import org.apache.catalina.Context;
+-import org.apache.catalina.Engine;
+-import org.apache.catalina.Host;
+-import org.apache.catalina.Lifecycle;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterDeployer;
+-import org.apache.catalina.ha.ClusterListener;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.tomcat.util.modeler.Registry;
+-
+-
+-/**
+- * <p>
+- * A farm war deployer is a class that is able to deploy/undeploy web
+- * applications in WAR form within the cluster.
+- * </p>
+- * Any host can act as the admin, and will have three directories
+- * <ul>
+- * <li>deployDir - the directory where we watch for changes</li>
+- * <li>applicationDir - the directory where we install applications</li>
+- * <li>tempDir - a temporaryDirectory to store binary data when downloading a
+- * war from the cluster</li>
+- * </ul>
+- * Currently we only support deployment of WAR files since they are easier to
+- * send across the wire.
+- *
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$
+- */
+-public class FarmWarDeployer extends ClusterListener implements ClusterDeployer,
FileChangeListener {
+- /*--Static Variables----------------------------------------*/
+- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+- .getLog(FarmWarDeployer.class);
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "FarmWarDeployer/1.2";
+-
+- /*--Instance Variables--------------------------------------*/
+- protected CatalinaCluster cluster = null;
+-
+- protected boolean started = false; //default 5 seconds
+-
+- protected HashMap fileFactories = new HashMap();
+-
+- protected String deployDir;
+-
+- protected String tempDir;
+-
+- protected String watchDir;
+-
+- protected boolean watchEnabled = false;
+-
+- protected WarWatcher watcher = null;
+-
+- /**
+- * Iteration count for background processing.
+- */
+- private int count = 0;
+-
+- /**
+- * Frequency of the Farm watchDir check. Cluster wide deployment will be
+- * done once for the specified amount of backgrondProcess calls (ie, the
+- * lower the amount, the most often the checks will occur).
+- */
+- protected int processDeployFrequency = 2;
+-
+- /**
+- * Path where context descriptors should be deployed.
+- */
+- protected File configBase = null;
+-
+- /**
+- * The associated host.
+- */
+- protected Host host = null;
+-
+- /**
+- * The host appBase.
+- */
+- protected File appBase = null;
+-
+- /**
+- * MBean server.
+- */
+- protected MBeanServer mBeanServer = null;
+-
+- /**
+- * The associated deployer ObjectName.
+- */
+- protected ObjectName oname = null;
+-
+- /*--Constructor---------------------------------------------*/
+- public FarmWarDeployer() {
+- }
+-
+- /**
+- * Return descriptive information about this deployer implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- /*--Logic---------------------------------------------------*/
+- public void start() throws Exception {
+- if (started)
+- return;
+- getCluster().addClusterListener(this);
+- if (watchEnabled) {
+- watcher = new WarWatcher(this, new File(getWatchDir()));
+- if (log.isInfoEnabled())
+- log.info("Cluster deployment is watching " + getWatchDir()
+- + " for changes.");
+- }
+-
+- // Check to correct engine and host setup
+- Object parent = getCluster().getContainer();
+- Engine engine = null;
+- String hostname = null;
+- if ( parent instanceof Host ) {
+- host = (Host) parent;
+- engine = (Engine) host.getParent();
+- hostname = host.getName();
+- }else {
+- engine = (Engine)parent;
+- hostname = engine.getDefaultHost();
+- }
+- try {
+- oname = new ObjectName(engine.getName() + ":type=Deployer,host="
+- + hostname);
+- } catch (Exception e) {
+- log.error("Can't construct MBean object name" + e);
+- }
+- configBase = new File(System.getProperty("catalina.base"),
"conf");
+- if (engine != null) {
+- configBase = new File(configBase, engine.getName());
+- } else if (host != null) {
+- configBase = new File(configBase, host.getName());
+- }
+-
+- // Retrieve the MBean server
+- mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
+-
+- started = true;
+- count = 0;
+- if (log.isInfoEnabled())
+- log.info("Cluster FarmWarDeployer started.");
+- }
+-
+- /*
+- * stop cluster wide deployments
+- *
+- * @see org.apache.catalina.ha.ClusterDeployer#stop()
+- */
+- public void stop() throws LifecycleException {
+- started = false;
+- getCluster().removeClusterListener(this);
+- count = 0;
+- if (watcher != null) {
+- watcher.clear();
+- watcher = null;
+-
+- }
+- if (log.isInfoEnabled())
+- log.info("Cluster FarmWarDeployer stopped.");
+- }
+-
+- public void cleanDeployDir() {
+- throw new java.lang.UnsupportedOperationException(
+- "Method cleanDeployDir() not yet implemented.");
+- }
+-
+- /**
+- * Callback from the cluster, when a message is received, The cluster will
+- * broadcast it invoking the messageReceived on the receiver.
+- *
+- * @param msg
+- * ClusterMessage - the message received from the cluster
+- */
+- public void messageReceived(ClusterMessage msg) {
+- try {
+- if (msg instanceof FileMessage && msg != null) {
+- FileMessage fmsg = (FileMessage) msg;
+- if (log.isDebugEnabled())
+- log.debug("receive cluster deployment [ path: "
+- + fmsg.getContextPath() + " war: "
+- + fmsg.getFileName() + " ]");
+- FileMessageFactory factory = getFactory(fmsg);
+- // TODO correct second try after app is in service!
+- if (factory.writeMessage(fmsg)) {
+- //last message received war file is completed
+- String name = factory.getFile().getName();
+- if (!name.endsWith(".war"))
+- name = name + ".war";
+- File deployable = new File(getDeployDir(), name);
+- try {
+- String path = fmsg.getContextPath();
+- if (!isServiced(path)) {
+- addServiced(path);
+- try {
+- remove(path);
+- factory.getFile().renameTo(deployable);
+- check(path);
+- } finally {
+- removeServiced(path);
+- }
+- if (log.isDebugEnabled())
+- log.debug("deployment from " + path
+- + " finished.");
+- } else
+- log.error("Application " + path
+- + " in used. touch war file " + name
+- + " again!");
+- } catch (Exception ex) {
+- log.error(ex);
+- } finally {
+- removeFactory(fmsg);
+- }
+- }
+- } else if (msg instanceof UndeployMessage && msg != null) {
+- try {
+- UndeployMessage umsg = (UndeployMessage) msg;
+- String path = umsg.getContextPath();
+- if (log.isDebugEnabled())
+- log.debug("receive cluster undeployment from " +
path);
+- if (!isServiced(path)) {
+- addServiced(path);
+- try {
+- remove(path);
+- } finally {
+- removeServiced(path);
+- }
+- if (log.isDebugEnabled())
+- log.debug("undeployment from " + path
+- + " finished.");
+- } else
+- log.error("Application "
+- + path
+- + " in used. Sorry not remove from backup cluster
nodes!");
+- } catch (Exception ex) {
+- log.error(ex);
+- }
+- }
+- } catch (java.io.IOException x) {
+- log.error("Unable to read farm deploy file message.", x);
+- }
+- }
+-
+- /**
+- * create factory for all transported war files
+- *
+- * @param msg
+- * @return Factory for all app message (war files)
+- * @throws java.io.FileNotFoundException
+- * @throws java.io.IOException
+- */
+- public synchronized FileMessageFactory getFactory(FileMessage msg)
+- throws java.io.FileNotFoundException, java.io.IOException {
+- File tmpFile = new File(msg.getFileName());
+- File writeToFile = new File(getTempDir(), tmpFile.getName());
+- FileMessageFactory factory = (FileMessageFactory) fileFactories.get(msg
+- .getFileName());
+- if (factory == null) {
+- factory = FileMessageFactory.getInstance(writeToFile, true);
+- fileFactories.put(msg.getFileName(), factory);
+- }
+- return factory;
+- }
+-
+- /**
+- * Remove file (war) from messages)
+- *
+- * @param msg
+- */
+- public void removeFactory(FileMessage msg) {
+- fileFactories.remove(msg.getFileName());
+- }
+-
+- /**
+- * Before the cluster invokes messageReceived the cluster will ask the
+- * receiver to accept or decline the message, In the future, when messages
+- * get big, the accept method will only take a message header
+- *
+- * @param msg
+- * ClusterMessage
+- * @return boolean - returns true to indicate that messageReceived should be
+- * invoked. If false is returned, the messageReceived method will
+- * not be invoked.
+- */
+- public boolean accept(ClusterMessage msg) {
+- return (msg instanceof FileMessage) || (msg instanceof UndeployMessage);
+- }
+-
+- /**
+- * Install a new web application, whose web application archive is at the
+- * specified URL, into this container and all the other members of the
+- * cluster with the specified context path. A context path of "" (the
empty
+- * string) should be used for the root application for this container.
+- * Otherwise, the context path must start with a slash.
+- * <p>
+- * If this application is successfully installed locally, a ContainerEvent
+- * of type <code>INSTALL_EVENT</code> will be sent to all registered
+- * listeners, with the newly created <code>Context</code> as an
argument.
+- *
+- * @param contextPath
+- * The context path to which this application should be installed
+- * (must be unique)
+- * @param war
+- * A URL of type "jar:" that points to a WAR file, or type
+- * "file:" that points to an unpacked directory structure
+- * containing the web application to be installed
+- *
+- * @exception IllegalArgumentException
+- * if the specified context path is malformed (it must be
""
+- * or start with a slash)
+- * @exception IllegalStateException
+- * if the specified context path is already attached to an
+- * existing web application
+- * @exception IOException
+- * if an input/output error was encountered during
+- * installation
+- */
+- public void install(String contextPath, URL war) throws IOException {
+- Member[] members = getCluster().getMembers();
+- Member localMember = getCluster().getLocalMember();
+- FileMessageFactory factory = FileMessageFactory.getInstance(new File(
+- war.getFile()), false);
+- FileMessage msg = new FileMessage(localMember, war.getFile(),
+- contextPath);
+- if(log.isDebugEnabled())
+- log.debug("Send cluster war deployment [ path:"
+- + contextPath + " war: " + war + " ]
started.");
+- msg = factory.readMessage(msg);
+- while (msg != null) {
+- for (int i = 0; i < members.length; i++) {
+- if (log.isDebugEnabled())
+- log.debug("Send cluster war fragment [ path: "
+- + contextPath + " war: " + war + " to: "
+ members[i] + " ]");
+- getCluster().send(msg, members[i]);
+- }
+- msg = factory.readMessage(msg);
+- }
+- if(log.isDebugEnabled())
+- log.debug("Send cluster war deployment [ path: "
+- + contextPath + " war: " + war + " ]
finished.");
+- }
+-
+- /**
+- * Remove an existing web application, attached to the specified context
+- * path. If this application is successfully removed, a ContainerEvent of
+- * type <code>REMOVE_EVENT</code> will be sent to all registered
+- * listeners, with the removed <code>Context</code> as an argument.
+- * Deletes the web application war file and/or directory if they exist in
+- * the Host's appBase.
+- *
+- * @param contextPath
+- * The context path of the application to be removed
+- * @param undeploy
+- * boolean flag to remove web application from server
+- *
+- * @exception IllegalArgumentException
+- * if the specified context path is malformed (it must be
""
+- * or start with a slash)
+- * @exception IllegalArgumentException
+- * if the specified context path does not identify a
+- * currently installed web application
+- * @exception IOException
+- * if an input/output error occurs during removal
+- */
+- public void remove(String contextPath, boolean undeploy) throws IOException {
+- if (log.isInfoEnabled())
+- log.info("Cluster wide remove of web app " + contextPath);
+- Member localMember = getCluster().getLocalMember();
+- UndeployMessage msg = new UndeployMessage(localMember, System
+- .currentTimeMillis(), "Undeploy:" + contextPath +
":"
+- + System.currentTimeMillis(), contextPath, undeploy);
+- if (log.isDebugEnabled())
+- log.debug("Send cluster wide undeployment from "
+- + contextPath );
+- cluster.send(msg);
+- // remove locally
+- if (undeploy) {
+- try {
+- if (!isServiced(contextPath)) {
+- addServiced(contextPath);
+- try {
+- remove(contextPath);
+- } finally {
+- removeServiced(contextPath);
+- }
+- } else
+- log.error("Local remove from " + contextPath
+- + "failed, other manager has app in service!");
+-
+- } catch (Exception ex) {
+- log.error("local remove from " + contextPath + "
failed", ex);
+- }
+- }
+-
+- }
+-
+- /*
+- * Modifcation from watchDir war detected!
+- *
+- * @see org.apache.catalina.ha.deploy.FileChangeListener#fileModified(java.io.File)
+- */
+- public void fileModified(File newWar) {
+- try {
+- File deployWar = new File(getDeployDir(), newWar.getName());
+- copy(newWar, deployWar);
+- String contextName = getContextName(deployWar);
+- if (log.isInfoEnabled())
+- log.info("Installing webapp[" + contextName + "] from
"
+- + deployWar.getAbsolutePath());
+- try {
+- remove(contextName, false);
+- } catch (Exception x) {
+- log.error("No removal", x);
+- }
+- install(contextName, deployWar.toURL());
+- } catch (Exception x) {
+- log.error("Unable to install WAR file", x);
+- }
+- }
+-
+- /*
+- * War remvoe from watchDir
+- *
+- * @see org.apache.catalina.ha.deploy.FileChangeListener#fileRemoved(java.io.File)
+- */
+- public void fileRemoved(File removeWar) {
+- try {
+- String contextName = getContextName(removeWar);
+- if (log.isInfoEnabled())
+- log.info("Removing webapp[" + contextName + "]");
+- remove(contextName, true);
+- } catch (Exception x) {
+- log.error("Unable to remove WAR file", x);
+- }
+- }
+-
+- /**
+- * Create a context path from war
+- * @param war War filename
+- * @return '/filename' or if war name is ROOT.war context name is empty
string ''
+- */
+- protected String getContextName(File war) {
+- String contextName = "/"
+- + war.getName().substring(0,
+- war.getName().lastIndexOf(".war"));
+- if("/ROOT".equals(contextName))
+- contextName= "" ;
+- return contextName ;
+- }
+-
+- /**
+- * Given a context path, get the config file name.
+- */
+- protected String getConfigFile(String path) {
+- String basename = null;
+- if (path.equals("")) {
+- basename = "ROOT";
+- } else {
+- basename = path.substring(1).replace('/', '#');
+- }
+- return (basename);
+- }
+-
+- /**
+- * Given a context path, get the config file name.
+- */
+- protected String getDocBase(String path) {
+- String basename = null;
+- if (path.equals("")) {
+- basename = "ROOT";
+- } else {
+- basename = path.substring(1);
+- }
+- return (basename);
+- }
+-
+- /**
+- * Return a File object representing the "application root" directory for
+- * our associated Host.
+- */
+- protected File getAppBase() {
+-
+- if (appBase != null) {
+- return appBase;
+- }
+-
+- File file = new File(host.getAppBase());
+- if (!file.isAbsolute())
+- file = new File(System.getProperty("catalina.base"), host
+- .getAppBase());
+- try {
+- appBase = file.getCanonicalFile();
+- } catch (IOException e) {
+- appBase = file;
+- }
+- return (appBase);
+-
+- }
+-
+- /**
+- * Invoke the remove method on the deployer.
+- */
+- protected void remove(String path) throws Exception {
+- // TODO Handle remove also work dir content !
+- // Stop the context first to be nicer
+- Context context = (Context) host.findChild(path);
+- if (context != null) {
+- if(log.isDebugEnabled())
+- log.debug("Undeploy local context " +path );
+- ((Lifecycle) context).stop();
+- File war = new File(getAppBase(), getDocBase(path) + ".war");
+- File dir = new File(getAppBase(), getDocBase(path));
+- File xml = new File(configBase, getConfigFile(path) + ".xml");
+- if (war.exists()) {
+- war.delete();
+- } else if (dir.exists()) {
+- undeployDir(dir);
+- } else {
+- xml.delete();
+- }
+- // Perform new deployment and remove internal HostConfig state
+- check(path);
+- }
+-
+- }
+-
+- /**
+- * Delete the specified directory, including all of its contents and
+- * subdirectories recursively.
+- *
+- * @param dir
+- * File object representing the directory to be deleted
+- */
+- protected void undeployDir(File dir) {
+-
+- String files[] = dir.list();
+- if (files == null) {
+- files = new String[0];
+- }
+- for (int i = 0; i < files.length; i++) {
+- File file = new File(dir, files[i]);
+- if (file.isDirectory()) {
+- undeployDir(file);
+- } else {
+- file.delete();
+- }
+- }
+- dir.delete();
+-
+- }
+-
+- /*
+- * Call watcher to check for deploy changes
+- *
+- * @see org.apache.catalina.ha.ClusterDeployer#backgroundProcess()
+- */
+- public void backgroundProcess() {
+- if (started) {
+- count = (count + 1) % processDeployFrequency;
+- if (count == 0 && watchEnabled) {
+- watcher.check();
+- }
+- }
+-
+- }
+-
+- /*--Deployer Operations ------------------------------------*/
+-
+- /**
+- * Invoke the check method on the deployer.
+- */
+- protected void check(String name) throws Exception {
+- String[] params = { name };
+- String[] signature = { "java.lang.String" };
+- mBeanServer.invoke(oname, "check", params, signature);
+- }
+-
+- /**
+- * Invoke the check method on the deployer.
+- */
+- protected boolean isServiced(String name) throws Exception {
+- String[] params = { name };
+- String[] signature = { "java.lang.String" };
+- Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",
+- params, signature);
+- return result.booleanValue();
+- }
+-
+- /**
+- * Invoke the check method on the deployer.
+- */
+- protected void addServiced(String name) throws Exception {
+- String[] params = { name };
+- String[] signature = { "java.lang.String" };
+- mBeanServer.invoke(oname, "addServiced", params, signature);
+- }
+-
+- /**
+- * Invoke the check method on the deployer.
+- */
+- protected void removeServiced(String name) throws Exception {
+- String[] params = { name };
+- String[] signature = { "java.lang.String" };
+- mBeanServer.invoke(oname, "removeServiced", params, signature);
+- }
+-
+- /*--Instance Getters/Setters--------------------------------*/
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- public void setCluster(CatalinaCluster cluster) {
+- this.cluster = cluster;
+- }
+-
+- public boolean equals(Object listener) {
+- return super.equals(listener);
+- }
+-
+- public int hashCode() {
+- return super.hashCode();
+- }
+-
+- public String getDeployDir() {
+- return deployDir;
+- }
+-
+- public void setDeployDir(String deployDir) {
+- this.deployDir = deployDir;
+- }
+-
+- public String getTempDir() {
+- return tempDir;
+- }
+-
+- public void setTempDir(String tempDir) {
+- this.tempDir = tempDir;
+- }
+-
+- public String getWatchDir() {
+- return watchDir;
+- }
+-
+- public void setWatchDir(String watchDir) {
+- this.watchDir = watchDir;
+- }
+-
+- public boolean isWatchEnabled() {
+- return watchEnabled;
+- }
+-
+- public boolean getWatchEnabled() {
+- return watchEnabled;
+- }
+-
+- public void setWatchEnabled(boolean watchEnabled) {
+- this.watchEnabled = watchEnabled;
+- }
+-
+- /**
+- * Return the frequency of watcher checks.
+- */
+- public int getProcessDeployFrequency() {
+-
+- return (this.processDeployFrequency);
+-
+- }
+-
+- /**
+- * Set the watcher checks frequency.
+- *
+- * @param processExpiresFrequency
+- * the new manager checks frequency
+- */
+- public void setProcessDeployFrequency(int processExpiresFrequency) {
+-
+- if (processExpiresFrequency <= 0) {
+- return;
+- }
+- this.processDeployFrequency = processExpiresFrequency;
+- }
+-
+- /**
+- * Copy a file to the specified temp directory.
+- * @param from copy from temp
+- * @param to to host appBase directory
+- * @return true, copy successful
+- */
+- protected boolean copy(File from, File to) {
+- try {
+- if (!to.exists())
+- to.createNewFile();
+- java.io.FileInputStream is = new java.io.FileInputStream(from);
+- java.io.FileOutputStream os = new java.io.FileOutputStream(to,
+- false);
+- byte[] buf = new byte[4096];
+- while (true) {
+- int len = is.read(buf);
+- if (len < 0)
+- break;
+- os.write(buf, 0, len);
+- }
+- is.close();
+- os.close();
+- } catch (IOException e) {
+- log.error("Unable to copy file from:" + from + " to:" +
to, e);
+- return false;
+- }
+- return true;
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/ha/deploy/mbeans-descriptors.xml (working copy)
+@@ -1,27 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<mbeans-descriptors>
+- <mbean
+- name="FarmWarDeployer"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Farm Deployer - Broken"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.deploy.FarmWarDeployer">
+- </mbean>
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/ha/deploy/FileMessage.java
+===================================================================
+--- java/org/apache/catalina/ha/deploy/FileMessage.java (revision 590752)
++++ java/org/apache/catalina/ha/deploy/FileMessage.java (working copy)
+@@ -1,113 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.deploy;
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.ha.ClusterMessageBase;
+-
+-/**
+- * Contains the data for a file being transferred over TCP, this is
+- * essentially a fragment of a file, read and written by the FileMessageFactory
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public class FileMessage extends ClusterMessageBase implements ClusterMessage,
Serializable {
+- private int messageNumber;
+- private byte[] data;
+- private int dataLength;
+-
+- private long totalLength;
+- private long totalNrOfMsgs;
+- private String fileName;
+- private String contextPath;
+-
+- public FileMessage(Member source,
+- String fileName,
+- String contextPath) {
+- this.address=source;
+- this.fileName=fileName;
+- this.contextPath=contextPath;
+- }
+-
+- /*
+- public void writeExternal(ObjectOutput out) throws IOException {
+-
+- }
+-
+- public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
+-
+- }
+- */
+-
+- public int getMessageNumber() {
+- return messageNumber;
+- }
+- public void setMessageNumber(int messageNumber) {
+- this.messageNumber = messageNumber;
+- }
+- public long getTotalNrOfMsgs() {
+- return totalNrOfMsgs;
+- }
+- public void setTotalNrOfMsgs(long totalNrOfMsgs) {
+- this.totalNrOfMsgs = totalNrOfMsgs;
+- }
+- public byte[] getData() {
+- return data;
+- }
+- public void setData(byte[] data, int length) {
+- this.data = data;
+- this.dataLength = length;
+- }
+- public int getDataLength() {
+- return dataLength;
+- }
+- public void setDataLength(int dataLength) {
+- this.dataLength = dataLength;
+- }
+- public long getTotalLength() {
+- return totalLength;
+- }
+- public void setTotalLength(long totalLength) {
+- this.totalLength = totalLength;
+- }
+-
+- public String getUniqueId() {
+- StringBuffer result = new StringBuffer(getFileName());
+- result.append("#-#");
+- result.append(getMessageNumber());
+- result.append("#-#");
+- result.append(System.currentTimeMillis());
+- return result.toString();
+- }
+-
+-
+- public String getFileName() {
+- return fileName;
+- }
+- public void setFileName(String fileName) {
+- this.fileName = fileName;
+- }
+- public String getContextPath() {
+- return contextPath;
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/deploy/UndeployMessage.java
+===================================================================
+--- java/org/apache/catalina/ha/deploy/UndeployMessage.java (revision 590752)
++++ java/org/apache/catalina/ha/deploy/UndeployMessage.java (working copy)
+@@ -1,114 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.deploy;
+-
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.tribes.Member;
+-import java.io.Serializable;
+-public class UndeployMessage implements ClusterMessage,Serializable {
+- private Member address;
+- private long timestamp;
+- private String uniqueId;
+- private String contextPath;
+- private boolean undeploy;
+- private int resend = 0;
+- private int compress = 0;
+-
+- public UndeployMessage() {} //for serialization
+- public UndeployMessage(Member address,
+- long timestamp,
+- String uniqueId,
+- String contextPath,
+- boolean undeploy) {
+- this.address = address;
+- this.timestamp= timestamp;
+- this.undeploy = undeploy;
+- this.uniqueId = uniqueId;
+- this.undeploy = undeploy;
+- this.contextPath = contextPath;
+- }
+-
+- public Member getAddress() {
+- return address;
+- }
+-
+- public void setAddress(Member address) {
+- this.address = address;
+- }
+-
+- public long getTimestamp() {
+- return timestamp;
+- }
+-
+- public void setTimestamp(long timestamp) {
+- this.timestamp = timestamp;
+- }
+-
+- public String getUniqueId() {
+- return uniqueId;
+- }
+-
+- public void setUniqueId(String uniqueId) {
+- this.uniqueId = uniqueId;
+- }
+-
+- public String getContextPath() {
+- return contextPath;
+- }
+-
+- public void setContextPath(String contextPath) {
+- this.contextPath = contextPath;
+- }
+-
+- public boolean getUndeploy() {
+- return undeploy;
+- }
+-
+- public void setUndeploy(boolean undeploy) {
+- this.undeploy = undeploy;
+- }
+- /**
+- * @return Returns the compress.
+- * @since 5.5.10
+- */
+- public int getCompress() {
+- return compress;
+- }
+- /**
+- * @param compress The compress to set.
+- * @since 5.5.10
+- */
+- public void setCompress(int compress) {
+- this.compress = compress;
+- }
+- /**
+- * @return Returns the resend.
+- * @since 5.5.10
+- */
+- public int getResend() {
+- return resend;
+- }
+- /**
+- * @param resend The resend to set.
+- * @since 5.5.10
+- */
+- public void setResend(int resend) {
+- this.resend = resend;
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/deploy/WarWatcher.java
+===================================================================
+--- java/org/apache/catalina/ha/deploy/WarWatcher.java (revision 590752)
++++ java/org/apache/catalina/ha/deploy/WarWatcher.java (working copy)
+@@ -1,239 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.deploy;
+-
+-import java.io.File;
+-import java.util.HashMap;
+-import java.util.Map;
+-import java.util.Iterator;
+-
+-/**
+- * <p>
+- * The <b>WarWatcher </b> watches the deployDir for changes made to the
+- * directory (adding new WAR files->deploy or remove WAR files->undeploy) And
+- * notifies a listener of the changes made
+- * </p>
+- *
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version 1.1
+- */
+-
+-public class WarWatcher {
+-
+- /*--Static Variables----------------------------------------*/
+- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+- .getLog(WarWatcher.class);
+-
+- /*--Instance Variables--------------------------------------*/
+- /**
+- * Directory to watch for war files
+- */
+- protected File watchDir = null;
+-
+- /**
+- * Parent to be notified of changes
+- */
+- protected FileChangeListener listener = null;
+-
+- /**
+- * Currently deployed files
+- */
+- protected Map currentStatus = new HashMap();
+-
+- /*--Constructor---------------------------------------------*/
+-
+- public WarWatcher() {
+- }
+-
+- public WarWatcher(FileChangeListener listener, File watchDir) {
+- this.listener = listener;
+- this.watchDir = watchDir;
+- }
+-
+- /*--Logic---------------------------------------------------*/
+-
+- /**
+- * check for modification and send notifcation to listener
+- */
+- public void check() {
+- if (log.isInfoEnabled())
+- log.info("check cluster wars at " + watchDir);
+- File[] list = watchDir.listFiles(new WarFilter());
+- if (list == null)
+- list = new File[0];
+- //first make sure all the files are listed in our current status
+- for (int i = 0; i < list.length; i++) {
+- addWarInfo(list[i]);
+- }
+-
+- //check all the status codes and update the FarmDeployer
+- for (Iterator i = currentStatus.entrySet().iterator(); i.hasNext();) {
+- Map.Entry entry = (Map.Entry) i.next();
+- WarInfo info = (WarInfo) entry.getValue();
+- int check = info.check();
+- if (check == 1) {
+- listener.fileModified(info.getWar());
+- } else if (check == -1) {
+- listener.fileRemoved(info.getWar());
+- //no need to keep in memory
+- currentStatus.remove(info.getWar());
+- }
+- }
+-
+- }
+-
+- /**
+- * add cluster war to the watcher state
+- * @param warfile
+- */
+- protected void addWarInfo(File warfile) {
+- WarInfo info = (WarInfo) currentStatus.get(warfile.getAbsolutePath());
+- if (info == null) {
+- info = new WarInfo(warfile);
+- info.setLastState(-1); //assume file is non existent
+- currentStatus.put(warfile.getAbsolutePath(), info);
+- }
+- }
+-
+- /**
+- * clear watcher state
+- */
+- public void clear() {
+- currentStatus.clear();
+- }
+-
+- /**
+- * @return Returns the watchDir.
+- */
+- public File getWatchDir() {
+- return watchDir;
+- }
+-
+- /**
+- * @param watchDir
+- * The watchDir to set.
+- */
+- public void setWatchDir(File watchDir) {
+- this.watchDir = watchDir;
+- }
+-
+- /**
+- * @return Returns the listener.
+- */
+- public FileChangeListener getListener() {
+- return listener;
+- }
+-
+- /**
+- * @param listener
+- * The listener to set.
+- */
+- public void setListener(FileChangeListener listener) {
+- this.listener = listener;
+- }
+-
+- /*--Inner classes-------------------------------------------*/
+-
+- /**
+- * File name filter for war files
+- */
+- protected class WarFilter implements java.io.FilenameFilter {
+- public boolean accept(File path, String name) {
+- if (name == null)
+- return false;
+- return name.endsWith(".war");
+- }
+- }
+-
+- /**
+- * File information on existing WAR files
+- */
+- protected class WarInfo {
+- protected File war = null;
+-
+- protected long lastChecked = 0;
+-
+- protected long lastState = 0;
+-
+- public WarInfo(File war) {
+- this.war = war;
+- this.lastChecked = war.lastModified();
+- if (!war.exists())
+- lastState = -1;
+- }
+-
+- public boolean modified() {
+- return war.exists() && war.lastModified() > lastChecked;
+- }
+-
+- public boolean exists() {
+- return war.exists();
+- }
+-
+- /**
+- * Returns 1 if the file has been added/modified, 0 if the file is
+- * unchanged and -1 if the file has been removed
+- *
+- * @return int 1=file added; 0=unchanged; -1=file removed
+- */
+- public int check() {
+- //file unchanged by default
+- int result = 0;
+-
+- if (modified()) {
+- //file has changed - timestamp
+- result = 1;
+- lastState = result;
+- } else if ((!exists()) && (!(lastState == -1))) {
+- //file was removed
+- result = -1;
+- lastState = result;
+- } else if ((lastState == -1) && exists()) {
+- //file was added
+- result = 1;
+- lastState = result;
+- }
+- this.lastChecked = System.currentTimeMillis();
+- return result;
+- }
+-
+- public File getWar() {
+- return war;
+- }
+-
+- public int hashCode() {
+- return war.getAbsolutePath().hashCode();
+- }
+-
+- public boolean equals(Object other) {
+- if (other instanceof WarInfo) {
+- WarInfo wo = (WarInfo) other;
+- return wo.getWar().equals(getWar());
+- } else {
+- return false;
+- }
+- }
+-
+- protected void setLastState(int lastState) {
+- this.lastState = lastState;
+- }
+-
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/ha/deploy/FileChangeListener.java
+===================================================================
+--- java/org/apache/catalina/ha/deploy/FileChangeListener.java (revision 590752)
++++ java/org/apache/catalina/ha/deploy/FileChangeListener.java (working copy)
+@@ -1,24 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.deploy;
+-import java.io.File;
+-
+-public interface FileChangeListener {
+- public void fileModified(File f);
+- public void fileRemoved(File f);
+-}
+Index: java/org/apache/catalina/ha/deploy/FileMessageFactory.java
+===================================================================
+--- java/org/apache/catalina/ha/deploy/FileMessageFactory.java (revision 590752)
++++ java/org/apache/catalina/ha/deploy/FileMessageFactory.java (working copy)
+@@ -1,312 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.deploy;
+-
+-import java.io.File;
+-import java.io.IOException;
+-import java.io.FileInputStream;
+-import java.io.FileOutputStream;
+-import java.io.FileNotFoundException;
+-
+-/**
+- * This factory is used to read files and write files by splitting them up into
+- * smaller messages. So that entire files don't have to be read into memory.
+- * <BR>
+- * The factory can be used as a reader or writer but not both at the same time.
+- * When done reading or writing the factory will close the input or output
+- * streams and mark the factory as closed. It is not possible to use it after
+- * that. <BR>
+- * To force a cleanup, call cleanup() from the calling object. <BR>
+- * This class is not thread safe.
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class FileMessageFactory {
+- /*--Static Variables----------------------------------------*/
+- public static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
+- .getLog(FileMessageFactory.class);
+-
+- /**
+- * The number of bytes that we read from file
+- */
+- public static final int READ_SIZE = 1024 * 10; //10kb
+-
+- /**
+- * The file that we are reading/writing
+- */
+- protected File file = null;
+-
+- /**
+- * True means that we are writing with this factory. False means that we are
+- * reading with this factory
+- */
+- protected boolean openForWrite;
+-
+- /**
+- * Once the factory is used, it can not be reused.
+- */
+- protected boolean closed = false;
+-
+- /**
+- * When openForWrite=false, the input stream is held by this variable
+- */
+- protected FileInputStream in;
+-
+- /**
+- * When openForWrite=true, the output stream is held by this variable
+- */
+- protected FileOutputStream out;
+-
+- /**
+- * The number of messages we have read or written
+- */
+- protected int nrOfMessagesProcessed = 0;
+-
+- /**
+- * The total size of the file
+- */
+- protected long size = 0;
+-
+- /**
+- * The total number of packets that we split this file into
+- */
+- protected long totalNrOfMessages = 0;
+-
+- /**
+- * The bytes that we hold the data in, not thread safe.
+- */
+- protected byte[] data = new byte[READ_SIZE];
+-
+- /**
+- * Private constructor, either instantiates a factory to read or write. <BR>
+- * When openForWrite==true, then a the file, f, will be created and an
+- * output stream is opened to write to it. <BR>
+- * When openForWrite==false, an input stream is opened, the file has to
+- * exist.
+- *
+- * @param f
+- * File - the file to be read/written
+- * @param openForWrite
+- * boolean - true means we are writing to the file, false means
+- * we are reading from the file
+- * @throws FileNotFoundException -
+- * if the file to be read doesn't exist
+- * @throws IOException -
+- * if the system fails to open input/output streams to the file
+- * or if it fails to create the file to be written to.
+- */
+- private FileMessageFactory(File f, boolean openForWrite)
+- throws FileNotFoundException, IOException {
+- this.file = f;
+- this.openForWrite = openForWrite;
+- if (log.isDebugEnabled())
+- log.debug("open file " + f + " write " + openForWrite);
+- if (openForWrite) {
+- if (!file.exists())
+- file.createNewFile();
+- out = new FileOutputStream(f);
+- } else {
+- size = file.length();
+- totalNrOfMessages = (size / READ_SIZE) + 1;
+- in = new FileInputStream(f);
+- }//end if
+-
+- }
+-
+- /**
+- * Creates a factory to read or write from a file. When opening for read,
+- * the readMessage can be invoked, and when opening for write the
+- * writeMessage can be invoked.
+- *
+- * @param f
+- * File - the file to be read or written
+- * @param openForWrite
+- * boolean - true, means we are writing to the file, false means
+- * we are reading from it
+- * @throws FileNotFoundException -
+- * if the file to be read doesn't exist
+- * @throws IOException -
+- * if it fails to create the file that is to be written
+- * @return FileMessageFactory
+- */
+- public static FileMessageFactory getInstance(File f, boolean openForWrite)
+- throws FileNotFoundException, IOException {
+- return new FileMessageFactory(f, openForWrite);
+- }
+-
+- /**
+- * Reads file data into the file message and sets the size, totalLength,
+- * totalNrOfMsgs and the message number <BR>
+- * If EOF is reached, the factory returns null, and closes itself, otherwise
+- * the same message is returned as was passed in. This makes sure that not
+- * more memory is ever used. To remember, neither the file message or the
+- * factory are thread safe. dont hand off the message to one thread and read
+- * the same with another.
+- *
+- * @param f
+- * FileMessage - the message to be populated with file data
+- * @throws IllegalArgumentException -
+- * if the factory is for writing or is closed
+- * @throws IOException -
+- * if a file read exception occurs
+- * @return FileMessage - returns the same message passed in as a parameter,
+- * or null if EOF
+- */
+- public FileMessage readMessage(FileMessage f)
+- throws IllegalArgumentException, IOException {
+- checkState(false);
+- int length = in.read(data);
+- if (length == -1) {
+- cleanup();
+- return null;
+- } else {
+- f.setData(data, length);
+- f.setTotalLength(size);
+- f.setTotalNrOfMsgs(totalNrOfMessages);
+- f.setMessageNumber(++nrOfMessagesProcessed);
+- return f;
+- }//end if
+- }
+-
+- /**
+- * Writes a message to file. If (msg.getMessageNumber() ==
+- * msg.getTotalNrOfMsgs()) the output stream will be closed after writing.
+- *
+- * @param msg
+- * FileMessage - message containing data to be written
+- * @throws IllegalArgumentException -
+- * if the factory is opened for read or closed
+- * @throws IOException -
+- * if a file write error occurs
+- * @return returns true if the file is complete and outputstream is closed,
+- * false otherwise.
+- */
+- public boolean writeMessage(FileMessage msg)
+- throws IllegalArgumentException, IOException {
+- if (!openForWrite)
+- throw new IllegalArgumentException(
+- "Can't write message, this factory is reading.");
+- if (log.isDebugEnabled())
+- log.debug("Message " + msg + " data " + msg.getData()
+- + " data length " + msg.getDataLength() + " out
" + out);
+- if (out != null) {
+- out.write(msg.getData(), 0, msg.getDataLength());
+- nrOfMessagesProcessed++;
+- out.flush();
+- if (msg.getMessageNumber() == msg.getTotalNrOfMsgs()) {
+- out.close();
+- cleanup();
+- return true;
+- }//end if
+- } else {
+- if (log.isWarnEnabled())
+- log.warn("Receive Message again -- Sender ActTimeout to short [
path: "
+- + msg.getContextPath()
+- + " war: "
+- + msg.getFileName()
+- + " data: "
+- + msg.getData()
+- + " data length: " + msg.getDataLength() +
" ]");
+- }
+- return false;
+- }//writeMessage
+-
+- /**
+- * Closes the factory, its streams and sets all its references to null
+- */
+- public void cleanup() {
+- if (in != null)
+- try {
+- in.close();
+- } catch (Exception ignore) {
+- }
+- if (out != null)
+- try {
+- out.close();
+- } catch (Exception ignore) {
+- }
+- in = null;
+- out = null;
+- size = 0;
+- closed = true;
+- data = null;
+- nrOfMessagesProcessed = 0;
+- totalNrOfMessages = 0;
+- }
+-
+- /**
+- * Check to make sure the factory is able to perform the function it is
+- * asked to do. Invoked by readMessage/writeMessage before those methods
+- * proceed.
+- *
+- * @param openForWrite
+- * boolean
+- * @throws IllegalArgumentException
+- */
+- protected void checkState(boolean openForWrite)
+- throws IllegalArgumentException {
+- if (this.openForWrite != openForWrite) {
+- cleanup();
+- if (openForWrite)
+- throw new IllegalArgumentException(
+- "Can't write message, this factory is reading.");
+- else
+- throw new IllegalArgumentException(
+- "Can't read message, this factory is writing.");
+- }
+- if (this.closed) {
+- cleanup();
+- throw new IllegalArgumentException("Factory has been closed.");
+- }
+- }
+-
+- /**
+- * Example usage.
+- *
+- * @param args
+- * String[], args[0] - read from filename, args[1] write to
+- * filename
+- * @throws Exception
+- */
+- public static void main(String[] args) throws Exception {
+-
+- System.out
+- .println("Usage: FileMessageFactory fileToBeRead
fileToBeWritten");
+- System.out
+- .println("Usage: This will make a copy of the file on the local
file system");
+- FileMessageFactory read = getInstance(new File(args[0]), false);
+- FileMessageFactory write = getInstance(new File(args[1]), true);
+- FileMessage msg = new FileMessage(null, args[0], args[0]);
+- msg = read.readMessage(msg);
+- System.out.println("Expecting to write " + msg.getTotalNrOfMsgs()
+- + " messages.");
+- int cnt = 0;
+- while (msg != null) {
+- write.writeMessage(msg);
+- cnt++;
+- msg = read.readMessage(msg);
+- }//while
+- System.out.println("Actually wrote " + cnt + " messages.");
+- }///main
+-
+- public File getFile() {
+- return file;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/ha/CatalinaCluster.java
+===================================================================
+--- java/org/apache/catalina/ha/CatalinaCluster.java (revision 590752)
++++ java/org/apache/catalina/ha/CatalinaCluster.java (working copy)
+@@ -1,130 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha;
+-
+-import java.util.Map;
+-
+-import org.apache.catalina.Cluster;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.Manager;
+-import org.apache.catalina.Valve;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.juli.logging.Log;
+-
+-
+-
+-/**
+- * A <b>CatalinaCluster</b> interface allows to plug in and out the
+- * different cluster implementations
+- *
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public interface CatalinaCluster extends Cluster {
+- // ----------------------------------------------------- Instance Variables
+-
+- /**
+- * Descriptive information about this component implementation.
+- */
+- public String info = "CatalinaCluster/2.0";
+-
+- /**
+- * Start the cluster, the owning container will invoke this
+- * @throws Exception - if failure to start cluster
+- */
+- public void start() throws Exception;
+-
+- /**
+- * Stops the cluster, the owning container will invoke this
+- * @throws LifecycleException
+- */
+- public void stop() throws LifecycleException;
+-
+- /**
+- * Returns the associates logger with this cluster.
+- *
+- * @return Log
+- */
+- public Log getLogger();
+-
+- /**
+- * Sends a message to all the members in the cluster
+- * @param msg ClusterMessage
+- */
+- public void send(ClusterMessage msg);
+-
+- /**
+- * Sends a message to a specific member in the cluster.
+- *
+- * @param msg ClusterMessage
+- * @param dest Member
+- */
+- public void send(ClusterMessage msg, Member dest);
+-
+- /**
+- * Sends a message to a all members at local cluster domain
+- *
+- * @param msg ClusterMessage
+- */
+- public void sendClusterDomain(ClusterMessage msg);
+-
+- /**
+- * Returns that cluster has members.
+- */
+- public boolean hasMembers();
+-
+- /**
+- * Returns all the members currently participating in the cluster.
+- *
+- * @return Member[]
+- */
+- public Member[] getMembers();
+-
+- /**
+- * Return the member that represents this node.
+- *
+- * @return Member
+- */
+- public Member getLocalMember();
+-
+- public void addValve(Valve valve);
+-
+- public void addClusterListener(ClusterListener listener);
+-
+- public void removeClusterListener(ClusterListener listener);
+-
+- public void setClusterDeployer(ClusterDeployer deployer);
+-
+- public ClusterDeployer getClusterDeployer();
+-
+- /**
+- * @return The map of managers
+- */
+- public Map getManagers();
+-
+- public Manager getManager(String name);
+- public String getManagerName(String name, Manager manager);
+- public Valve[] getValves();
+-
+- public void setChannel(Channel channel);
+- public Channel getChannel();
+-
+-
+-}
+Index: java/org/apache/catalina/ha/package.html
+===================================================================
+--- java/org/apache/catalina/ha/package.html (revision 590752)
++++ java/org/apache/catalina/ha/package.html (working copy)
+@@ -1,23 +0,0 @@
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<body>
+-
+-<p>This package contains code for Clustering, the base class
+-of a Cluster is <code>org.apache.catalina.Cluster</code> implementations
+-of this class is done when implementing a new Cluster protocol</p>
+-
+-</body>
+Index: java/org/apache/catalina/ha/ClusterValve.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterValve.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterValve.java (working copy)
+@@ -1,40 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha;
+-
+-import org.apache.catalina.Valve;
+-
+-/**
+- * Cluster valves are a simple extension to the Tomcat valve architecture
+- * with a small addition of being able to reference the cluster component in the
container it sits in.
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$, $Date$
+- */
+-public interface ClusterValve extends Valve{
+- /**
+- * Returns the cluster the cluster deployer is associated with
+- * @return CatalinaCluster
+- */
+- public CatalinaCluster getCluster();
+-
+- /**
+- * Associates the cluster deployer with a cluster
+- * @param cluster CatalinaCluster
+- */
+- public void setCluster(CatalinaCluster cluster);
+-}
+Index: java/org/apache/catalina/ha/LocalStrings.properties
+===================================================================
+--- java/org/apache/catalina/ha/LocalStrings.properties (revision 590752)
++++ java/org/apache/catalina/ha/LocalStrings.properties (working copy)
+@@ -1,16 +0,0 @@
+-# Licensed to the Apache Software Foundation (ASF) under one or more
+-# contributor license agreements. See the NOTICE file distributed with
+-# this work for additional information regarding copyright ownership.
+-# The ASF licenses this file to You under the Apache License, Version 2.0
+-# (the "License"); you may not use this file except in compliance with
+-# the License. You may obtain a copy of the License at
+-#
+-#
http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS,
+-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-# See the License for the specific language governing permissions and
+-# limitations under the License.
+-
+-cluster.mbean.register.already=MBean {0} already registered!
+Index: java/org/apache/catalina/ha/ClusterMessageBase.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterMessageBase.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterMessageBase.java (working copy)
+@@ -1,76 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha;
+-
+-import org.apache.catalina.tribes.Member;
+-
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class ClusterMessageBase implements ClusterMessage {
+-
+- protected transient Member address;
+- private String uniqueId;
+- private long timestamp;
+- public ClusterMessageBase() {
+- }
+-
+- /**
+- * getAddress
+- *
+- * @return Member
+- * @todo Implement this org.apache.catalina.ha.ClusterMessage method
+- */
+- public Member getAddress() {
+- return address;
+- }
+-
+- public String getUniqueId() {
+- return uniqueId;
+- }
+-
+- public long getTimestamp() {
+- return timestamp;
+- }
+-
+- /**
+- * setAddress
+- *
+- * @param member Member
+- * @todo Implement this org.apache.catalina.ha.ClusterMessage method
+- */
+- public void setAddress(Member member) {
+- this.address = member;
+- }
+-
+- public void setUniqueId(String uniqueId) {
+- this.uniqueId = uniqueId;
+- }
+-
+- public void setTimestamp(long timestamp) {
+- this.timestamp = timestamp;
+- }
+-}
+Index: java/org/apache/catalina/ha/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/ha/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/ha/mbeans-descriptors.xml (working copy)
+@@ -1,106 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<mbeans-descriptors>
+-
+- <mbean name="SimpleTcpCluster"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Tcp Cluster implementation"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.tcp.SimpleTcpCluster">
+-
+- </mbean>
+-
+-
+- <mbean name="SimpleTcpReplicationManager"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Clustered implementation of the Manager interface"
+- domain="Catalina"
+- group="Manager"
+-
type="org.apache.catalina.ha.tcp.SimpleTcpReplicationManager">
+-
+- <attribute name="algorithm"
+- description="The message digest algorithm to be used when generating
+- session identifiers"
+- type="java.lang.String"/>
+-
+- <attribute name="checkInterval"
+- description="The interval (in seconds) between checks for expired
+- sessions"
+- type="int"/>
+-
+- <attribute name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- <attribute name="distributable"
+- description="The distributable flag for Sessions created by this
+- Manager"
+- type="boolean"/>
+-
+- <attribute name="entropy"
+- description="A String initialization parameter used to increase the
+- entropy of the initialization of our random number
+- generator"
+- type="java.lang.String"/>
+-
+- <attribute name="managedResource"
+- description="The managed resource this MBean is associated with"
+- type="java.lang.Object"/>
+-
+- <attribute name="maxActiveSessions"
+- description="The maximum number of active Sessions allowed, or -1
+- for no limit"
+- type="int"/>
+-
+- <attribute name="maxInactiveInterval"
+- description="The default maximum inactive interval for Sessions
+- created by this Manager"
+- type="int"/>
+-
+- <attribute name="name"
+- description="The descriptive name of this Manager implementation
+- (for logging)"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- </mbean>
+-
+-
+-
+-<mbean name="ReplicationValve"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Valve for simple tcp replication"
+- domain="Catalina"
+- group="Valve"
+- type="org.apache.catalina.ha.tcp.ReplicationValve">
+-
+- <attribute name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- <attribute name="debug"
+- description="The debugging detail level for this component"
+- type="int"/>
+-
+- </mbean>
+-
+-
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/ha/ClusterSession.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterSession.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterSession.java (working copy)
+@@ -1,40 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha;
+-
+-import org.apache.catalina.Session;
+-import javax.servlet.http.HttpSession;
+-
+-public interface ClusterSession extends Session, HttpSession {
+- /**
+- * returns true if this session is the primary session, if that is the
+- * case, the manager can expire it upon timeout.
+- * @return True if this session is primary
+- */
+- public boolean isPrimarySession();
+-
+- /**
+- * Sets whether this is the primary session or not.
+- * @param primarySession Flag value
+- */
+- public void setPrimarySession(boolean primarySession);
+-
+-
+-
+-}
+Index: java/org/apache/catalina/ha/ClusterRuleSet.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterRuleSet.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterRuleSet.java (working copy)
+@@ -1,195 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha;
+-
+-
+-import org.apache.tomcat.util.digester.Digester;
+-import org.apache.tomcat.util.digester.RuleSetBase;
+-
+-
+-/**
+- * <p><strong>RuleSet</strong> for processing the contents of a
+- * Cluster definition element. </p>
+- *
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-
+-public class ClusterRuleSet extends RuleSetBase {
+-
+-
+- // ----------------------------------------------------- Instance Variables
+-
+-
+- /**
+- * The matching pattern prefix to use for recognizing our elements.
+- */
+- protected String prefix = null;
+-
+-
+- // ------------------------------------------------------------ Constructor
+-
+-
+- /**
+- * Construct an instance of this <code>RuleSet</code> with the default
+- * matching pattern prefix.
+- */
+- public ClusterRuleSet() {
+-
+- this("");
+-
+- }
+-
+-
+- /**
+- * Construct an instance of this <code>RuleSet</code> with the
specified
+- * matching pattern prefix.
+- *
+- * @param prefix Prefix for matching pattern rules (including the
+- * trailing slash character)
+- */
+- public ClusterRuleSet(String prefix) {
+- super();
+- this.namespaceURI = null;
+- this.prefix = prefix;
+- }
+-
+-
+- // --------------------------------------------------------- Public Methods
+-
+-
+- /**
+- * <p>Add the set of Rule instances defined in this RuleSet to the
+- * specified <code>Digester</code> instance, associating them with
+- * our namespace URI (if any). This method should only be called
+- * by a Digester instance.</p>
+- *
+- * @param digester Digester instance to which the new Rule instances
+- * should be added.
+- */
+- public void addRuleInstances(Digester digester) {
+- //Cluster configuration start
+- digester.addObjectCreate(prefix + "Manager",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(prefix + "Manager");
+- digester.addSetNext(prefix + "Manager",
+- "setManagerTemplate",
+- "org.apache.catalina.ha.ClusterManager");
+-
+-
+-
+- digester.addObjectCreate(prefix + "Channel",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(prefix + "Channel");
+- digester.addSetNext(prefix + "Channel",
+- "setChannel",
+- "org.apache.catalina.tribes.Channel");
+-
+-
+- String channelPrefix = prefix + "Channel/";
+- { //channel properties
+- digester.addObjectCreate(channelPrefix + "Membership",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(channelPrefix + "Membership");
+- digester.addSetNext(channelPrefix + "Membership",
+- "setMembershipService",
+-
"org.apache.catalina.tribes.MembershipService");
+-
+- digester.addObjectCreate(channelPrefix + "Sender",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(channelPrefix + "Sender");
+- digester.addSetNext(channelPrefix + "Sender",
+- "setChannelSender",
+- "org.apache.catalina.tribes.ChannelSender");
+-
+- digester.addObjectCreate(channelPrefix + "Sender/Transport",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(channelPrefix + "Sender/Transport");
+- digester.addSetNext(channelPrefix + "Sender/Transport",
+- "setTransport",
+-
"org.apache.catalina.tribes.transport.MultiPointSender");
+-
+-
+- digester.addObjectCreate(channelPrefix + "Receiver",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(channelPrefix + "Receiver");
+- digester.addSetNext(channelPrefix + "Receiver",
+- "setChannelReceiver",
+-
"org.apache.catalina.tribes.ChannelReceiver");
+-
+- digester.addObjectCreate(channelPrefix + "Interceptor",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(channelPrefix + "Interceptor");
+- digester.addSetNext(channelPrefix + "Interceptor",
+- "addInterceptor",
+-
"org.apache.catalina.tribes.ChannelInterceptor");
+-
+-
+- digester.addObjectCreate(channelPrefix + "Interceptor/Member",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(channelPrefix + "Interceptor/Member");
+- digester.addSetNext(channelPrefix + "Interceptor/Member",
+- "addStaticMember",
+- "org.apache.catalina.tribes.Member");
+- }
+-
+- digester.addObjectCreate(prefix + "Valve",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(prefix + "Valve");
+- digester.addSetNext(prefix + "Valve",
+- "addValve",
+- "org.apache.catalina.Valve");
+-
+- digester.addObjectCreate(prefix + "Deployer",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(prefix + "Deployer");
+- digester.addSetNext(prefix + "Deployer",
+- "setClusterDeployer",
+- "org.apache.catalina.ha.ClusterDeployer");
+-
+- digester.addObjectCreate(prefix + "Listener",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(prefix + "Listener");
+- digester.addSetNext(prefix + "Listener",
+- "addLifecycleListener",
+- "org.apache.catalina.LifecycleListener");
+-
+- digester.addObjectCreate(prefix + "ClusterListener",
+- null, // MUST be specified in the element
+- "className");
+- digester.addSetProperties(prefix + "ClusterListener");
+- digester.addSetNext(prefix + "ClusterListener",
+- "addClusterListener",
+- "org.apache.catalina.ha.ClusterListener");
+- //Cluster configuration end
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/ClusterDeployer.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterDeployer.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterDeployer.java (working copy)
+@@ -1,121 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha;
+-
+-/**
+- * A <b>ClusterDeployer</b> interface allows to plug in and out the
+- * different deployment implementations
+- *
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-import org.apache.catalina.LifecycleException;
+-import java.io.IOException;
+-import java.net.URL;
+-import org.apache.catalina.tribes.ChannelListener;
+-
+-public interface ClusterDeployer extends ChannelListener {
+- /**
+- * Descriptive information about this component implementation.
+- */
+- public String info = "ClusterDeployer/1.0";
+- /**
+- * Start the cluster deployer, the owning container will invoke this
+- * @throws Exception - if failure to start cluster
+- */
+- public void start() throws Exception;
+-
+- /**
+- * Stops the cluster deployer, the owning container will invoke this
+- * @throws LifecycleException
+- */
+- public void stop() throws LifecycleException;
+-
+- /**
+- * Sets the deployer for this cluster deployer to use.
+- * @param deployer Deployer
+- */
+- // FIXME
+- //public void setDeployer(Deployer deployer);
+-
+- /**
+- * Install a new web application, whose web application archive is at the
+- * specified URL, into this container and all the other
+- * members of the cluster with the specified context path.
+- * A context path of "" (the empty string) should be used for the root
+- * application for this container. Otherwise, the context path must
+- * start with a slash.
+- * <p>
+- * If this application is successfully installed locally,
+- * a ContainerEvent of type
+- * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
+- * with the newly created <code>Context</code> as an argument.
+- *
+- * @param contextPath The context path to which this application should
+- * be installed (must be unique)
+- * @param war A URL of type "jar:" that points to a WAR file, or type
+- * "file:" that points to an unpacked directory structure containing
+- * the web application to be installed
+- *
+- * @exception IllegalArgumentException if the specified context path
+- * is malformed (it must be "" or start with a slash)
+- * @exception IllegalStateException if the specified context path
+- * is already attached to an existing web application
+- * @exception IOException if an input/output error was encountered
+- * during installation
+- */
+- public void install(String contextPath, URL war) throws IOException;
+-
+- /**
+- * Remove an existing web application, attached to the specified context
+- * path. If this application is successfully removed, a
+- * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
+- * registered listeners, with the removed <code>Context</code> as
+- * an argument. Deletes the web application war file and/or directory
+- * if they exist in the Host's appBase.
+- *
+- * @param contextPath The context path of the application to be removed
+- * @param undeploy boolean flag to remove web application from server
+- *
+- * @exception IllegalArgumentException if the specified context path
+- * is malformed (it must be "" or start with a slash)
+- * @exception IllegalArgumentException if the specified context path does
+- * not identify a currently installed web application
+- * @exception IOException if an input/output error occurs during
+- * removal
+- */
+- public void remove(String contextPath, boolean undeploy) throws IOException;
+-
+- /**
+- * call from container Background Process
+- */
+- public void backgroundProcess();
+-
+- /**
+- * Returns the cluster the cluster deployer is associated with
+- * @return CatalinaCluster
+- */
+- public CatalinaCluster getCluster();
+-
+- /**
+- * Associates the cluster deployer with a cluster
+- * @param cluster CatalinaCluster
+- */
+- public void setCluster(CatalinaCluster cluster);
+-
+-}
+Index: java/org/apache/catalina/ha/ClusterMessage.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterMessage.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterMessage.java (working copy)
+@@ -1,34 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.ha;
+-
+-import java.io.Serializable;
+-import org.apache.catalina.tribes.Member;
+-
+-
+-/**
+- * @author Filip Hanik
+- *
+- */
+-public interface ClusterMessage extends Serializable {
+- public Member getAddress();
+- public void setAddress(Member member);
+- public String getUniqueId();
+- public void setUniqueId(String id);
+- public long getTimestamp();
+- public void setTimestamp(long timestamp);
+-}
+Index: java/org/apache/catalina/ha/ClusterListener.java
+===================================================================
+--- java/org/apache/catalina/ha/ClusterListener.java (revision 590752)
++++ java/org/apache/catalina/ha/ClusterListener.java (working copy)
+@@ -1,114 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha;
+-
+-
+-
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.util.StringManager;
+-
+-
+-/**
+- * Receive SessionID cluster change from other backup node after primary session
+- * node is failed.
+- *
+- * @author Peter Rossbach
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- */
+-public abstract class ClusterListener implements ChannelListener {
+-
+- public static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ClusterListener.class);
+-
+-
+- //--Instance Variables--------------------------------------
+-
+- /**
+- * The string manager for this package.
+- */
+- protected StringManager sm = StringManager.getManager(Constants.Package);
+-
+- protected CatalinaCluster cluster = null;
+-
+- //--Constructor---------------------------------------------
+-
+- public ClusterListener() {
+- }
+-
+- //--Instance Getters/Setters--------------------------------
+-
+- public CatalinaCluster getCluster() {
+- return cluster;
+- }
+-
+- public void setCluster(CatalinaCluster cluster) {
+- if (log.isDebugEnabled()) {
+- if (cluster != null)
+- log.debug("add ClusterListener " + this.toString() + " to
cluster" + cluster);
+- else
+- log.debug("remove ClusterListener " + this.toString() + "
from cluster");
+- }
+- this.cluster = cluster;
+- }
+-
+- public boolean equals(Object listener) {
+- return super.equals(listener);
+- }
+-
+- public int hashCode() {
+- return super.hashCode();
+- }
+-
+- //--Logic---------------------------------------------------
+-
+- public final void messageReceived(Serializable msg, Member member) {
+- if ( msg instanceof ClusterMessage ) messageReceived((ClusterMessage)msg);
+- }
+- public final boolean accept(Serializable msg, Member member) {
+- if ( msg instanceof ClusterMessage ) return true;
+- return false;
+- }
+-
+-
+-
+- /**
+- * Callback from the cluster, when a message is received, The cluster will
+- * broadcast it invoking the messageReceived on the receiver.
+- *
+- * @param msg
+- * ClusterMessage - the message received from the cluster
+- */
+- public abstract void messageReceived(ClusterMessage msg) ;
+-
+-
+- /**
+- * Accept only SessionIDMessages
+- *
+- * @param msg
+- * ClusterMessage
+- * @return boolean - returns true to indicate that messageReceived should be
+- * invoked. If false is returned, the messageReceived method will
+- * not be invoked.
+- */
+- public abstract boolean accept(ClusterMessage msg) ;
+-
+-}
+Index: java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
+===================================================================
+--- java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java (revision 590752)
++++ java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java (working copy)
+@@ -1,447 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.ha.authenticator;
+-
+-
+-import java.security.Principal;
+-
+-import org.apache.catalina.Container;
+-import org.apache.catalina.Cluster;
+-import org.apache.catalina.Engine;
+-import org.apache.catalina.Host;
+-import org.apache.catalina.LifecycleException;
+-import org.apache.catalina.Manager;
+-import org.apache.catalina.Session;
+-import org.apache.catalina.authenticator.SingleSignOn;
+-import org.apache.catalina.ha.CatalinaCluster;
+-import org.apache.catalina.ha.ClusterManager;
+-
+-
+-
+-/**
+- * A <strong>Valve</strong> that supports a "single sign on" user
experience on
+- * each nodes of a cluster, where the security identity of a user who successfully
+- * authenticates to one web application is propogated to other web applications and
+- * to other nodes cluster in the same security domain. For successful use, the
following
+- * requirements must be met:
+- * <ul>
+- * <li>This Valve must be configured on the Container that represents a
+- * virtual host (typically an implementation of
<code>Host</code>).</li>
+- * <li>The <code>Realm</code> that contains the shared user and role
+- * information must be configured on the same Container (or a higher
+- * one), and not overridden at the web application level.</li>
+- * <li>The web applications themselves must use one of the standard
+- * Authenticators found in the
+- * <code>org.apache.catalina.authenticator</code> package.</li>
+- * </ul>
+- *
+- * @author Fabien Carrion
+- */
+-
+-public class ClusterSingleSignOn
+- extends SingleSignOn {
+-
+-
+- // ----------------------------------------------------- Instance Variables
+-
+-
+- /**
+- * Descriptive information about this Valve implementation.
+- */
+- protected static String info =
+- "org.apache.catalina.cluster.authenticator.ClusterSingleSignOn";
+-
+- protected int messageNumber = 0;
+-
+- private ClusterSingleSignOnListener clusterSSOListener = null;
+-
+-
+- // ------------------------------------------------------------- Properties
+-
+- private CatalinaCluster cluster = null;
+-
+-
+-
+- /**
+- * Return descriptive information about this Valve implementation.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- public CatalinaCluster getCluster() {
+-
+- return cluster;
+-
+- }
+-
+- public void setCluster(CatalinaCluster cluster) {
+-
+- this.cluster = cluster;
+-
+- }
+-
+-
+- // ------------------------------------------------------ Lifecycle Methods
+-
+-
+- /**
+- * 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.
+- *
+- * @exception LifecycleException if this component detects a fatal error
+- * that prevents this component from being used
+- */
+- public void start() throws LifecycleException {
+-
+- super.start();
+-
+- clusterSSOListener = new ClusterSingleSignOnListener();
+- clusterSSOListener.setClusterSSO(this);
+-
+- // Load the cluster component, if any
+- try {
+- //the channel is already running
+- Cluster cluster = getCluster();
+- // stop remove cluster binding
+- if(cluster == null) {
+- Container host = getContainer();
+- if(host != null && host instanceof Host) {
+- cluster = host.getCluster();
+- if(cluster != null && cluster instanceof CatalinaCluster) {
+- setCluster((CatalinaCluster) cluster);
+- getCluster().addClusterListener(clusterSSOListener);
+- } else {
+- Container engine = host.getParent();
+- if(engine != null && engine instanceof Engine) {
+- cluster = engine.getCluster();
+- if(cluster != null && cluster instanceof CatalinaCluster) {
+- setCluster((CatalinaCluster) cluster);
+- getCluster().addClusterListener(clusterSSOListener);
+- }
+- } else {
+- cluster = null;
+- }
+- }
+- }
+- }
+- if (cluster == null) {
+- throw new LifecycleException
+- ("There is no Cluster for ClusterSingleSignOn");
+- }
+-
+- } catch (Throwable t) {
+- throw new LifecycleException
+- ("ClusterSingleSignOn exception during clusterLoad " + t);
+- }
+-
+- }
+-
+-
+- /**
+- * 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.
+- *
+- * @exception LifecycleException if this component detects a fatal error
+- * that needs to be reported
+- */
+- public void stop() throws LifecycleException {
+-
+- super.stop();
+-
+- if (getCluster() != null && getCluster() instanceof CatalinaCluster) {
+- getCluster().removeClusterListener(clusterSSOListener);
+- }
+-
+- }
+-
+-
+- // --------------------------------------------------------- Public Methods
+-
+-
+- /**
+- * Return a String rendering of this object.
+- */
+- public String toString() {
+-
+- StringBuffer sb = new StringBuffer("ClusterSingleSignOn[");
+- if (container == null )
+- sb.append("Container is null");
+- else
+- sb.append(container.getName());
+- sb.append("]");
+- return (sb.toString());
+-
+- }
+-
+-
+- // ------------------------------------------------------ Protected Methods
+-
+-
+- /**
+- * Notify the cluster of the addition of a Session to
+- * an SSO session and associate the specified single
+- * sign on identifier with the specified Session on the
+- * local node.
+- *
+- * @param ssoId Single sign on identifier
+- * @param session Session to be associated
+- */
+- protected void associate(String ssoId, Session session) {
+-
+- if (cluster != null) {
+- messageNumber++;
+- SingleSignOnMessage msg =
+- new SingleSignOnMessage(cluster.getLocalMember(),
+- ssoId, session.getId());
+- Manager mgr = session.getManager();
+- if ((mgr != null) && (mgr instanceof ClusterManager))
+- msg.setContextName(((ClusterManager) mgr).getName());
+-
+- msg.setAction(SingleSignOnMessage.ADD_SESSION);
+-
+- cluster.sendClusterDomain(msg);
+-
+- if (containerLog.isDebugEnabled())
+- containerLog.debug("SingleSignOnMessage Send with action "
+- + msg.getAction());
+- }
+-
+- associateLocal(ssoId, session);
+-
+- }
+-
+- protected void associateLocal(String ssoId, Session session) {
+-
+- super.associate(ssoId, session);
+-
+- }
+-
+- /**
+- * Notify the cluster of the removal of a Session from an
+- * SSO session and deregister the specified session. If it is the last
+- * session, then also get rid of the single sign on identifier on the
+- * local node.
+- *
+- * @param ssoId Single sign on identifier
+- * @param session Session to be deregistered
+- */
+- protected void deregister(String ssoId, Session session) {
+-
+- if (cluster != null) {
+- messageNumber++;
+- SingleSignOnMessage msg =
+- new SingleSignOnMessage(cluster.getLocalMember(),
+- ssoId, session.getId());
+- Manager mgr = session.getManager();
+- if ((mgr != null) && (mgr instanceof ClusterManager))
+- msg.setContextName(((ClusterManager) mgr).getName());
+-
+- msg.setAction(SingleSignOnMessage.DEREGISTER_SESSION);
+-
+- cluster.sendClusterDomain(msg);
+- if (containerLog.isDebugEnabled())
+- containerLog.debug("SingleSignOnMessage Send with action "
+- + msg.getAction());
+- }
+-
+- deregisterLocal(ssoId, session);
+-
+- }
+-
+- protected void deregisterLocal(String ssoId, Session session) {
+-
+- super.deregister(ssoId, session);
+-
+- }
+-
+- /**
+- * Notifies the cluster that a single sign on session
+- * has been terminated due to a user logout, deregister
+- * the specified single sign on identifier, and invalidate
+- * any associated sessions on the local node.
+- *
+- * @param ssoId Single sign on identifier to deregister
+- */
+- protected void deregister(String ssoId) {
+-
+- if (cluster != null) {
+- messageNumber++;
+- SingleSignOnMessage msg =
+- new SingleSignOnMessage(cluster.getLocalMember(),
+- ssoId, null);
+- msg.setAction(SingleSignOnMessage.LOGOUT_SESSION);
+-
+- cluster.sendClusterDomain(msg);
+- if (containerLog.isDebugEnabled())
+- containerLog.debug("SingleSignOnMessage Send with action "
+- + msg.getAction());
+- }
+-
+- deregisterLocal(ssoId);
+-
+- }
+-
+- protected void deregisterLocal(String ssoId) {
+-
+- super.deregister(ssoId);
+-
+- }
+-
+- /**
+- * Notifies the cluster of the creation of a new SSO entry
+- * and register the specified Principal as being associated
+- * with the specified value for the single sign on identifier.
+- *
+- * @param ssoId Single sign on identifier to register
+- * @param principal Associated user principal that is identified
+- * @param authType Authentication type used to authenticate this
+- * user principal
+- * @param username Username used to authenticate this user
+- * @param password Password used to authenticate this user
+- */
+- protected void register(String ssoId, Principal principal, String authType,
+- String username, String password) {
+-
+- if (cluster != null) {
+- messageNumber++;
+- SingleSignOnMessage msg =
+- new SingleSignOnMessage(cluster.getLocalMember(),
+- ssoId, null);
+- msg.setAction(SingleSignOnMessage.REGISTER_SESSION);
+- msg.setAuthType(authType);
+- msg.setUsername(username);
+- msg.setPassword(password);
+-
+- cluster.sendClusterDomain(msg);
+- if (containerLog.isDebugEnabled())
+- containerLog.debug("SingleSignOnMessage Send with action "
+- + msg.getAction());
+- }
+-
+- registerLocal(ssoId, principal, authType, username, password);
+-
+- }
+-
+- protected void registerLocal(String ssoId, Principal principal, String authType,
+- String username, String password) {
+-
+- super.register(ssoId, principal, authType, username, password);
+-
+- }
+-
+-
+- /**
+- * Notifies the cluster of an update of the security credentials
+- * associated with an SSO session. Updates any
<code>SingleSignOnEntry</code>
+- * found under key <code>ssoId</code> with the given authentication
data.
+- * <p>
+- * The purpose of this method is to allow an SSO entry that was
+- * established without a username/password combination (i.e. established
+- * following DIGEST or CLIENT-CERT authentication) to be updated with
+- * a username and password if one becomes available through a subsequent
+- * BASIC or FORM authentication. The SSO entry will then be usable for
+- * reauthentication.
+- * <p>
+- * <b>NOTE:</b> Only updates the SSO entry if a call to
+- * <code>SingleSignOnEntry.getCanReauthenticate()</code> returns
+- * <code>false</code>; otherwise, it is assumed that the SSO entry
already
+- * has sufficient information to allow reauthentication and that no update
+- * is needed.
+- *
+- * @param ssoId identifier of Single sign to be updated
+- * @param principal the <code>Principal</code> returned by the latest
+- * call to <code>Realm.authenticate</code>.
+- * @param authType the type of authenticator used (BASIC, CLIENT-CERT,
+- * DIGEST or FORM)
+- * @param username the username (if any) used for the authentication
+- * @param password the password (if any) used for the authentication
+- */
+- protected void update(String ssoId, Principal principal, String authType,
+- String username, String password) {
+-
+- if (cluster != null) {
+- messageNumber++;
+- SingleSignOnMessage msg =
+- new SingleSignOnMessage(cluster.getLocalMember(),
+- ssoId, null);
+- msg.setAction(SingleSignOnMessage.UPDATE_SESSION);
+- msg.setAuthType(authType);
+- msg.setUsername(username);
+- msg.setPassword(password);
+-
+- cluster.sendClusterDomain(msg);
+- if (containerLog.isDebugEnabled())
+- containerLog.debug("SingleSignOnMessage Send with action "
+- + msg.getAction());
+- }
+-
+- updateLocal(ssoId, principal, authType, username, password);
+-
+- }
+-
+- protected void updateLocal(String ssoId, Principal principal, String authType,
+- String username, String password) {
+-
+- super.update(ssoId, principal, authType, username, password);
+-
+- }
+-
+-
+- /**
+- * Remove a single Session from a SingleSignOn and notify the cluster
+- * of the removal. Called when a session is timed out and no longer active.
+- *
+- * @param ssoId Single sign on identifier from which to remove the session.
+- * @param session the session to be removed.
+- */
+- protected void removeSession(String ssoId, Session session) {
+-
+- if (cluster != null) {
+- messageNumber++;
+- SingleSignOnMessage msg =
+- new SingleSignOnMessage(cluster.getLocalMember(),
+- ssoId, session.getId());
+-
+- Manager mgr = session.getManager();
+- if ((mgr != null) && (mgr instanceof ClusterManager))
+- msg.setContextName(((ClusterManager) mgr).getName());
+-
+- msg.setAction(SingleSignOnMessage.REMOVE_SESSION);
+-
+- cluster.sendClusterDomain(msg);
+- if (containerLog.isDebugEnabled())
+- containerLog.debug("SingleSignOnMessage Send with action "
+- + msg.getAction());
+- }
+-
+- removeSessionLocal(ssoId, session);
+- }
+-
+- protected void removeSessionLocal(String ssoId, Session session) {
+-
+- super.removeSession(ssoId, session);
+-
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml (working copy)
+@@ -1,41 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<mbeans-descriptors>
+-
+- <mbean name="ClusterSingleSignOn"
+- description="A Valve that supports a 'single signon' user
experience on a whole cluster"
+- domain="Catalina"
+- group="Valve"
+-
type="org.apache.catalina.cluster.authenticator.ClusterSingleSignOn">
+-
+- <attribute name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- <attribute name="requireReauthentication"
+- description="Should we attempt to reauthenticate each request
against the security Realm?"
+- type="boolean"/>
+-
+- <attribute name="cookieDomain"
+- description="(Optiona) Domain to be used by sso cookies"
+- type="java.lang.String" />
+-
+- </mbean>
+-
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/ha/authenticator/SingleSignOnMessage.java
+===================================================================
+--- java/org/apache/catalina/ha/authenticator/SingleSignOnMessage.java (revision 590752)
++++ java/org/apache/catalina/ha/authenticator/SingleSignOnMessage.java (working copy)
+@@ -1,188 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.authenticator;
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * Contains the SingleSignOn data, read and written by the ClusterSingleSignOn
+- * @author Fabien Carrion
+- */
+-
+-public class SingleSignOnMessage implements ClusterMessage, Serializable {
+-
+- public static final int ADD_SESSION = 1;
+- public static final int DEREGISTER_SESSION = 2;
+- public static final int LOGOUT_SESSION = 3;
+- public static final int REGISTER_SESSION = 4;
+- public static final int UPDATE_SESSION = 5;
+- public static final int REMOVE_SESSION = 6;
+-
+- private int action = -1;
+- private String ssoId = null;
+- private String ctxname = null;
+- private String sessionId = null;
+- private String authType = null;
+- private String password = null;
+- private String username = null;
+-
+- private Member address = null;
+- private long timestamp = 0;
+- private String uniqueId = null;
+-
+- public SingleSignOnMessage(Member source,
+- String ssoId,
+- String sessionId) {
+- this.address = source;
+- this.ssoId = ssoId;
+- this.sessionId = sessionId;
+- }
+-
+- /**
+- * Get the address that this message originated from. This would be set
+- * if the message was being relayed from a host other than the one
+- * that originally sent it.
+- */
+- public Member getAddress() {
+- return address;
+- }
+-
+- /**
+- * Called by the cluster before sending it to the other
+- * nodes.
+- *
+- * @param member Member
+- */
+- public void setAddress(Member member) {
+- this.address = member;
+- }
+-
+- /**
+- * Timestamp message.
+- *
+- * @return long
+- */
+- public long getTimestamp() {
+- return timestamp;
+- }
+-
+- /**
+- * Called by the cluster before sending out
+- * the message.
+- *
+- * @param timestamp The timestamp
+- */
+- public void setTimestamp(long timestamp) {
+- this.timestamp = timestamp;
+- }
+-
+- /**
+- * Each message must have a unique ID, in case of using async replication,
+- * and a smart queue, this id is used to replace messages not yet sent.
+- *
+- * @return String
+- */
+- public String getUniqueId() {
+- if (this.uniqueId != null)
+- return this.uniqueId;
+- StringBuffer result = new StringBuffer(getSsoId());
+- result.append("#-#");
+- result.append(System.currentTimeMillis());
+- return result.toString();
+- }
+-
+- public void setUniqueId(String uniqueId) {
+- this.uniqueId = uniqueId;
+- }
+-
+- public int getAction() {
+- return action;
+- }
+-
+- public void setAction(int action) {
+- this.action = action;
+- }
+-
+- public String getSsoId() {
+- return ssoId;
+- }
+-
+- public void setSsoId(String ssoId) {
+- this.ssoId = ssoId;
+- }
+-
+- public String getContextName() {
+- return ctxname;
+- }
+-
+- public void setContextName(String ctxname) {
+- this.ctxname = ctxname;
+- }
+-
+- public String getSessionId() {
+- return sessionId;
+- }
+-
+- public void setSessionId(String sessionId) {
+- this.sessionId = sessionId;
+- }
+-
+- public String getAuthType() {
+- return authType;
+- }
+-
+- public void setAuthType(String authType) {
+- this.authType = authType;
+- }
+-
+- public String getPassword() {
+- return password;
+- }
+-
+- public void setPassword(String password) {
+- this.password = password;
+- }
+-
+- public String getUsername() {
+- return username;
+- }
+-
+- public void setUsername(String username) {
+- this.username = username;
+- }
+-
+-
+- // --------------------------------------------------------- Public Methods
+-
+- /**
+- * Return a String rendering of this object.
+- */
+- public String toString() {
+-
+- StringBuffer sb = new StringBuffer("SingleSignOnMessage[action=");
+- sb.append(getAction()).append(", ssoId=").append(getSsoId());
+- sb.append(", sessionId=").append(getSessionId()).append(",
username=");
+- sb.append(getUsername()).append("]");
+- return (sb.toString());
+-
+- }
+-
+-}
+Index: java/org/apache/catalina/ha/authenticator/ClusterSingleSignOnListener.java
+===================================================================
+--- java/org/apache/catalina/ha/authenticator/ClusterSingleSignOnListener.java (revision
590752)
++++ java/org/apache/catalina/ha/authenticator/ClusterSingleSignOnListener.java (working
copy)
+@@ -1,180 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.authenticator;
+-
+-import java.util.Map;
+-import java.io.IOException;
+-
+-import org.apache.catalina.Session;
+-import org.apache.catalina.ha.ClusterManager;
+-import org.apache.catalina.ha.ClusterMessage;
+-import org.apache.catalina.ha.ClusterListener;
+-
+-/**
+- * Receive replicated SingleSignOnMessage form other cluster node.
+- *
+- * @author Fabien Carrion
+- */
+-public class ClusterSingleSignOnListener extends ClusterListener {
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- protected static final String info =
"org.apache.catalina.session.ClusterSingleSignOnListener/1.0";
+-
+- // ------------------------------------------------------------- Properties
+-
+- private ClusterSingleSignOn clusterSSO = null;
+-
+-
+- //--Constructor---------------------------------------------
+-
+- public ClusterSingleSignOnListener() {
+- }
+-
+- //--Logic---------------------------------------------------
+-
+- /**
+- * Return descriptive information about this implementation.
+- */
+- public String getInfo() {
+-
+- return (info);
+-
+- }
+-
+- public ClusterSingleSignOn getClusterSSO() {
+-
+- return clusterSSO;
+-
+- }
+-
+- public void setClusterSSO(ClusterSingleSignOn clusterSSO) {
+-
+- this.clusterSSO = clusterSSO;
+-
+- }
+-
+-
+- /**
+- * Callback from the cluster, when a message is received, The cluster will
+- * broadcast it invoking the messageReceived on the receiver.
+- *
+- * @param myobj
+- * ClusterMessage - the message received from the cluster
+- */
+- public void messageReceived(ClusterMessage myobj) {
+- if (myobj != null && myobj instanceof SingleSignOnMessage) {
+- SingleSignOnMessage msg = (SingleSignOnMessage) myobj;
+- int action = msg.getAction();
+- Session session = null;
+-
+- if (log.isDebugEnabled())
+- log.debug("SingleSignOnMessage Received with action "
+- + msg.getAction());
+-
+- switch(action) {
+- case SingleSignOnMessage.ADD_SESSION:
+- session = getSession(msg.getSessionId(),
+- msg.getContextName());
+- if (session != null)
+- clusterSSO.associateLocal(msg.getSsoId(), session);
+- break;
+- case SingleSignOnMessage.DEREGISTER_SESSION:
+- session = getSession(msg.getSessionId(),
+- msg.getContextName());
+- if (session != null)
+- clusterSSO.deregisterLocal(msg.getSsoId(), session);
+- break;
+- case SingleSignOnMessage.LOGOUT_SESSION:
+- clusterSSO.deregisterLocal(msg.getSsoId());
+- break;
+- case SingleSignOnMessage.REGISTER_SESSION:
+- clusterSSO.registerLocal(msg.getSsoId(), null, msg.getAuthType(),
+- msg.getUsername(), msg.getPassword());
+- break;
+- case SingleSignOnMessage.UPDATE_SESSION:
+- clusterSSO.updateLocal(msg.getSsoId(), null, msg.getAuthType(),
+- msg.getUsername(), msg.getPassword());
+- break;
+- case SingleSignOnMessage.REMOVE_SESSION:
+- session = getSession(msg.getSessionId(),
+- msg.getContextName());
+- if (session != null)
+- clusterSSO.removeSessionLocal(msg.getSsoId(), session);
+- break;
+- }
+- }
+- }
+-
+- /**
+- * Accept only SingleSignOnMessage
+- *
+- * @param msg
+- * ClusterMessage
+- * @return boolean - returns true to indicate that messageReceived should be
+- * invoked. If false is returned, the messageReceived method will
+- * not be invoked.
+- */
+- public boolean accept(ClusterMessage msg) {
+- return (msg instanceof SingleSignOnMessage);
+- }
+-
+-
+- private Session getSession(String sessionId, String ctxname) {
+-
+- Map managers = clusterSSO.getCluster().getManagers() ;
+- Session session = null;
+-
+- if (ctxname == null) {
+- java.util.Iterator i = managers.keySet().iterator();
+- while (i.hasNext()) {
+- String key = (String) i.next();
+- ClusterManager mgr = (ClusterManager) managers.get(key);
+- if (mgr != null) {
+- try {
+- session = mgr.findSession(sessionId);
+- } catch (IOException io) {
+- log.error("Session doesn't exist:" + io);
+- }
+- return session;
+- } else {
+- //this happens a lot before the system has started
+- // up
+- if (log.isDebugEnabled())
+- log.debug("Context manager doesn't exist:"
+- + key);
+- }
+- }
+- } else {
+- ClusterManager mgr = (ClusterManager) managers.get(ctxname);
+- if (mgr != null) {
+- try {
+- session = mgr.findSession(sessionId);
+- } catch (IOException io) {
+- log.error("Session doesn't exist:" + io);
+- }
+- return session;
+- } else if (log.isErrorEnabled())
+- log.error("Context manager doesn't exist:" + ctxname);
+- }
+-
+- return null;
+- }
+-}
+-
+Index: java/org/apache/catalina/ha/util/IDynamicProperty.java
+===================================================================
+--- java/org/apache/catalina/ha/util/IDynamicProperty.java (revision 590752)
++++ java/org/apache/catalina/ha/util/IDynamicProperty.java (working copy)
+@@ -1,58 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.ha.util;
+-
+-import java.util.Iterator;
+-
+-/**
+- * @author Peter Rossbach
+- * @version $Revision$, $Date$
+- */
+-
+-public interface IDynamicProperty {
+-
+- /**
+- * set config attributes with reflect
+- *
+- * @param name
+- * @param value
+- */
+- public boolean setProperty(String name, Object value) ;
+-
+- /**
+- * get current config
+- *
+- * @param key
+- * @return The property
+- */
+- public Object getProperty(String key) ;
+- /**
+- * Get all properties keys
+- *
+- * @return An iterator over the property names
+- */
+- public Iterator getPropertyNames() ;
+-
+- /**
+- * remove a configured property.
+- *
+- * @param key
+- */
+- public void removeProperty(String key) ;
+-
+-}
+Index: java/org/apache/catalina/tribes/Heartbeat.java
+===================================================================
+--- java/org/apache/catalina/tribes/Heartbeat.java (revision 590752)
++++ java/org/apache/catalina/tribes/Heartbeat.java (working copy)
+@@ -1,34 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-/**
+- * Can be implemented by the ChannelListener and Membership listeners to receive
heartbeat
+- * notifications from the Channel
+- * @author Filip Hanik
+- * @version 1.0
+- * @see Channel
+- * @see Channel#heartbeat()
+- */
+-public interface Heartbeat {
+-
+- /**
+- * Heartbeat invokation for resources cleanup etc
+- */
+- public void heartbeat();
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/MembershipListener.java
+===================================================================
+--- java/org/apache/catalina/tribes/MembershipListener.java (revision 590752)
++++ java/org/apache/catalina/tribes/MembershipListener.java (working copy)
+@@ -1,45 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes;
+-
+-/**
+- * The MembershipListener interface is used as a callback to the
+- * membership service. It has two methods that will notify the listener
+- * when a member has joined the group and when a member has disappeared (crashed)
+- *
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-
+-public interface MembershipListener {
+- /**
+- * A member was added to the group
+- * @param member Member - the member that was added
+- */
+- public void memberAdded(Member member);
+-
+- /**
+- * A member was removed from the group<br>
+- * If the member left voluntarily, the Member.getCommand will contain the
Member.SHUTDOWN_PAYLOAD data
+- * @param member Member
+- * @see Member#SHUTDOWN_PAYLOAD
+- */
+- public void memberDisappeared(Member member);
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/Member.java
+===================================================================
+--- java/org/apache/catalina/tribes/Member.java (revision 590752)
++++ java/org/apache/catalina/tribes/Member.java (working copy)
+@@ -1,119 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes;
+-
+-/**
+- * The Member interface, defines a member in the group.
+- * Each member can carry a set of properties, defined by the actual
implementation.<BR>
+- * A member is identified by the host/ip/uniqueId<br>
+- * The host is what interface the member is listening to, to receive data<br>
+- * The port is what port the member is listening to, to receive data<br>
+- * The uniqueId defines the session id for the member. This is an important feature
+- * since a member that has crashed and the starts up again on the same port/host is
+- * not guaranteed to be the same member, so no state transfers will ever be confused
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-
+-public interface Member {
+-
+- /**
+- * When a member leaves the cluster, the payload of the memberDisappeared member
+- * will be the following bytes. This indicates a soft shutdown, and not a crash
+- */
+- public static final byte[] SHUTDOWN_PAYLOAD = new byte[] {66, 65, 66, 89, 45, 65,
76, 69, 88};
+-
+- /**
+- * Returns the name of this node, should be unique within the group.
+- */
+- public String getName();
+-
+- /**
+- * Returns the listen host for the ChannelReceiver implementation
+- * @return IPv4 or IPv6 representation of the host address this member listens to
incoming data
+- * @see ChannelReceiver
+- */
+- public byte[] getHost();
+-
+- /**
+- * Returns the listen port for the ChannelReceiver implementation
+- * @return the listen port for this member, -1 if its not listening on an unsecure
port
+- * @see ChannelReceiver
+- */
+- public int getPort();
+-
+- /**
+- * Returns the secure listen port for the ChannelReceiver implementation.
+- * Returns -1 if its not listening to a secure port.
+- * @return the listen port for this member, -1 if its not listening on a secure
port
+- * @see ChannelReceiver
+- */
+- public int getSecurePort();
+-
+-
+- /**
+- * Contains information on how long this member has been online.
+- * The result is the number of milli seconds this member has been
+- * broadcasting its membership to the group.
+- * @return nr of milliseconds since this member started.
+- */
+- public long getMemberAliveTime();
+-
+- /**
+- * The current state of the member
+- * @return boolean - true if the member is functioning correctly
+- */
+- public boolean isReady();
+- /**
+- * The current state of the member
+- * @return boolean - true if the member is suspect, but the crash has not been
confirmed
+- */
+- public boolean isSuspect();
+-
+- /**
+- *
+- * @return boolean - true if the member has been confirmed to malfunction
+- */
+- public boolean isFailing();
+-
+- /**
+- * returns a UUID unique for this member over all sessions.
+- * If the member crashes and restarts, the uniqueId will be different.
+- * @return byte[]
+- */
+- public byte[] getUniqueId();
+-
+- /**
+- * returns the payload associated with this member
+- * @return byte[]
+- */
+- public byte[] getPayload();
+-
+- /**
+- * returns the command associated with this member
+- * @return byte[]
+- */
+- public byte[] getCommand();
+-
+- /**
+- * Domain for this cluster
+- * @return byte[]
+- */
+- public byte[] getDomain();
+-}
+Index: java/org/apache/catalina/tribes/ByteMessage.java
+===================================================================
+--- java/org/apache/catalina/tribes/ByteMessage.java (revision 590752)
++++ java/org/apache/catalina/tribes/ByteMessage.java (working copy)
+@@ -1,102 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import java.io.Serializable;
+-import java.io.Externalizable;
+-import java.io.ObjectInput;
+-import java.io.IOException;
+-import java.io.ObjectOutput;
+-
+-/**
+- * A byte message is not serialized and deserialized by the channel
+- * instead it is sent as a byte array<br>
+- * By default Tribes uses java serialization when it receives an object
+- * to be sent over the wire. Java serialization is not the most
+- * efficient of serializing data, and Tribes might not even
+- * have access to the correct class loaders to deserialize the object properly.
+- * <br>
+- * The ByteMessage class is a class where the channel when it receives it will
+- * not attempt to perform serialization, instead it will simply stream the
<code>getMessage()</code>
+- * bytes.<br>
+- * If you are using multiple applications on top of Tribes you should add some sort of
header
+- * so that you can decide with the <code>ChannelListener.accept()</code>
whether this message was intended
+- * for you.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public class ByteMessage implements Serializable, Externalizable {
+- /**
+- * Storage for the message to be sent
+- */
+- private byte[] message;
+-
+-
+- /**
+- * Creates an empty byte message
+- * Constructor also for deserialization
+- */
+- public ByteMessage() {
+- }
+-
+- /**
+- * Creates a byte message wit h
+- * @param data byte[] - the message contents
+- */
+- public ByteMessage(byte[] data) {
+- message = data;
+- }
+-
+- /**
+- * Returns the message contents of this byte message
+- * @return byte[] - message contents, can be null
+- */
+- public byte[] getMessage() {
+- return message;
+- }
+-
+- /**
+- * Sets the message contents of this byte message
+- * @param message byte[]
+- */
+- public void setMessage(byte[] message) {
+- this.message = message;
+- }
+-
+- /**
+- * @see java.io.Externalizable#readExternal
+- * @param in ObjectInput
+- * @throws IOException
+- */
+- public void readExternal(ObjectInput in ) throws IOException {
+- int length = in.readInt();
+- message = new byte[length];
+- in.read(message,0,length);
+- }
+-
+- /**
+- * @see java.io.Externalizable#writeExternal
+- * @param out ObjectOutput
+- * @throws IOException
+- */
+- public void writeExternal(ObjectOutput out) throws IOException {
+- out.writeInt(message!=null?message.length:0);
+- if ( message!=null ) out.write(message,0,message.length);
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/Channel.java
+===================================================================
+--- java/org/apache/catalina/tribes/Channel.java (revision 590752)
++++ java/org/apache/catalina/tribes/Channel.java (working copy)
+@@ -1,347 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import java.io.Serializable;
+-
+-/**
+- * Channel interface<br>
+- * A channel is a representation of a group of nodes all participating in some sort of
+- * communication with each other.<br>
+- * The channel is the main API class for Tribes, this is essentially the only class
+- * that an application needs to be aware of. Through the channel the application
can:<br>
+- * 1. send messages<br>
+- * 2. receive message (by registering a
<code>ChannelListener</code><br>
+- * 3. get all members of the group <code>getMembers()</code><br>
+- * 4. receive notifications of members added and members disappeared by
+- * registerering a <code>MembershipListener</code><br>
+- * <br>
+- * The channel has 5 major components:<br>
+- * 1. Data receiver, with a built in thread pool to receive messages from other
peers<br>
+- * 2. Data sender, an implementation for sending data using NIO or java.io<br>
+- * 3. Membership listener,listens for membership broadcasts<br>
+- * 4. Membership broadcaster, broadcasts membership pings.<br>
+- * 5. Channel interceptors, the ability to manipulate messages as they are sent or
arrive<br><br>
+- * The channel layout is:
+- * <pre><code>
+- * ChannelListener_1..ChannelListener_N MembershipListener_1..MembershipListener_N
[Application Layer]
+- * \ \ / /
+- * \ \ / /
+- * \ \ / /
+- * \ \ / /
+- * \ \ / /
+- * \ \ / /
+- * ---------------------------------------
+- * |
+- * |
+- * Channel
+- * |
+- * ChannelInterceptor_1
+- * |
[Channel stack]
+- * ChannelInterceptor_N
+- * |
+- * Coordinator (implements
MessageListener,MembershipListener,ChannelInterceptor)
+- * --------------------
+- * / | \
+- * / | \
+- * / | \
+- * / | \
+- * / | \
+- * MembershipService ChannelSender ChannelReceiver [IO
layer]
+- * </code></pre>
+- *
+- * For example usage @see org.apache.catalina.tribes.group.GroupChannel
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public interface Channel {
+-
+- /**
+- * Start and stop sequences can be controlled by these constants
+- * This allows you to start separate components of the channel <br>
+- * DEFAULT - starts or stops all components in the channel
+- * @see #start(int)
+- * @see #stop(int)
+- */
+- public static final int DEFAULT = 15;
+-
+- /**
+- * Start and stop sequences can be controlled by these constants
+- * This allows you to start separate components of the channel <br>
+- * SND_RX_SEQ - starts or stops the data receiver. Start means opening a server
socket
+- * in case of a TCP implementation
+- * @see #start(int)
+- * @see #stop(int)
+- */
+- public static final int SND_RX_SEQ = 1;
+-
+- /**
+- * Start and stop sequences can be controlled by these constants
+- * This allows you to start separate components of the channel <br>
+- * SND_TX_SEQ - starts or stops the data sender. This should not open any sockets,
+- * as sockets are opened on demand when a message is being sent
+- * @see #start(int)
+- * @see #stop(int)
+- */
+- public static final int SND_TX_SEQ = 2;
+-
+- /**
+- * Start and stop sequences can be controlled by these constants
+- * This allows you to start separate components of the channel <br>
+- * MBR_RX_SEQ - starts or stops the membership listener. In a multicast
implementation
+- * this will open a datagram socket and join a group and listen for membership
messages
+- * members joining
+- * @see #start(int)
+- * @see #stop(int)
+- */
+- public static final int MBR_RX_SEQ = 4;
+-
+- /**
+- * Start and stop sequences can be controlled by these constants
+- * This allows you to start separate components of the channel <br>
+- * MBR_TX_SEQ - starts or stops the membership broadcaster. In a multicast
implementation
+- * this will open a datagram socket and join a group and broadcast the local member
information
+- * @see #start(int)
+- * @see #stop(int)
+- */
+- public static final int MBR_TX_SEQ = 8;
+-
+- /**
+- * Send options, when a message is sent, it can have an option flag
+- * to trigger certain behavior. Most flags are used to trigger channel interceptors
+- * as the message passes through the channel stack. <br>
+- * However, there are five default flags that every channel implementation must
implement<br>
+- * SEND_OPTIONS_BYTE_MESSAGE - The message is a pure byte message and no marshalling
or unmarshalling will
+- * be performed.<br>
+- *
+- * @see #send(Member[], Serializable , int)
+- * @see #send(Member[], Serializable, int, ErrorHandler)
+- */
+- public static final int SEND_OPTIONS_BYTE_MESSAGE = 0x0001;
+-
+- /**
+- * Send options, when a message is sent, it can have an option flag
+- * to trigger certain behavior. Most flags are used to trigger channel interceptors
+- * as the message passes through the channel stack. <br>
+- * However, there are five default flags that every channel implementation must
implement<br>
+- * SEND_OPTIONS_USE_ACK - Message is sent and an ACK is received when the message
has been received by the recipient<br>
+- * If no ack is received, the message is not considered successful<br>
+- * @see #send(Member[], Serializable , int)
+- * @see #send(Member[], Serializable, int, ErrorHandler)
+- */
+- public static final int SEND_OPTIONS_USE_ACK = 0x0002;
+-
+- /**
+- * Send options, when a message is sent, it can have an option flag
+- * to trigger certain behavior. Most flags are used to trigger channel interceptors
+- * as the message passes through the channel stack. <br>
+- * However, there are five default flags that every channel implementation must
implement<br>
+- * SEND_OPTIONS_SYNCHRONIZED_ACK - Message is sent and an ACK is received when the
message has been received and
+- * processed by the recipient<br>
+- * If no ack is received, the message is not considered successful<br>
+- * @see #send(Member[], Serializable , int)
+- * @see #send(Member[], Serializable, int, ErrorHandler)
+- */
+- public static final int SEND_OPTIONS_SYNCHRONIZED_ACK = 0x0004;
+-
+- /**
+- * Send options, when a message is sent, it can have an option flag
+- * to trigger certain behavior. Most flags are used to trigger channel interceptors
+- * as the message passes through the channel stack. <br>
+- * However, there are five default flags that every channel implementation must
implement<br>
+- * SEND_OPTIONS_ASYNCHRONOUS - Message is sent and an ACK is received when the
message has been received and
+- * processed by the recipient<br>
+- * If no ack is received, the message is not considered successful<br>
+- * @see #send(Member[], Serializable , int)
+- * @see #send(Member[], Serializable, int, ErrorHandler)
+- */
+- public static final int SEND_OPTIONS_ASYNCHRONOUS = 0x0008;
+-
+- /**
+- * Send options, when a message is sent, it can have an option flag
+- * to trigger certain behavior. Most flags are used to trigger channel interceptors
+- * as the message passes through the channel stack. <br>
+- * However, there are five default flags that every channel implementation must
implement<br>
+- * SEND_OPTIONS_SECURE - Message is sent over an encrypted channel<br>
+- * @see #send(Member[], Serializable , int)
+- * @see #send(Member[], Serializable, int, ErrorHandler)
+- */
+- public static final int SEND_OPTIONS_SECURE = 0x0010;
+-
+-
+- /**
+- * Send options, when a message is sent, it can have an option flag
+- * to trigger certain behavior. Most flags are used to trigger channel interceptors
+- * as the message passes through the channel stack. <br>
+- * However, there are five default flags that every channel implementation must
implement<br>
+- * SEND_OPTIONS_DEFAULT - the default sending options, just a helper variable.
<br>
+- * The default is <code>int SEND_OPTIONS_DEFAULT =
SEND_OPTIONS_USE_ACK;</code><br>
+- * @see #SEND_OPTIONS_USE_ACK
+- * @see #send(Member[], Serializable , int)
+- * @see #send(Member[], Serializable, int, ErrorHandler)
+- */
+- public static final int SEND_OPTIONS_DEFAULT = SEND_OPTIONS_USE_ACK;
+-
+-
+- /**
+- * Adds an interceptor to the channel message chain.
+- * @param interceptor ChannelInterceptor
+- */
+- public void addInterceptor(ChannelInterceptor interceptor);
+-
+- /**
+- * Starts up the channel. This can be called multiple times for individual services
to start
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will start all services <BR>
+- * MBR_RX_SEQ - starts the membership receiver <BR>
+- * MBR_TX_SEQ - starts the membership broadcaster <BR>
+- * SND_TX_SEQ - starts the replication transmitter<BR>
+- * SND_RX_SEQ - starts the replication receiver<BR>
+- * <b>Note:</b> In order for the membership broadcaster to
+- * transmit the correct information, it has to be started after the replication
receiver.
+- * @throws ChannelException if a startup error occurs or the service is already
started or an error occurs.
+- */
+- public void start(int svc) throws ChannelException;
+-
+- /**
+- * Shuts down the channel. This can be called multiple times for individual services
to shutdown
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will shutdown all services <BR>
+- * MBR_RX_SEQ - stops the membership receiver <BR>
+- * MBR_TX_SEQ - stops the membership broadcaster <BR>
+- * SND_TX_SEQ - stops the replication transmitter<BR>
+- * SND_RX_SEQ - stops the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
stopped or an error occurs.
+- */
+- public void stop(int svc) throws ChannelException;
+-
+- /**
+- * Send a message to one or more members in the cluster
+- * @param destination Member[] - the destinations, can not be null or zero length,
the reason for that
+- * is that a membership change can occur and at that time the application is
uncertain what group the message
+- * actually got sent to.
+- * @param msg Serializable - the message to send, has to be serializable, or a
<code>ByteMessage</code> to
+- * send a pure byte array
+- * @param options int - sender options, see class documentation for each interceptor
that is configured in order to trigger interceptors
+- * @return a unique Id that identifies the message that is sent
+- * @see ByteMessage
+- * @see #SEND_OPTIONS_USE_ACK
+- * @see #SEND_OPTIONS_ASYNCHRONOUS
+- * @see #SEND_OPTIONS_SYNCHRONIZED_ACK
+- */
+- public UniqueId send(Member[] destination, Serializable msg, int options) throws
ChannelException;
+-
+- /**
+- * Send a message to one or more members in the cluster
+- * @param destination Member[] - the destinations, null or zero length means all
+- * @param msg ClusterMessage - the message to send
+- * @param options int - sender options, see class documentation
+- * @param handler ErrorHandler - handle errors through a callback, rather than throw
it
+- * @return a unique Id that identifies the message that is sent
+- * @exception ChannelException - if a serialization error happens.
+- */
+- public UniqueId send(Member[] destination, Serializable msg, int options,
ErrorHandler handler) throws ChannelException;
+-
+- /**
+- * Sends a heart beat through the interceptor stacks
+- * Use this method to alert interceptors and other components to
+- * clean up garbage, timed out messages etc.<br>
+- * If you application has a background thread, then you can save one thread,
+- * by configuring your channel to not use an internal heartbeat thread
+- * and invoking this method.
+- * @see #setHeartbeat(boolean)
+- */
+- public void heartbeat();
+-
+- /**
+- * Enables or disables internal heartbeat.
+- * @param enable boolean - default value is implementation specific
+- * @see #heartbeat()
+- */
+- public void setHeartbeat(boolean enable);
+-
+- /**
+- * Add a membership listener, will get notified when a new member joins, leaves or
crashes
+- * <br>If the membership listener implements the Heartbeat interface
+- * the <code>heartbeat()</code> method will be invoked when the
heartbeat runs on the channel
+- * @param listener MembershipListener
+- * @see MembershipListener
+- */
+- public void addMembershipListener(MembershipListener listener);
+-
+- /**
+- * Add a channel listener, this is a callback object when messages are received
+- * <br>If the channel listener implements the Heartbeat interface
+- * the <code>heartbeat()</code> method will be invoked when the
heartbeat runs on the channel
+- * @param listener ChannelListener
+- * @see ChannelListener
+- * @see Heartbeat
+- */
+- public void addChannelListener(ChannelListener listener);
+-
+- /**
+- * remove a membership listener, listeners are removed based on Object.hashCode and
Object.equals
+- * @param listener MembershipListener
+- * @see MembershipListener
+- */
+- public void removeMembershipListener(MembershipListener listener);
+- /**
+- * remove a channel listener, listeners are removed based on Object.hashCode and
Object.equals
+- * @param listener ChannelListener
+- * @see ChannelListener
+- */
+- public void removeChannelListener(ChannelListener listener);
+-
+- /**
+- * Returns true if there are any members in the group,
+- * this call is the same as <code>getMembers().length>0</code>
+- * @return boolean - true if there are any members automatically discovered
+- */
+- public boolean hasMembers() ;
+-
+- /**
+- * Get all current group members
+- * @return all members or empty array, never null
+- */
+- public Member[] getMembers() ;
+-
+- /**
+- * Return the member that represents this node. This is also the data
+- * that gets broadcasted through the membership broadcaster component
+- * @param incAlive - optimization, true if you want it to calculate alive time
+- * since the membership service started.
+- * @return Member
+- */
+- public Member getLocalMember(boolean incAlive);
+-
+- /**
+- * Returns the member from the membership service with complete and
+- * recent data. Some implementations might serialize and send
+- * membership information along with a message, and instead of sending
+- * complete membership details, only send the primary identifier for the member
+- * but not the payload or other information. When such message is received
+- * the application can retrieve the cached member through this call.<br>
+- * In most cases, this is not necessary.
+- * @param mbr Member
+- * @return Member
+- */
+- public Member getMember(Member mbr);
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/tipis/Streamable.java
+===================================================================
+--- java/org/apache/catalina/tribes/tipis/Streamable.java (revision 590752)
++++ java/org/apache/catalina/tribes/tipis/Streamable.java (working copy)
+@@ -1,61 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.tipis;
+-
+-import java.io.IOException;
+-
+-/**
+- * Example usage:
+- * <code><pre>
+- * byte[] data = new byte[1024];
+- * Streamable st = ....;
+- * while ( !st.eof() ) {
+- * int length = st.read(data,0,data.length);
+- * String s = new String(data,0,length);
+- * System.out.println(s);
+- * }
+- * </pre></code>
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public interface Streamable {
+-
+- /**
+- * returns true if the stream has reached its end
+- * @return boolean
+- */
+- public boolean eof();
+-
+- /**
+- * write data into the byte array starting at offset, maximum bytes read are
(data.length-offset)
+- * @param data byte[] - the array to read data into
+- * @param offset int - start position for writing data
+- * @return int - the number of bytes written into the data buffer
+- */
+- public int write(byte[] data, int offset, int length) throws IOException;
+-
+- /**
+- * read data into the byte array starting at offset
+- * @param data byte[] - the array to read data into
+- * @param offset int - start position for writing data
+- * @param length - the desired read length
+- * @return int - the number of bytes read from the data buffer
+- */
+- public int read(byte[] data, int offset, int length) throws IOException;
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
+===================================================================
+--- java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java (revision 590752)
++++ java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java (working copy)
+@@ -1,190 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.tipis;
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import org.apache.catalina.tribes.group.RpcCallback;
+-import org.apache.catalina.tribes.util.Arrays;
+-import org.apache.catalina.tribes.UniqueId;
+-import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
+-
+-/**
+- * A smart implementation of a stateful replicated map. uses primary/secondary backup
strategy.
+- * One node is always the primary and one node is always the backup.
+- * This map is synchronized across a cluster, and only has one backup
member.<br/>
+- * A perfect usage for this map would be a session map for a session manager in a
clustered environment.<br/>
+- * The only way to modify this list is to use the <code>put, putAll,
remove</code> methods.
+- * entrySet, entrySetFull, keySet, keySetFull, returns all non modifiable
sets.<br><br>
+- * If objects (values) in the map change without invoking <code>put()</code>
or <code>remove()</code>
+- * the data can be distributed using two different methods:<br>
+- * <code>replicate(boolean)</code> and <code>replicate(Object,
boolean)</code><br>
+- * These two methods are very important two understand. The map can work with two set of
value objects:<br>
+- * 1. Serializable - the entire object gets serialized each time it is
replicated<br>
+- * 2. ReplicatedMapEntry - this interface allows for a isDirty() flag and to replicate
diffs if desired.<br>
+- * Implementing the <code>ReplicatedMapEntry</code> interface allows you to
decide what objects
+- * get replicated and how much data gets replicated each time.<br>
+- * If you implement a smart AOP mechanism to detect changes in underlying objects, you
can replicate
+- * only those changes by implementing the ReplicatedMapEntry interface, and return true
when isDiffable()
+- * is invoked.<br><br>
+- *
+- * This map implementation doesn't have a background thread running to replicate
changes.
+- * If you do have changes without invoking put/remove then you need to invoke one of the
following methods:
+- * <ul>
+- * <li><code>replicate(Object,boolean)</code> - replicates only the
object that belongs to the key</li>
+- * <li><code>replicate(boolean)</code> - Scans the entire map for
changes and replicates data</li>
+- * </ul>
+- * the <code>boolean</code> value in the <code>replicate</code>
method used to decide
+- * whether to only replicate objects that implement the
<code>ReplicatedMapEntry</code> interface
+- * or to replicate all objects. If an object doesn't implement the
<code>ReplicatedMapEntry</code> interface
+- * each time the object gets replicated the entire object gets serialized, hence a call
to <code>replicate(true)</code>
+- * will replicate all objects in this map that are using this node as primary.
+- *
+- * <br><br><b>REMBER TO CALL <code>breakdown()</code> or
<code>finalize()</code> when you are done with the map to
+- * avoid memory leaks.<br><br>
+- * @todo implement periodic sync/transfer thread
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class LazyReplicatedMap extends AbstractReplicatedMap
+- implements RpcCallback, ChannelListener, MembershipListener {
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(LazyReplicatedMap.class);
+-
+-
+-
+-//------------------------------------------------------------------------------
+-// CONSTRUCTORS / DESTRUCTORS
+-//------------------------------------------------------------------------------
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple
maps per channel
+- * @param initialCapacity int - the size of this map, see HashMap
+- * @param loadFactor float - load factor, see HashMap
+- */
+- public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String
mapContextName, int initialCapacity, float loadFactor, ClassLoader[] cls) {
+- super(owner,channel,timeout,mapContextName,initialCapacity,loadFactor,
Channel.SEND_OPTIONS_DEFAULT,cls);
+- }
+-
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple
maps per channel
+- * @param initialCapacity int - the size of this map, see HashMap
+- */
+- public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String
mapContextName, int initialCapacity, ClassLoader[] cls) {
+- super(owner, channel,timeout,mapContextName,initialCapacity,
LazyReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls);
+- }
+-
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple
maps per channel
+- */
+- public LazyReplicatedMap(MapOwner owner, Channel channel, long timeout, String
mapContextName, ClassLoader[] cls) {
+- super(owner, channel,timeout,mapContextName,
LazyReplicatedMap.DEFAULT_INITIAL_CAPACITY,LazyReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT,
cls);
+- }
+-
+-
+-
+-
+-
+-//------------------------------------------------------------------------------
+-// METHODS TO OVERRIDE
+-//------------------------------------------------------------------------------
+- protected int getStateMessageType() {
+- return AbstractReplicatedMap.MapMessage.MSG_STATE;
+- }
+-
+- /**
+- * publish info about a map pair (key/value) to other nodes in the cluster
+- * @param key Object
+- * @param value Object
+- * @return Member - the backup node
+- * @throws ChannelException
+- */
+- protected Member[] publishEntryInfo(Object key, Object value) throws
ChannelException {
+- if (! (key instanceof Serializable && value instanceof Serializable) )
return new Member[0];
+- Member[] members = getMapMembers();
+- int firstIdx = getNextBackupIndex();
+- int nextIdx = firstIdx;
+- Member[] backup = new Member[0];
+-
+- //there are no backups
+- if ( members.length == 0 || firstIdx == -1 ) return backup;
+-
+- boolean success = false;
+- do {
+- //select a backup node
+- Member next = members[nextIdx];
+-
+- //increment for the next round of back up selection
+- nextIdx = nextIdx + 1;
+- if ( nextIdx >= members.length ) nextIdx = 0;
+-
+- if (next == null) {
+- continue;
+- }
+- MapMessage msg = null;
+- try {
+- backup = wrap(next);
+- //publish the backup data to one node
+- msg = new MapMessage(getMapContextName(), MapMessage.MSG_BACKUP, false,
+- (Serializable) key, (Serializable) value, null,
channel.getLocalMember(false), backup);
+- if ( log.isTraceEnabled() )
+- log.trace("Publishing backup data:"+msg+" to:
"+next.getName());
+- UniqueId id = getChannel().send(backup, msg, getChannelSendOptions());
+- if ( log.isTraceEnabled() )
+- log.trace("Data published:"+msg+" msg Id:"+id);
+- //we published out to a backup, mark the test success
+- success = true;
+- }catch ( ChannelException x ) {
+- log.error("Unable to replicate backup key:"+key+" to
backup:"+next+". Reason:"+x.getMessage(),x);
+- }
+- try {
+- //publish the data out to all nodes
+- Member[] proxies = excludeFromSet(backup, getMapMembers());
+- if (success && proxies.length > 0 ) {
+- msg = new MapMessage(getMapContextName(), MapMessage.MSG_PROXY,
false,
+- (Serializable) key, null, null,
channel.getLocalMember(false),backup);
+- if ( log.isTraceEnabled() )
+- log.trace("Publishing proxy data:"+msg+" to:
"+Arrays.toNameString(proxies));
+- getChannel().send(proxies, msg, getChannelSendOptions());
+- }
+- }catch ( ChannelException x ) {
+- //log the error, but proceed, this should only happen if a node went
down,
+- //and if the node went down, then it can't receive the message, the
others
+- //should still get it.
+- log.error("Unable to replicate proxy key:"+key+" to
backup:"+next+". Reason:"+x.getMessage(),x);
+- }
+- } while ( !success && (firstIdx!=nextIdx));
+- return backup;
+- }
+-
+-
+-
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/tipis/ReplicatedMap.java
+===================================================================
+--- java/org/apache/catalina/tribes/tipis/ReplicatedMap.java (revision 590752)
++++ java/org/apache/catalina/tribes/tipis/ReplicatedMap.java (working copy)
+@@ -1,123 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.tipis;
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import org.apache.catalina.tribes.group.RpcCallback;
+-import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
+-
+-/**
+- * All-to-all replication for a hash map implementation. Each node in the cluster will
carry an identical
+- * copy of the map.<br><br>
+- * This map implementation doesn't have a background thread running to replicate
changes.
+- * If you do have changes without invoking put/remove then you need to invoke one of the
following methods:
+- * <ul>
+- * <li><code>replicate(Object,boolean)</code> - replicates only the
object that belongs to the key</li>
+- * <li><code>replicate(boolean)</code> - Scans the entire map for
changes and replicates data</li>
+- * </ul>
+- * the <code>boolean</code> value in the <code>replicate</code>
method used to decide
+- * whether to only replicate objects that implement the
<code>ReplicatedMapEntry</code> interface
+- * or to replicate all objects. If an object doesn't implement the
<code>ReplicatedMapEntry</code> interface
+- * each time the object gets replicated the entire object gets serialized, hence a call
to <code>replicate(true)</code>
+- * will replicate all objects in this map that are using this node as primary.
+- *
+- * <br><br><b>REMBER TO CALL <code>breakdown()</code> or
<code>finalize()</code> when you are done with the map to
+- * avoid memory leaks.<br><br>
+- * @todo implement periodic sync/transfer thread
+- * @author Filip Hanik
+- * @version 1.0
+- *
+- * @todo memberDisappeared, should do nothing except change map membership
+- * by default it relocates the primary objects
+- */
+-public class ReplicatedMap extends AbstractReplicatedMap implements RpcCallback,
ChannelListener, MembershipListener {
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ReplicatedMap.class);
+-
+-//------------------------------------------------------------------------------
+-// CONSTRUCTORS / DESTRUCTORS
+-//------------------------------------------------------------------------------
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple maps
per channel
+- * @param initialCapacity int - the size of this map, see HashMap
+- * @param loadFactor float - load factor, see HashMap
+- */
+- public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String
mapContextName, int initialCapacity,float loadFactor, ClassLoader[] cls) {
+- super(owner,channel, timeout, mapContextName, initialCapacity, loadFactor,
Channel.SEND_OPTIONS_DEFAULT, cls);
+- }
+-
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple maps
per channel
+- * @param initialCapacity int - the size of this map, see HashMap
+- */
+- public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String
mapContextName, int initialCapacity, ClassLoader[] cls) {
+- super(owner,channel, timeout, mapContextName, initialCapacity,
AbstractReplicatedMap.DEFAULT_LOAD_FACTOR,Channel.SEND_OPTIONS_DEFAULT, cls);
+- }
+-
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple maps
per channel
+- */
+- public ReplicatedMap(MapOwner owner, Channel channel, long timeout, String
mapContextName, ClassLoader[] cls) {
+- super(owner, channel, timeout,
mapContextName,AbstractReplicatedMap.DEFAULT_INITIAL_CAPACITY,
AbstractReplicatedMap.DEFAULT_LOAD_FACTOR, Channel.SEND_OPTIONS_DEFAULT, cls);
+- }
+-
+-//------------------------------------------------------------------------------
+-// METHODS TO OVERRIDE
+-//------------------------------------------------------------------------------
+- protected int getStateMessageType() {
+- return AbstractReplicatedMap.MapMessage.MSG_STATE_COPY;
+- }
+-
+- /**
+- * publish info about a map pair (key/value) to other nodes in the cluster
+- * @param key Object
+- * @param value Object
+- * @return Member - the backup node
+- * @throws ChannelException
+- */
+- protected Member[] publishEntryInfo(Object key, Object value) throws
ChannelException {
+- if (! (key instanceof Serializable && value instanceof Serializable) )
return new Member[0];
+- //select a backup node
+- Member[] backup = getMapMembers();
+-
+- if (backup == null || backup.length == 0) return null;
+-
+- //publish the data out to all nodes
+- MapMessage msg = new MapMessage(getMapContextName(), MapMessage.MSG_COPY,
false,
+- (Serializable) key, (Serializable) value,
null,channel.getLocalMember(false), backup);
+-
+- getChannel().send(getMapMembers(), msg, getChannelSendOptions());
+-
+- return backup;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java
+===================================================================
+--- java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java (revision 590752)
++++ java/org/apache/catalina/tribes/tipis/ReplicatedMapEntry.java (working copy)
+@@ -1,124 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.tipis;
+-
+-import java.io.IOException;
+-import java.io.Serializable;
+-
+-/**
+- *
+- * For smarter replication, an object can implement this interface to replicate
diffs<br>
+- * The replication logic will call the methods in the following order:<br>
+- * <code>
+- * 1. if ( entry.isDirty() ) <br>
+- * try {
+- * 2. entry.lock();<br>
+- * 3. byte[] diff = entry.getDiff();<br>
+- * 4. entry.reset();<br>
+- * } finally {<br>
+- * 5. entry.unlock();<br>
+- * }<br>
+- * }<br>
+- * </code>
+- * <br>
+- * <br>
+- * When the data is deserialized the logic is called in the following order<br>
+- * <code>
+- * 1. ReplicatedMapEntry entry = (ReplicatedMapEntry)objectIn.readObject();<br>
+- * 2. if ( isBackup(entry)||isPrimary(entry) ) entry.setOwner(owner); <br>
+- * </code>
+- * <br>
+- *
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public interface ReplicatedMapEntry extends Serializable {
+-
+- /**
+- * Has the object changed since last replication
+- * and is not in a locked state
+- * @return boolean
+- */
+- public boolean isDirty();
+-
+- /**
+- * If this returns true, the map will extract the diff using getDiff()
+- * Otherwise it will serialize the entire object.
+- * @return boolean
+- */
+- public boolean isDiffable();
+-
+- /**
+- * Returns a diff and sets the dirty map to false
+- * @return byte[]
+- * @throws IOException
+- */
+- public byte[] getDiff() throws IOException;
+-
+-
+- /**
+- * Applies a diff to an existing object.
+- * @param diff byte[]
+- * @param offset int
+- * @param length int
+- * @throws IOException
+- */
+- public void applyDiff(byte[] diff, int offset, int length) throws IOException,
ClassNotFoundException;
+-
+- /**
+- * Resets the current diff state and resets the dirty flag
+- */
+- public void resetDiff();
+-
+- /**
+- * Lock during serialization
+- */
+- public void lock();
+-
+- /**
+- * Unlock after serialization
+- */
+- public void unlock();
+-
+- /**
+- * This method is called after the object has been
+- * created on a remote map. On this method,
+- * the object can initialize itself for any data that wasn't
+- *
+- * @param owner Object
+- */
+- public void setOwner(Object owner);
+-
+- /**
+- * For accuracy checking, a serialized attribute can contain a version number
+- * This number increases as modifications are made to the data.
+- * The replicated map can use this to ensure accuracy on a periodic basis
+- * @return long - the version number or -1 if the data is not versioned
+- */
+- public long getVersion();
+-
+- /**
+- * Forces a certain version to a replicated map entry<br>
+- * @param version long
+- */
+- public void setVersion(long version);
+-
+-
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
+===================================================================
+--- java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java (revision 590752)
++++ java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java (working copy)
+@@ -1,1474 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.tipis;
+-
+-import java.io.IOException;
+-import java.io.ObjectInput;
+-import java.io.ObjectOutput;
+-import java.io.Serializable;
+-import java.io.UnsupportedEncodingException;
+-import java.util.ArrayList;
+-import java.util.Collection;
+-import java.util.Collections;
+-import java.util.HashMap;
+-import java.util.Iterator;
+-import java.util.LinkedHashSet;
+-import java.util.Map;
+-import java.util.Set;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.Heartbeat;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import org.apache.catalina.tribes.group.Response;
+-import org.apache.catalina.tribes.group.RpcCallback;
+-import org.apache.catalina.tribes.group.RpcChannel;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import org.apache.catalina.tribes.util.Arrays;
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-import java.util.concurrent.ConcurrentHashMap;
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public abstract class AbstractReplicatedMap extends ConcurrentHashMap implements
RpcCallback, ChannelListener, MembershipListener, Heartbeat {
+- protected static Log log = LogFactory.getLog(AbstractReplicatedMap.class);
+-
+- /**
+- * The default initial capacity - MUST be a power of two.
+- */
+- public static final int DEFAULT_INITIAL_CAPACITY = 16;
+-
+- /**
+- * The load factor used when none specified in constructor.
+- **/
+- public static final float DEFAULT_LOAD_FACTOR = 0.75f;
+-
+- /**
+- * Used to identify the map
+- */
+- final String chset = "ISO-8859-1";
+-
+-//------------------------------------------------------------------------------
+-// INSTANCE VARIABLES
+-//------------------------------------------------------------------------------
+- protected abstract int getStateMessageType();
+-
+-
+- /**
+- * Timeout for RPC messages, how long we will wait for a reply
+- */
+- protected transient long rpcTimeout = 5000;
+- /**
+- * Reference to the channel for sending messages
+- */
+- protected transient Channel channel;
+- /**
+- * The RpcChannel to send RPC messages through
+- */
+- protected transient RpcChannel rpcChannel;
+- /**
+- * The Map context name makes this map unique, this
+- * allows us to have more than one map shared
+- * through one channel
+- */
+- protected transient byte[] mapContextName;
+- /**
+- * Has the state been transferred
+- */
+- protected transient boolean stateTransferred = false;
+- /**
+- * Simple lock object for transfers
+- */
+- protected transient Object stateMutex = new Object();
+- /**
+- * A list of members in our map
+- */
+- protected transient HashMap mapMembers = new HashMap();
+- /**
+- * Our default send options
+- */
+- protected transient int channelSendOptions = Channel.SEND_OPTIONS_DEFAULT;
+- /**
+- * The owner of this map, ala a SessionManager for example
+- */
+- protected transient MapOwner mapOwner;
+- /**
+- * External class loaders if serialization and deserialization is to be performed
successfully.
+- */
+- protected transient ClassLoader[] externalLoaders;
+-
+- /**
+- * The node we are currently backing up data to, this index will rotate
+- * on a round robin basis
+- */
+- protected transient int currentNode = 0;
+-
+- /**
+- * Since the map keeps internal membership
+- * this is the timeout for a ping message to be responded to
+- * If a remote map doesn't respond within this timeframe,
+- * its considered dead.
+- */
+- protected transient long accessTimeout = 5000;
+-
+- /**
+- * Readable string of the mapContextName value
+- */
+- protected transient String mapname = "";
+-
+-//------------------------------------------------------------------------------
+-// map owner interface
+-//------------------------------------------------------------------------------
+-
+- public static interface MapOwner {
+- public void objectMadePrimay(Object key, Object value);
+- }
+-
+-//------------------------------------------------------------------------------
+-// CONSTRUCTORS
+-//------------------------------------------------------------------------------
+-
+- /**
+- * Creates a new map
+- * @param channel The channel to use for communication
+- * @param timeout long - timeout for RPC messags
+- * @param mapContextName String - unique name for this map, to allow multiple maps
per channel
+- * @param initialCapacity int - the size of this map, see HashMap
+- * @param loadFactor float - load factor, see HashMap
+- * @param cls - a list of classloaders to be used for deserialization of objects.
+- */
+- public AbstractReplicatedMap(MapOwner owner,
+- Channel channel,
+- long timeout,
+- String mapContextName,
+- int initialCapacity,
+- float loadFactor,
+- int channelSendOptions,
+- ClassLoader[] cls) {
+- super(initialCapacity, loadFactor, 15);
+- init(owner, channel, mapContextName, timeout, channelSendOptions, cls);
+-
+- }
+-
+- /**
+- * Helper methods, wraps a single member in an array
+- * @param m Member
+- * @return Member[]
+- */
+- protected Member[] wrap(Member m) {
+- if ( m == null ) return new Member[0];
+- else return new Member[] {m};
+- }
+-
+- /**
+- * Initializes the map by creating the RPC channel, registering itself as a channel
listener
+- * This method is also responsible for initiating the state transfer
+- * @param owner Object
+- * @param channel Channel
+- * @param mapContextName String
+- * @param timeout long
+- * @param channelSendOptions int
+- * @param cls ClassLoader[]
+- */
+- protected void init(MapOwner owner, Channel channel, String mapContextName, long
timeout, int channelSendOptions,ClassLoader[] cls) {
+- log.info("Initializing AbstractReplicatedMap with context
name:"+mapContextName);
+- this.mapOwner = owner;
+- this.externalLoaders = cls;
+- this.channelSendOptions = channelSendOptions;
+- this.channel = channel;
+- this.rpcTimeout = timeout;
+-
+- try {
+- this.mapname = mapContextName;
+- //unique context is more efficient if it is stored as bytes
+- this.mapContextName = mapContextName.getBytes(chset);
+- } catch (UnsupportedEncodingException x) {
+- log.warn("Unable to encode mapContextName[" + mapContextName +
"] using getBytes(" + chset +") using default getBytes()", x);
+- this.mapContextName = mapContextName.getBytes();
+- }
+- if ( log.isTraceEnabled() ) log.trace("Created Lazy Map with
name:"+mapContextName+", bytes:"+Arrays.toString(this.mapContextName));
+-
+- //create an rpc channel and add the map as a listener
+- this.rpcChannel = new RpcChannel(this.mapContextName, channel, this);
+- //add this map as a message listener
+- this.channel.addChannelListener(this);
+- //listen for membership notifications
+- this.channel.addMembershipListener(this);
+-
+-
+- try {
+- //broadcast our map, this just notifies other members of our existence
+- broadcast(MapMessage.MSG_INIT, true);
+- //transfer state from another map
+- transferState();
+- //state is transferred, we are ready for messaging
+- broadcast(MapMessage.MSG_START, true);
+- } catch (ChannelException x) {
+- log.warn("Unable to send map start message.");
+- throw new RuntimeException("Unable to start replicated map.",x);
+- }
+- }
+-
+-
+- /**
+- * Sends a ping out to all the members in the cluster, not just map members
+- * that this map is alive.
+- * @param timeout long
+- * @throws ChannelException
+- */
+- protected void ping(long timeout) throws ChannelException {
+- //send out a map membership message, only wait for the first reply
+- MapMessage msg = new MapMessage(this.mapContextName,
+- MapMessage.MSG_INIT,
+- false,
+- null,
+- null,
+- null,
+- channel.getLocalMember(false),
+- null);
+- if ( channel.getMembers().length > 0 ) {
+- //send a ping, wait for all nodes to reply
+- Response[] resp = rpcChannel.send(channel.getMembers(),
+- msg, rpcChannel.ALL_REPLY,
+- (channelSendOptions),
+- (int) accessTimeout);
+- for (int i = 0; i < resp.length; i++) {
+- memberAlive(resp[i].getSource());
+- } //for
+- }
+- //update our map of members, expire some if we didn't receive a ping back
+- synchronized (mapMembers) {
+- Iterator it = mapMembers.entrySet().iterator();
+- long now = System.currentTimeMillis();
+- while ( it.hasNext() ) {
+- Map.Entry entry = (Map.Entry)it.next();
+- long access = ((Long)entry.getValue()).longValue();
+- if ( (now - access) > timeout ) {
+- it.remove();
+- memberDisappeared( (Member) entry.getKey());
+- }
+- }
+- }//synch
+- }
+-
+- /**
+- * We have received a member alive notification
+- * @param member Member
+- */
+- protected void memberAlive(Member member) {
+- synchronized (mapMembers) {
+- if (!mapMembers.containsKey(member)) {
+- mapMemberAdded(member);
+- } //end if
+- mapMembers.put(member, new Long(System.currentTimeMillis()));
+- }
+- }
+-
+- /**
+- * Helper method to broadcast a message to all members in a channel
+- * @param msgtype int
+- * @param rpc boolean
+- * @throws ChannelException
+- */
+- protected void broadcast(int msgtype, boolean rpc) throws ChannelException {
+- //send out a map membership message, only wait for the first reply
+- MapMessage msg = new MapMessage(this.mapContextName, msgtype,
+- false, null, null, null,
channel.getLocalMember(false), null);
+- if ( rpc) {
+- Response[] resp = rpcChannel.send(channel.getMembers(), msg,
rpcChannel.FIRST_REPLY, (channelSendOptions),rpcTimeout);
+- for (int i = 0; i < resp.length; i++) {
+- mapMemberAdded(resp[i].getSource());
+- messageReceived(resp[i].getMessage(), resp[i].getSource());
+- }
+- } else {
+- channel.send(channel.getMembers(),msg,channelSendOptions);
+- }
+- }
+-
+- public void breakdown() {
+- finalize();
+- }
+-
+- public void finalize() {
+- try {broadcast(MapMessage.MSG_STOP,false); }catch ( Exception ignore){}
+- //cleanup
+- if (this.rpcChannel != null) {
+- this.rpcChannel.breakdown();
+- }
+- if (this.channel != null) {
+- this.channel.removeChannelListener(this);
+- this.channel.removeMembershipListener(this);
+- }
+- this.rpcChannel = null;
+- this.channel = null;
+- this.mapMembers.clear();
+- super.clear();
+- this.stateTransferred = false;
+- this.externalLoaders = null;
+- }
+-
+- public int hashCode() {
+- return Arrays.hashCode(this.mapContextName);
+- }
+-
+- public boolean equals(Object o) {
+- if ( o == null ) return false;
+- if ( !(o instanceof AbstractReplicatedMap)) return false;
+- if ( !(o.getClass().equals(this.getClass())) ) return false;
+- AbstractReplicatedMap other = (AbstractReplicatedMap)o;
+- return Arrays.equals(mapContextName,other.mapContextName);
+- }
+-
+-//------------------------------------------------------------------------------
+-// GROUP COM INTERFACES
+-//------------------------------------------------------------------------------
+- public Member[] getMapMembers(HashMap members) {
+- synchronized (members) {
+- Member[] result = new Member[members.size()];
+- members.keySet().toArray(result);
+- return result;
+- }
+- }
+- public Member[] getMapMembers() {
+- return getMapMembers(this.mapMembers);
+- }
+-
+- public Member[] getMapMembersExcl(Member[] exclude) {
+- synchronized (mapMembers) {
+- HashMap list = (HashMap)mapMembers.clone();
+- for (int i=0; i<exclude.length;i++) list.remove(exclude[i]);
+- return getMapMembers(list);
+- }
+- }
+-
+-
+- /**
+- * Replicates any changes to the object since the last time
+- * The object has to be primary, ie, if the object is a proxy or a backup, it will
not be replicated<br>
+- * @param complete - if set to true, the object is replicated to its backup
+- * if set to false, only objects that implement ReplicatedMapEntry and the isDirty()
returns true will
+- * be replicated
+- */
+- public void replicate(Object key, boolean complete) {
+- if ( log.isTraceEnabled() )
+- log.trace("Replicate invoked on key:"+key);
+- MapEntry entry = (MapEntry)super.get(key);
+- if ( entry == null ) return;
+- if ( !entry.isSerializable() ) return;
+- if (entry != null && entry.isPrimary() &&
entry.getBackupNodes()!= null && entry.getBackupNodes().length > 0) {
+- Object value = entry.getValue();
+- //check to see if we need to replicate this object isDirty()||complete
+- boolean repl = complete || ( (value instanceof ReplicatedMapEntry)
&& ( (ReplicatedMapEntry) value).isDirty());
+-
+- if (!repl) {
+- if ( log.isTraceEnabled() )
+- log.trace("Not replicating:"+key+", no change
made");
+-
+- return;
+- }
+- //check to see if the message is diffable
+- boolean diff = ( (value instanceof ReplicatedMapEntry) && (
(ReplicatedMapEntry) value).isDiffable());
+- MapMessage msg = null;
+- if (diff) {
+- ReplicatedMapEntry rentry = (ReplicatedMapEntry)entry.getValue();
+- try {
+- rentry.lock();
+- //construct a diff message
+- msg = new MapMessage(mapContextName, MapMessage.MSG_BACKUP,
+- true, (Serializable) entry.getKey(), null,
+- rentry.getDiff(),
+- entry.getPrimary(),
+- entry.getBackupNodes());
+- } catch (IOException x) {
+- log.error("Unable to diff object. Will replicate the entire
object instead.", x);
+- } finally {
+- rentry.unlock();
+- }
+-
+- }
+- if (msg == null) {
+- //construct a complete
+- msg = new MapMessage(mapContextName, MapMessage.MSG_BACKUP,
+- false, (Serializable) entry.getKey(),
+- (Serializable) entry.getValue(),
+- null, entry.getPrimary(),entry.getBackupNodes());
+-
+- }
+- try {
+- if ( channel!=null && entry.getBackupNodes()!= null &&
entry.getBackupNodes().length > 0 ) {
+- channel.send(entry.getBackupNodes(), msg, channelSendOptions);
+- }
+- } catch (ChannelException x) {
+- log.error("Unable to replicate data.", x);
+- }
+- } //end if
+-
+- }
+-
+- /**
+- * This can be invoked by a periodic thread to replicate out any changes.
+- * For maps that don't store objects that implement ReplicatedMapEntry, this
+- * method should be used infrequently to avoid large amounts of data transfer
+- * @param complete boolean
+- */
+- public void replicate(boolean complete) {
+- Iterator i = super.entrySet().iterator();
+- while (i.hasNext()) {
+- Map.Entry e = (Map.Entry) i.next();
+- replicate(e.getKey(), complete);
+- } //while
+-
+- }
+-
+- public void transferState() {
+- try {
+- Member[] members = getMapMembers();
+- Member backup = members.length > 0 ? (Member) members[0] : null;
+- if (backup != null) {
+- MapMessage msg = new MapMessage(mapContextName, getStateMessageType(),
false,
+- null, null, null, null, null);
+- Response[] resp = rpcChannel.send(new Member[] {backup}, msg,
rpcChannel.FIRST_REPLY, channelSendOptions, rpcTimeout);
+- if (resp.length > 0) {
+- synchronized (stateMutex) {
+- msg = (MapMessage) resp[0].getMessage();
+- msg.deserialize(getExternalLoaders());
+- ArrayList list = (ArrayList) msg.getValue();
+- for (int i = 0; i < list.size(); i++) {
+- messageReceived( (Serializable) list.get(i),
resp[0].getSource());
+- } //for
+- }
+- } else {
+- log.warn("Transfer state, 0 replies, probably a
timeout.");
+- }
+- }
+- } catch (ChannelException x) {
+- log.error("Unable to transfer LazyReplicatedMap state.", x);
+- } catch (IOException x) {
+- log.error("Unable to transfer LazyReplicatedMap state.", x);
+- } catch (ClassNotFoundException x) {
+- log.error("Unable to transfer LazyReplicatedMap state.", x);
+- }
+- stateTransferred = true;
+- }
+-
+- /**
+- * @todo implement state transfer
+- * @param msg Serializable
+- * @return Serializable - null if no reply should be sent
+- */
+- public Serializable replyRequest(Serializable msg, final Member sender) {
+- if (! (msg instanceof MapMessage))return null;
+- MapMessage mapmsg = (MapMessage) msg;
+-
+- //map init request
+- if (mapmsg.getMsgType() == mapmsg.MSG_INIT) {
+- mapmsg.setPrimary(channel.getLocalMember(false));
+- return mapmsg;
+- }
+-
+- //map start request
+- if (mapmsg.getMsgType() == mapmsg.MSG_START) {
+- mapmsg.setPrimary(channel.getLocalMember(false));
+- mapMemberAdded(sender);
+- return mapmsg;
+- }
+-
+- //backup request
+- if (mapmsg.getMsgType() == mapmsg.MSG_RETRIEVE_BACKUP) {
+- MapEntry entry = (MapEntry)super.get(mapmsg.getKey());
+- if (entry == null || (!entry.isSerializable()) )return null;
+- mapmsg.setValue( (Serializable) entry.getValue());
+- return mapmsg;
+- }
+-
+- //state transfer request
+- if (mapmsg.getMsgType() == mapmsg.MSG_STATE || mapmsg.getMsgType() ==
mapmsg.MSG_STATE_COPY) {
+- synchronized (stateMutex) { //make sure we dont do two things at the same
time
+- ArrayList list = new ArrayList();
+- Iterator i = super.entrySet().iterator();
+- while (i.hasNext()) {
+- Map.Entry e = (Map.Entry) i.next();
+- MapEntry entry = (MapEntry) super.get(e.getKey());
+- if ( entry != null && entry.isSerializable() ) {
+- boolean copy = (mapmsg.getMsgType() == mapmsg.MSG_STATE_COPY);
+- MapMessage me = new MapMessage(mapContextName,
+-
copy?MapMessage.MSG_COPY:MapMessage.MSG_PROXY,
+- false, (Serializable) entry.getKey(), copy?(Serializable)
entry.getValue():null, null, entry.getPrimary(),entry.getBackupNodes());
+- list.add(me);
+- }
+- }
+- mapmsg.setValue(list);
+- return mapmsg;
+-
+- } //synchronized
+- }
+-
+- return null;
+-
+- }
+-
+- /**
+- * If the reply has already been sent to the requesting thread,
+- * the rpc callback can handle any data that comes in after the fact.
+- * @param msg Serializable
+- * @param sender Member
+- */
+- public void leftOver(Serializable msg, Member sender) {
+- //left over membership messages
+- if (! (msg instanceof MapMessage))return;
+-
+- MapMessage mapmsg = (MapMessage) msg;
+- try {
+- mapmsg.deserialize(getExternalLoaders());
+- if (mapmsg.getMsgType() == MapMessage.MSG_START) {
+- mapMemberAdded(mapmsg.getPrimary());
+- } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT) {
+- memberAlive(mapmsg.getPrimary());
+- }
+- } catch (IOException x ) {
+- log.error("Unable to deserialize MapMessage.",x);
+- } catch (ClassNotFoundException x ) {
+- log.error("Unable to deserialize MapMessage.",x);
+- }
+- }
+-
+- public void messageReceived(Serializable msg, Member sender) {
+- if (! (msg instanceof MapMessage)) return;
+-
+- MapMessage mapmsg = (MapMessage) msg;
+- if ( log.isTraceEnabled() ) {
+- log.trace("Map["+mapname+"] received message:"+mapmsg);
+- }
+-
+- try {
+- mapmsg.deserialize(getExternalLoaders());
+- } catch (IOException x) {
+- log.error("Unable to deserialize MapMessage.", x);
+- return;
+- } catch (ClassNotFoundException x) {
+- log.error("Unable to deserialize MapMessage.", x);
+- return;
+- }
+- if ( log.isTraceEnabled() )
+- log.trace("Map message received from:"+sender.getName()+"
msg:"+mapmsg);
+- if (mapmsg.getMsgType() == MapMessage.MSG_START) {
+- mapMemberAdded(mapmsg.getPrimary());
+- }
+-
+- if (mapmsg.getMsgType() == MapMessage.MSG_STOP) {
+- memberDisappeared(mapmsg.getPrimary());
+- }
+-
+- if (mapmsg.getMsgType() == MapMessage.MSG_PROXY) {
+- MapEntry entry = (MapEntry)super.get(mapmsg.getKey());
+- if ( entry==null ) {
+- entry = new MapEntry(mapmsg.getKey(), mapmsg.getValue());
+- entry.setBackup(false);
+- entry.setProxy(true);
+- entry.setBackupNodes(mapmsg.getBackupNodes());
+- entry.setPrimary(mapmsg.getPrimary());
+- super.put(entry.getKey(), entry);
+- } else {
+- entry.setProxy(true);
+- entry.setBackup(false);
+- entry.setBackupNodes(mapmsg.getBackupNodes());
+- entry.setPrimary(mapmsg.getPrimary());
+- }
+- }
+-
+- if (mapmsg.getMsgType() == MapMessage.MSG_REMOVE) {
+- super.remove(mapmsg.getKey());
+- }
+-
+- if (mapmsg.getMsgType() == MapMessage.MSG_BACKUP || mapmsg.getMsgType() ==
MapMessage.MSG_COPY) {
+- MapEntry entry = (MapEntry)super.get(mapmsg.getKey());
+- if (entry == null) {
+- entry = new MapEntry(mapmsg.getKey(), mapmsg.getValue());
+- entry.setBackup(mapmsg.getMsgType() == MapMessage.MSG_BACKUP);
+- entry.setProxy(false);
+- entry.setBackupNodes(mapmsg.getBackupNodes());
+- entry.setPrimary(mapmsg.getPrimary());
+- if (mapmsg.getValue()!=null && mapmsg.getValue() instanceof
ReplicatedMapEntry ) {
+- ((ReplicatedMapEntry)mapmsg.getValue()).setOwner(getMapOwner());
+- }
+- } else {
+- entry.setBackup(mapmsg.getMsgType() == MapMessage.MSG_BACKUP);
+- entry.setProxy(false);
+- entry.setBackupNodes(mapmsg.getBackupNodes());
+- entry.setPrimary(mapmsg.getPrimary());
+- if (entry.getValue() instanceof ReplicatedMapEntry) {
+- ReplicatedMapEntry diff = (ReplicatedMapEntry) entry.getValue();
+- if (mapmsg.isDiff()) {
+- try {
+- diff.lock();
+- diff.applyDiff(mapmsg.getDiffValue(), 0,
mapmsg.getDiffValue().length);
+- } catch (Exception x) {
+- log.error("Unable to apply diff to key:" +
entry.getKey(), x);
+- } finally {
+- diff.unlock();
+- }
+- } else {
+- if ( mapmsg.getValue()!=null )
entry.setValue(mapmsg.getValue());
+- ((ReplicatedMapEntry)entry.getValue()).setOwner(getMapOwner());
+- } //end if
+- } else if (mapmsg.getValue() instanceof ReplicatedMapEntry) {
+- ReplicatedMapEntry re = (ReplicatedMapEntry)mapmsg.getValue();
+- re.setOwner(getMapOwner());
+- entry.setValue(re);
+- } else {
+- if ( mapmsg.getValue()!=null ) entry.setValue(mapmsg.getValue());
+- } //end if
+- } //end if
+- super.put(entry.getKey(), entry);
+- } //end if
+- }
+-
+- public boolean accept(Serializable msg, Member sender) {
+- boolean result = false;
+- if (msg instanceof MapMessage) {
+- if ( log.isTraceEnabled() ) log.trace("Map["+mapname+"]
accepting...."+msg);
+- result = Arrays.equals(mapContextName, ( (MapMessage) msg).getMapId());
+- if ( log.isTraceEnabled() ) log.trace("Msg["+mapname+"]
accepted["+result+"]...."+msg);
+- }
+- return result;
+- }
+-
+- public void mapMemberAdded(Member member) {
+- if ( member.equals(getChannel().getLocalMember(false)) ) return;
+- boolean memberAdded = false;
+- //select a backup node if we don't have one
+- synchronized (mapMembers) {
+- if (!mapMembers.containsKey(member) ) {
+- mapMembers.put(member, new Long(System.currentTimeMillis()));
+- memberAdded = true;
+- }
+- }
+- if ( memberAdded ) {
+- synchronized (stateMutex) {
+- Iterator i = super.entrySet().iterator();
+- while (i.hasNext()) {
+- Map.Entry e = (Map.Entry) i.next();
+- MapEntry entry = (MapEntry) super.get(e.getKey());
+- if ( entry == null ) continue;
+- if (entry.isPrimary() && (entry.getBackupNodes() == null ||
entry.getBackupNodes().length == 0)) {
+- try {
+- Member[] backup = publishEntryInfo(entry.getKey(),
entry.getValue());
+- entry.setBackupNodes(backup);
+- entry.setPrimary(channel.getLocalMember(false));
+- } catch (ChannelException x) {
+- log.error("Unable to select backup node.", x);
+- } //catch
+- } //end if
+- } //while
+- } //synchronized
+- }//end if
+- }
+-
+- public boolean inSet(Member m, Member[] set) {
+- if ( set == null ) return false;
+- boolean result = false;
+- for (int i=0; i<set.length && (!result); i++ )
+- if ( m.equals(set[i]) ) result = true;
+- return result;
+- }
+-
+- public Member[] excludeFromSet(Member[] mbrs, Member[] set) {
+- ArrayList result = new ArrayList();
+- for (int i=0; i<set.length; i++ ) {
+- boolean include = true;
+- for (int j=0; j<mbrs.length; j++ )
+- if ( mbrs[j].equals(set[i]) ) include = false;
+- if ( include ) result.add(set[i]);
+- }
+- return (Member[])result.toArray(new Member[result.size()]);
+- }
+-
+- public void memberAdded(Member member) {
+- //do nothing
+- }
+-
+- public void memberDisappeared(Member member) {
+- boolean removed = false;
+- synchronized (mapMembers) {
+- removed = (mapMembers.remove(member) != null );
+- if (!removed) {
+- if (log.isDebugEnabled()) log.debug("Member["+member+"]
disappeared, but was not present in the map.");
+- return; //the member was not part of our map.
+- }
+- }
+-
+- Iterator i = super.entrySet().iterator();
+- while (i.hasNext()) {
+- Map.Entry e = (Map.Entry) i.next();
+- MapEntry entry = (MapEntry) super.get(e.getKey());
+- if (entry==null) continue;
+- if (entry.isPrimary() && inSet(member,entry.getBackupNodes())) {
+- if (log.isDebugEnabled()) log.debug("[1] Primary choosing a new
backup");
+- try {
+- Member[] backup = publishEntryInfo(entry.getKey(),
entry.getValue());
+- entry.setBackupNodes(backup);
+- entry.setPrimary(channel.getLocalMember(false));
+- } catch (ChannelException x) {
+- log.error("Unable to relocate[" + entry.getKey() + "]
to a new backup node", x);
+- }
+- } else if (member.equals(entry.getPrimary())) {
+- if (log.isDebugEnabled()) log.debug("[2] Primary
disappeared");
+- entry.setPrimary(null);
+- } //end if
+-
+- if ( entry.isProxy() &&
+- entry.getPrimary() == null &&
+- entry.getBackupNodes()!=null &&
+- entry.getBackupNodes().length == 1 &&
+- entry.getBackupNodes()[0].equals(member) ) {
+- //remove proxies that have no backup nor primaries
+- if (log.isDebugEnabled()) log.debug("[3] Removing orphaned
proxy");
+- i.remove();
+- } else if ( entry.getPrimary() == null &&
+- entry.isBackup() &&
+- entry.getBackupNodes()!=null &&
+- entry.getBackupNodes().length == 1 &&
+- entry.getBackupNodes()[0].equals(channel.getLocalMember(false))
) {
+- try {
+- if (log.isDebugEnabled()) log.debug("[4] Backup becoming
primary");
+- entry.setPrimary(channel.getLocalMember(false));
+- entry.setBackup(false);
+- entry.setProxy(false);
+- Member[] backup = publishEntryInfo(entry.getKey(),
entry.getValue());
+- entry.setBackupNodes(backup);
+- if ( mapOwner!=null )
mapOwner.objectMadePrimay(entry.getKey(),entry.getValue());
+-
+- } catch (ChannelException x) {
+- log.error("Unable to relocate[" + entry.getKey() + "]
to a new backup node", x);
+- }
+- }
+-
+- } //while
+- }
+-
+- public int getNextBackupIndex() {
+- int size = mapMembers.size();
+- if (mapMembers.size() == 0)return -1;
+- int node = currentNode++;
+- if (node >= size) {
+- node = 0;
+- currentNode = 0;
+- }
+- return node;
+- }
+- public Member getNextBackupNode() {
+- Member[] members = getMapMembers();
+- int node = getNextBackupIndex();
+- if ( members.length == 0 || node==-1) return null;
+- if ( node >= members.length ) node = 0;
+- return members[node];
+- }
+-
+- protected abstract Member[] publishEntryInfo(Object key, Object value) throws
ChannelException;
+-
+- public void heartbeat() {
+- try {
+- ping(accessTimeout);
+- }catch ( Exception x ) {
+- log.error("Unable to send AbstractReplicatedMap.ping message",x);
+- }
+- }
+-
+-//------------------------------------------------------------------------------
+-// METHODS TO OVERRIDE
+-//------------------------------------------------------------------------------
+-
+- /**
+- * Removes an object from this map, it will also remove it from
+- *
+- * @param key Object
+- * @return Object
+- */
+- public Object remove(Object key) {
+- return remove(key,true);
+- }
+- public Object remove(Object key, boolean notify) {
+- MapEntry entry = (MapEntry)super.remove(key);
+-
+- try {
+- if (getMapMembers().length > 0 && notify) {
+- MapMessage msg = new MapMessage(getMapContextName(),
MapMessage.MSG_REMOVE, false, (Serializable) key, null, null, null,null);
+- getChannel().send(getMapMembers(), msg, getChannelSendOptions());
+- }
+- } catch ( ChannelException x ) {
+- log.error("Unable to replicate out data for a LazyReplicatedMap.remove
operation",x);
+- }
+- return entry!=null?entry.getValue():null;
+- }
+-
+- public MapEntry getInternal(Object key) {
+- return (MapEntry)super.get(key);
+- }
+-
+- public Object get(Object key) {
+- MapEntry entry = (MapEntry)super.get(key);
+- if (log.isTraceEnabled()) log.trace("Requesting id:"+key+"
entry:"+entry);
+- if ( entry == null ) return null;
+- if ( !entry.isPrimary() ) {
+- //if the message is not primary, we need to retrieve the latest value
+- try {
+- Member[] backup = null;
+- MapMessage msg = null;
+- if ( !entry.isBackup() ) {
+- //make sure we don't retrieve from ourselves
+- msg = new MapMessage(getMapContextName(),
MapMessage.MSG_RETRIEVE_BACKUP, false,
+- (Serializable) key, null, null, null,null);
+- Response[] resp = getRpcChannel().send(entry.getBackupNodes(),msg,
this.getRpcChannel().FIRST_REPLY, Channel.SEND_OPTIONS_DEFAULT, getRpcTimeout());
+- if (resp == null || resp.length == 0) {
+- //no responses
+- log.warn("Unable to retrieve remote object for key:" +
key);
+- return null;
+- }
+- msg = (MapMessage) resp[0].getMessage();
+- msg.deserialize(getExternalLoaders());
+- backup = entry.getBackupNodes();
+- if ( entry.getValue() instanceof ReplicatedMapEntry ) {
+- ReplicatedMapEntry val = (ReplicatedMapEntry)entry.getValue();
+- val.setOwner(getMapOwner());
+- }
+- if ( msg.getValue()!=null ) entry.setValue(msg.getValue());
+- }
+- if (entry.isBackup()) {
+- //select a new backup node
+- backup = publishEntryInfo(key, entry.getValue());
+- } else if ( entry.isProxy() ) {
+- //invalidate the previous primary
+- msg = new
MapMessage(getMapContextName(),MapMessage.MSG_PROXY,false,(Serializable)key,null,null,channel.getLocalMember(false),backup);
+- Member[] dest = getMapMembersExcl(backup);
+- if ( dest!=null && dest.length >0) {
+- getChannel().send(dest, msg, getChannelSendOptions());
+- }
+- }
+- entry.setPrimary(channel.getLocalMember(false));
+- entry.setBackupNodes(backup);
+- entry.setBackup(false);
+- entry.setProxy(false);
+-
+-
+- } catch (Exception x) {
+- log.error("Unable to replicate out data for a LazyReplicatedMap.get
operation", x);
+- return null;
+- }
+- }
+- if (log.isTraceEnabled()) log.trace("Requesting id:"+key+"
result:"+entry.getValue());
+- if ( entry.getValue() != null && entry.getValue() instanceof
ReplicatedMapEntry ) {
+- ReplicatedMapEntry val = (ReplicatedMapEntry)entry.getValue();
+- //hack, somehow this is not being set above
+- val.setOwner(getMapOwner());
+-
+- }
+- return entry.getValue();
+- }
+-
+-
+- protected void printMap(String header) {
+- try {
+- System.out.println("\nDEBUG MAP:"+header);
+- System.out.println("Map["+ new String(mapContextName, chset) +
", Map Size:" + super.size());
+- Member[] mbrs = getMapMembers();
+- for ( int i=0; i<mbrs.length;i++ ) {
+-
System.out.println("Mbr["+(i+1)+"="+mbrs[i].getName());
+- }
+- Iterator i = super.entrySet().iterator();
+- int cnt = 0;
+-
+- while (i.hasNext()) {
+- Map.Entry e = (Map.Entry) i.next();
+- System.out.println( (++cnt) + ". " + super.get(e.getKey()));
+- }
+- System.out.println("EndMap]\n\n");
+- }catch ( Exception ignore) {
+- ignore.printStackTrace();
+- }
+- }
+-
+- /**
+- * Returns true if the key has an entry in the map.
+- * The entry can be a proxy or a backup entry, invoking
<code>get(key)</code>
+- * will make this entry primary for the group
+- * @param key Object
+- * @return boolean
+- */
+- public boolean containsKey(Object key) {
+- return super.containsKey(key);
+- }
+-
+- public Object put(Object key, Object value) {
+- return put(key,value,true);
+- }
+-
+- public Object put(Object key, Object value, boolean notify) {
+- MapEntry entry = new MapEntry(key,value);
+- entry.setBackup(false);
+- entry.setProxy(false);
+- entry.setPrimary(channel.getLocalMember(false));
+-
+- Object old = null;
+-
+- //make sure that any old values get removed
+- if ( containsKey(key) ) old = remove(key);
+- try {
+- if ( notify ) {
+- Member[] backup = publishEntryInfo(key, value);
+- entry.setBackupNodes(backup);
+- }
+- } catch (ChannelException x) {
+- log.error("Unable to replicate out data for a LazyReplicatedMap.put
operation", x);
+- }
+- super.put(key,entry);
+- return old;
+- }
+-
+-
+- /**
+- * Copies all values from one map to this instance
+- * @param m Map
+- */
+- public void putAll(Map m) {
+- Iterator i = m.entrySet().iterator();
+- while ( i.hasNext() ) {
+- Map.Entry entry = (Map.Entry)i.next();
+- put(entry.getKey(),entry.getValue());
+- }
+- }
+-
+- public void clear() {
+- clear(true);
+- }
+-
+- public void clear(boolean notify) {
+- if ( notify ) {
+- //only delete active keys
+- Iterator keys = keySet().iterator();
+- while (keys.hasNext())
+- remove(keys.next());
+- } else {
+- super.clear();
+- }
+- }
+-
+- public boolean containsValue(Object value) {
+- if ( value == null ) {
+- return super.containsValue(value);
+- } else {
+- Iterator i = super.entrySet().iterator();
+- while (i.hasNext()) {
+- Map.Entry e = (Map.Entry) i.next();
+- MapEntry entry = (MapEntry) super.get(e.getKey());
+- if (entry!=null && entry.isPrimary() &&
value.equals(entry.getValue())) return true;
+- }//while
+- return false;
+- }//end if
+- }
+-
+- public Object clone() {
+- throw new UnsupportedOperationException("This operation is not valid on
a replicated map");
+- }
+-
+- /**
+- * Returns the entire contents of the map
+- * Map.Entry.getValue() will return a LazyReplicatedMap.MapEntry object
containing all the information
+- * about the object.
+- * @return Set
+- */
+- public Set entrySetFull() {
+- return super.entrySet();
+- }
+-
+- public Set keySetFull() {
+- return super.keySet();
+- }
+-
+- public int sizeFull() {
+- return super.size();
+- }
+-
+- public Set entrySet() {
+- LinkedHashSet set = new LinkedHashSet(super.size());
+- Iterator i = super.entrySet().iterator();
+- while ( i.hasNext() ) {
+- Map.Entry e = (Map.Entry)i.next();
+- Object key = e.getKey();
+- MapEntry entry = (MapEntry)super.get(key);
+- if ( entry != null && entry.isPrimary() )
set.add(entry.getValue());
+- }
+- return Collections.unmodifiableSet(set);
+- }
+-
+- public Set keySet() {
+- //todo implement
+- //should only return keys where this is active.
+- LinkedHashSet set = new LinkedHashSet(super.size());
+- Iterator i = super.entrySet().iterator();
+- while ( i.hasNext() ) {
+- Map.Entry e = (Map.Entry)i.next();
+- Object key = e.getKey();
+- MapEntry entry = (MapEntry)super.get(key);
+- if ( entry!=null && entry.isPrimary() ) set.add(key);
+- }
+- return Collections.unmodifiableSet(set);
+-
+- }
+-
+-
+- public int size() {
+- //todo, implement a counter variable instead
+- //only count active members in this node
+- int counter = 0;
+- Iterator it = super.entrySet().iterator();
+- while (it!=null && it.hasNext() ) {
+- Map.Entry e = (Map.Entry) it.next();
+- if ( e != null ) {
+- MapEntry entry = (MapEntry) super.get(e.getKey());
+- if (entry!=null && entry.isPrimary() &&
entry.getValue() != null) counter++;
+- }
+- }
+- return counter;
+- }
+-
+- protected boolean removeEldestEntry(Map.Entry eldest) {
+- return false;
+- }
+-
+- public boolean isEmpty() {
+- return size()==0;
+- }
+-
+- public Collection values() {
+- ArrayList values = new ArrayList();
+- Iterator i = super.entrySet().iterator();
+- while ( i.hasNext() ) {
+- Map.Entry e = (Map.Entry)i.next();
+- MapEntry entry = (MapEntry)super.get(e.getKey());
+- if (entry!=null && entry.isPrimary() &&
entry.getValue()!=null) values.add(entry.getValue());
+- }
+- return Collections.unmodifiableCollection(values);
+- }
+-
+-
+-//------------------------------------------------------------------------------
+-// Map Entry class
+-//------------------------------------------------------------------------------
+- public static class MapEntry implements Map.Entry {
+- private boolean backup;
+- private boolean proxy;
+- private Member[] backupNodes;
+- private Member primary;
+- private Object key;
+- private Object value;
+-
+- public MapEntry(Object key, Object value) {
+- setKey(key);
+- setValue(value);
+-
+- }
+-
+- public boolean isKeySerializable() {
+- return (key == null) || (key instanceof Serializable);
+- }
+-
+- public boolean isValueSerializable() {
+- return (value==null) || (value instanceof Serializable);
+- }
+-
+- public boolean isSerializable() {
+- return isKeySerializable() && isValueSerializable();
+- }
+-
+- public boolean isBackup() {
+- return backup;
+- }
+-
+- public void setBackup(boolean backup) {
+- this.backup = backup;
+- }
+-
+- public boolean isProxy() {
+- return proxy;
+- }
+-
+- public boolean isPrimary() {
+- return ( (!proxy) && (!backup));
+- }
+-
+- public void setProxy(boolean proxy) {
+- this.proxy = proxy;
+- }
+-
+- public boolean isDiffable() {
+- return (value instanceof ReplicatedMapEntry) &&
+- ((ReplicatedMapEntry)value).isDiffable();
+- }
+-
+- public void setBackupNodes(Member[] nodes) {
+- this.backupNodes = nodes;
+- }
+-
+- public Member[] getBackupNodes() {
+- return backupNodes;
+- }
+-
+- public void setPrimary(Member m) {
+- primary = m;
+- }
+-
+- public Member getPrimary() {
+- return primary;
+- }
+-
+- public Object getValue() {
+- return value;
+- }
+-
+- public Object setValue(Object value) {
+- Object old = this.value;
+- this.value = value;
+- return old;
+- }
+-
+- public Object getKey() {
+- return key;
+- }
+-
+- public Object setKey(Object key) {
+- Object old = this.key;
+- this.key = key;
+- return old;
+- }
+-
+- public int hashCode() {
+- return key.hashCode();
+- }
+-
+- public boolean equals(Object o) {
+- return key.equals(o);
+- }
+-
+- /**
+- * apply a diff, or an entire object
+- * @param data byte[]
+- * @param offset int
+- * @param length int
+- * @param diff boolean
+- * @throws IOException
+- * @throws ClassNotFoundException
+- */
+- public void apply(byte[] data, int offset, int length, boolean diff) throws
IOException, ClassNotFoundException {
+- if (isDiffable() && diff) {
+- ReplicatedMapEntry rentry = (ReplicatedMapEntry) value;
+- try {
+- rentry.lock();
+- rentry.applyDiff(data, offset, length);
+- } finally {
+- rentry.unlock();
+- }
+- } else if (length == 0) {
+- value = null;
+- proxy = true;
+- } else {
+- value = XByteBuffer.deserialize(data, offset, length);
+- }
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("MapEntry[key:");
+- buf.append(getKey()).append("; ");
+- buf.append("value:").append(getValue()).append("; ");
+- buf.append("primary:").append(isPrimary()).append(";
");
+- buf.append("backup:").append(isBackup()).append("; ");
+- buf.append("proxy:").append(isProxy()).append(";]");
+- return buf.toString();
+- }
+-
+- }
+-
+-//------------------------------------------------------------------------------
+-// map message to send to and from other maps
+-//------------------------------------------------------------------------------
+-
+- public static class MapMessage implements Serializable {
+- public static final int MSG_BACKUP = 1;
+- public static final int MSG_RETRIEVE_BACKUP = 2;
+- public static final int MSG_PROXY = 3;
+- public static final int MSG_REMOVE = 4;
+- public static final int MSG_STATE = 5;
+- public static final int MSG_START = 6;
+- public static final int MSG_STOP = 7;
+- public static final int MSG_INIT = 8;
+- public static final int MSG_COPY = 9;
+- public static final int MSG_STATE_COPY = 10;
+-
+- private byte[] mapId;
+- private int msgtype;
+- private boolean diff;
+- private transient Serializable key;
+- private transient Serializable value;
+- private byte[] valuedata;
+- private byte[] keydata;
+- private byte[] diffvalue;
+- private Member[] nodes;
+- private Member primary;
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("MapMessage[context=");
+- buf.append(new String(mapId));
+- buf.append("; type=");
+- buf.append(getTypeDesc());
+- buf.append("; key=");
+- buf.append(key);
+- buf.append("; value=");
+- buf.append(value);
+- return buf.toString();
+- }
+-
+- public String getTypeDesc() {
+- switch (msgtype) {
+- case MSG_BACKUP: return "MSG_BACKUP";
+- case MSG_RETRIEVE_BACKUP: return "MSG_RETRIEVE_BACKUP";
+- case MSG_PROXY: return "MSG_PROXY";
+- case MSG_REMOVE: return "MSG_REMOVE";
+- case MSG_STATE: return "MSG_STATE";
+- case MSG_START: return "MSG_START";
+- case MSG_STOP: return "MSG_STOP";
+- case MSG_INIT: return "MSG_INIT";
+- case MSG_STATE_COPY: return "MSG_STATE_COPY";
+- case MSG_COPY: return "MSG_COPY";
+- default : return "UNKNOWN";
+- }
+- }
+-
+- public MapMessage() {}
+-
+- public MapMessage(byte[] mapId,int msgtype, boolean diff,
+- Serializable key, Serializable value,
+- byte[] diffvalue, Member primary, Member[] nodes) {
+- this.mapId = mapId;
+- this.msgtype = msgtype;
+- this.diff = diff;
+- this.key = key;
+- this.value = value;
+- this.diffvalue = diffvalue;
+- this.nodes = nodes;
+- this.primary = primary;
+- setValue(value);
+- setKey(key);
+- }
+-
+- public void deserialize(ClassLoader[] cls) throws IOException,
ClassNotFoundException {
+- key(cls);
+- value(cls);
+- }
+-
+- public int getMsgType() {
+- return msgtype;
+- }
+-
+- public boolean isDiff() {
+- return diff;
+- }
+-
+- public Serializable getKey() {
+- try {
+- return key(null);
+- } catch ( Exception x ) {
+- log.error("Deserialization error of the MapMessage.key",x);
+- return null;
+- }
+- }
+-
+- public Serializable key(ClassLoader[] cls) throws IOException,
ClassNotFoundException {
+- if ( key!=null ) return key;
+- if ( keydata == null || keydata.length == 0 ) return null;
+- key = XByteBuffer.deserialize(keydata,0,keydata.length,cls);
+- keydata = null;
+- return key;
+- }
+-
+- public byte[] getKeyData() {
+- return keydata;
+- }
+-
+- public Serializable getValue() {
+- try {
+- return value(null);
+- } catch ( Exception x ) {
+- log.error("Deserialization error of the MapMessage.value",x);
+- return null;
+- }
+- }
+-
+- public Serializable value(ClassLoader[] cls) throws IOException,
ClassNotFoundException {
+- if ( value!=null ) return value;
+- if ( valuedata == null || valuedata.length == 0 ) return null;
+- value = XByteBuffer.deserialize(valuedata,0,valuedata.length,cls);
+- valuedata = null;;
+- return value;
+- }
+-
+- public byte[] getValueData() {
+- return valuedata;
+- }
+-
+- public byte[] getDiffValue() {
+- return diffvalue;
+- }
+-
+- public Member[] getBackupNodes() {
+- return nodes;
+- }
+-
+- private void setBackUpNodes(Member[] nodes) {
+- this.nodes = nodes;
+- }
+-
+- public Member getPrimary() {
+- return primary;
+- }
+-
+- private void setPrimary(Member m) {
+- primary = m;
+- }
+-
+- public byte[] getMapId() {
+- return mapId;
+- }
+-
+- public void setValue(Serializable value) {
+- try {
+- if ( value != null ) valuedata = XByteBuffer.serialize(value);
+- this.value = value;
+- }catch ( IOException x ) {
+- throw new RuntimeException(x);
+- }
+- }
+-
+- public void setKey(Serializable key) {
+- try {
+- if (key != null) keydata = XByteBuffer.serialize(key);
+- this.key = key;
+- } catch (IOException x) {
+- throw new RuntimeException(x);
+- }
+- }
+-
+- protected Member[] readMembers(ObjectInput in) throws IOException,
ClassNotFoundException {
+- int nodecount = in.readInt();
+- Member[] members = new Member[nodecount];
+- for ( int i=0; i<members.length; i++ ) {
+- byte[] d = new byte[in.readInt()];
+- in.read(d);
+- if (d.length > 0) members[i] = MemberImpl.getMember(d);
+- }
+- return members;
+- }
+-
+- protected void writeMembers(ObjectOutput out,Member[] members) throws
IOException {
+- if ( members == null ) members = new Member[0];
+- out.writeInt(members.length);
+- for (int i=0; i<members.length; i++ ) {
+- if ( members[i] != null ) {
+- byte[] d = members[i] != null ? (
(MemberImpl)members[i]).getData(false) : new byte[0];
+- out.writeInt(d.length);
+- out.write(d);
+- }
+- }
+- }
+-
+-
+- /**
+- * shallow clone
+- * @return Object
+- */
+- public Object clone() {
+- MapMessage msg = new MapMessage(this.mapId, this.msgtype, this.diff,
this.key, this.value, this.diffvalue, this.primary, this.nodes);
+- msg.keydata = this.keydata;
+- msg.valuedata = this.valuedata;
+- return msg;
+- }
+- } //MapMessage
+-
+-
+- public Channel getChannel() {
+- return channel;
+- }
+-
+- public byte[] getMapContextName() {
+- return mapContextName;
+- }
+-
+- public RpcChannel getRpcChannel() {
+- return rpcChannel;
+- }
+-
+- public long getRpcTimeout() {
+- return rpcTimeout;
+- }
+-
+- public Object getStateMutex() {
+- return stateMutex;
+- }
+-
+- public boolean isStateTransferred() {
+- return stateTransferred;
+- }
+-
+- public MapOwner getMapOwner() {
+- return mapOwner;
+- }
+-
+- public ClassLoader[] getExternalLoaders() {
+- return externalLoaders;
+- }
+-
+- public int getChannelSendOptions() {
+- return channelSendOptions;
+- }
+-
+- public long getAccessTimeout() {
+- return accessTimeout;
+- }
+-
+- public void setMapOwner(MapOwner mapOwner) {
+- this.mapOwner = mapOwner;
+- }
+-
+- public void setExternalLoaders(ClassLoader[] externalLoaders) {
+- this.externalLoaders = externalLoaders;
+- }
+-
+- public void setChannelSendOptions(int channelSendOptions) {
+- this.channelSendOptions = channelSendOptions;
+- }
+-
+- public void setAccessTimeout(long accessTimeout) {
+- this.accessTimeout = accessTimeout;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/RemoteProcessException.java
+===================================================================
+--- java/org/apache/catalina/tribes/RemoteProcessException.java (revision 590752)
++++ java/org/apache/catalina/tribes/RemoteProcessException.java (working copy)
+@@ -1,47 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-/**
+- * <p>Title: RemoteProcessException</p>
+- *
+- * <p>Description: Message thrown by a sender when USE_SYNC_ACK receives a
FAIL_ACK_COMMAND.<br>
+- * This means that the message was received on the remote node but the processing of the
message failed.
+- * This message will be embedded in a ChannelException.FaultyMember
+- * </p>
+- * @see ChannelException
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class RemoteProcessException extends RuntimeException {
+- public RemoteProcessException() {
+- super();
+- }
+-
+- public RemoteProcessException(String message) {
+- super(message);
+- }
+-
+- public RemoteProcessException(String message, Throwable cause) {
+- super(message, cause);
+- }
+-
+- public RemoteProcessException(Throwable cause) {
+- super(cause);
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/ChannelSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/ChannelSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/ChannelSender.java (working copy)
+@@ -1,69 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes;
+-
+-
+-/**
+- * ChannelReceiver Interface<br>
+- * The <code>ChannelSender</code> interface is the data sender component
+- * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel}
interface).<br>
+- * The channel sender must support "silent" members, ie, be able to send a
message to a member
+- * that is not in the membership, but is part of the destination parameter
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public interface ChannelSender extends Heartbeat
+-{
+- /**
+- * Notify the sender of a member being added to the group.<br>
+- * Optional. This can be an empty implementation, that does nothing
+- * @param member Member
+- */
+- public void add(Member member);
+- /**
+- * Notification that a member has been removed or crashed.
+- * Can be used to clean up open connections etc
+- * @param member Member
+- */
+- public void remove(Member member);
+-
+- /**
+- * Start the channel sender
+- * @throws IOException if preprocessing takes place and an error happens
+- */
+- public void start() throws java.io.IOException;
+- /**
+- * Stop the channel sender
+- */
+- public void stop();
+-
+- /**
+- * A channel heartbeat, use this method to clean up resources
+- */
+- public void heartbeat() ;
+-
+- /**
+- * Send a message to one or more recipients.
+- * @param message ChannelMessage - the message to be sent
+- * @param destination Member[] - the destinations
+- * @throws ChannelException - if an error happens, the ChannelSender MUST report
+- * individual send failures on a per member basis, using
ChannelException.addFaultyMember
+- * @see ChannelException#addFaultyMember(Member,java.lang.Exception)
+- */
+- public void sendMessage(ChannelMessage message, Member[] destination) throws
ChannelException;
+-}
+Index: java/org/apache/catalina/tribes/membership/LocalStrings.properties
+===================================================================
+--- java/org/apache/catalina/tribes/membership/LocalStrings.properties (revision 590752)
++++ java/org/apache/catalina/tribes/membership/LocalStrings.properties (working copy)
+@@ -1,16 +0,0 @@
+-# Licensed to the Apache Software Foundation (ASF) under one or more
+-# contributor license agreements. See the NOTICE file distributed with
+-# this work for additional information regarding copyright ownership.
+-# The ASF licenses this file to You under the Apache License, Version 2.0
+-# (the "License"); you may not use this file except in compliance with
+-# the License. You may obtain a copy of the License at
+-#
+-#
http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS,
+-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-# See the License for the specific language governing permissions and
+-# limitations under the License.
+-
+-cluster.mbean.register.already=MBean {0} already registered!
+Index: java/org/apache/catalina/tribes/membership/StaticMember.java
+===================================================================
+--- java/org/apache/catalina/tribes/membership/StaticMember.java (revision 590752)
++++ java/org/apache/catalina/tribes/membership/StaticMember.java (working copy)
+@@ -1,77 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.membership;
+-
+-import java.io.IOException;
+-import org.apache.catalina.tribes.util.Arrays;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class StaticMember extends MemberImpl {
+- public StaticMember() {
+- super();
+- }
+-
+- public StaticMember(String host, int port, long aliveTime) throws IOException {
+- super(host, port, aliveTime);
+- }
+-
+- public StaticMember(String host, int port, long aliveTime, byte[] payload) throws
IOException {
+- super(host, port, aliveTime, payload);
+- }
+-
+- /**
+- * @param host String, either in byte array string format, like {214,116,1,3}
+- * or as a regular hostname, 127.0.0.1 or
tomcat01.mydomain.com
+- */
+- public void setHost(String host) {
+- if ( host == null ) return;
+- if ( host.startsWith("{") ) setHost(Arrays.fromString(host));
+- else try { setHostname(host); }catch (IOException x) { throw new
RuntimeException(x);}
+-
+- }
+-
+- /**
+- * @param domain String, either in byte array string format, like {214,116,1,3}
+- * or as a regular string value like 'mydomain'. The latter will be
converted using ISO-8859-1 encoding
+- */
+- public void setDomain(String domain) {
+- if ( domain == null ) return;
+- if ( domain.startsWith("{") ) setDomain(Arrays.fromString(domain));
+- else setDomain(Arrays.convert(domain));
+- }
+-
+- /**
+- * @param id String, must be in byte array string format, like {214,116,1,3} and
exactly 16 bytes long
+- */
+- public void setUniqueId(String id) {
+- byte[] uuid = Arrays.fromString(id);
+- if ( uuid==null || uuid.length != 16 ) throw new RuntimeException("UUID
must be exactly 16 bytes, not:"+id);
+- setUniqueId(uuid);
+- }
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/tribes/membership/mbeans-descriptors.xml (working copy)
+@@ -1,89 +0,0 @@
+-<?xml version="1.0" encoding="UTF-8"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE mbeans-descriptors PUBLIC
+- "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+- "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+-<mbeans-descriptors>
+-
+- <mbean name="McastService"
+- description="Cluster Membership service implementation"
+- domain="Catalina"
+- group="Cluster"
+-
type="org.apache.catalina.tribes.membership.McastService">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="mcastAddr"
+- description="Multicast IP Address"
+- type="java.lang.String"/>
+- <attribute name="mcastBindAddress"
+- description="Multicast IP Interface address (default auto)"
+- type="java.lang.String"/>
+- <attribute name="mcastPort"
+- description="Multicast UDP Port"
+- type="int"/>
+- <attribute name="mcastFrequency"
+- description="Ping Frequency at msec"
+- type="long"/>
+- <attribute name="mcastClusterDomain"
+- description="Cluster Domain of this member"
+- type="java.lang.String"/>
+- <attribute name="mcastDropTime"
+- description="Timeout from frequency ping after member disapper
notify"
+- type="long"/>
+- <attribute name="mcastSoTimeout"
+- description="Multicast Socket Timeout"
+- type="int"/>
+- <attribute name="mcastTTL"
+- description=""
+- type="int"/>
+- <attribute name="recoveryCounter"
+- description="Counter after membership failure socket restarted"
+- type="int"/>
+- <attribute name="recoveryEnabled"
+- description="Membership recovery enabled"
+- is="true"
+- type="boolean"/>
+- <attribute name="recoverySleepTime"
+- description="Sleep time between next socket recovery (5000 msec)"
+- type="long"/>
+- <attribute name="localMemberName"
+- description="Complete local receiver information"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="membersByName"
+- description="Complete remote sender information"
+- type="[Ljava.lang.String;"
+- writeable="false"/>
+-
+- <operation name="start"
+- description="Start the cluster membership"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster membership"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/tribes/membership/McastService.java
+===================================================================
+--- java/org/apache/catalina/tribes/membership/McastService.java (revision 590752)
++++ java/org/apache/catalina/tribes/membership/McastService.java (working copy)
+@@ -1,574 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.membership;
+-
+-import java.util.Properties;
+-
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import org.apache.catalina.tribes.MembershipService;
+-import org.apache.catalina.tribes.util.StringManager;
+-import org.apache.catalina.tribes.util.UUIDGenerator;
+-import java.io.IOException;
+-
+-/**
+- * A <b>membership</b> implementation using simple multicast.
+- * This is the representation of a multicast membership service.
+- * This class is responsible for maintaining a list of active cluster nodes in the
cluster.
+- * If a node fails to send out a heartbeat, the node will be dismissed.
+- *
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-
+-public class McastService implements MembershipService,MembershipListener {
+-
+- private static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog( McastService.class );
+-
+- /**
+- * The string manager for this package.
+- */
+- protected StringManager sm = StringManager.getManager(Constants.Package);
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "McastService/2.1";
+-
+- /**
+- * The implementation specific properties
+- */
+- protected Properties properties = new Properties();
+- /**
+- * A handle to the actual low level implementation
+- */
+- protected McastServiceImpl impl;
+- /**
+- * A membership listener delegate (should be the cluster :)
+- */
+- protected MembershipListener listener;
+- /**
+- * The local member
+- */
+- protected MemberImpl localMember ;
+- private int mcastSoTimeout;
+- private int mcastTTL;
+-
+- protected byte[] payload;
+-
+- protected byte[] domain;
+-
+- /**
+- * Create a membership service.
+- */
+- public McastService() {
+- //default values
+- properties.setProperty("mcastPort","45564");
+- properties.setProperty("mcastAddress","228.0.0.4");
+- properties.setProperty("memberDropTime","3000");
+- properties.setProperty("mcastFrequency","500");
+-
+- }
+-
+- /**
+- * Return descriptive information about this implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+- return (info);
+- }
+-
+- /**
+- *
+- * @param properties
+- * <BR/>All are required<BR />
+- * 1. mcastPort - the port to listen to<BR>
+- * 2. mcastAddress - the mcast group address<BR>
+- * 4. bindAddress - the bind address if any - only one that can be null<BR>
+- * 5. memberDropTime - the time a member is gone before it is considered
gone.<BR>
+- * 6. mcastFrequency - the frequency of sending messages<BR>
+- * 7. tcpListenPort - the port this member listens to<BR>
+- * 8. tcpListenHost - the bind address of this member<BR>
+- * @exception java.lang.IllegalArgumentException if a property is missing.
+- */
+- public void setProperties(Properties properties) {
+- hasProperty(properties,"mcastPort");
+- hasProperty(properties,"mcastAddress");
+- hasProperty(properties,"memberDropTime");
+- hasProperty(properties,"mcastFrequency");
+- hasProperty(properties,"tcpListenPort");
+- hasProperty(properties,"tcpListenHost");
+- this.properties = properties;
+- }
+-
+- /**
+- * Return the properties, see setProperties
+- */
+- public Properties getProperties() {
+- return properties;
+- }
+-
+- /**
+- * Return the local member name
+- */
+- public String getLocalMemberName() {
+- return localMember.toString() ;
+- }
+-
+- /**
+- * Return the local member
+- */
+- public Member getLocalMember(boolean alive) {
+- if ( alive && localMember != null && impl != null)
localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());
+- return localMember;
+- }
+-
+- /**
+- * Sets the local member properties for broadcasting
+- */
+- public void setLocalMemberProperties(String listenHost, int listenPort) {
+- properties.setProperty("tcpListenHost",listenHost);
+- properties.setProperty("tcpListenPort",String.valueOf(listenPort));
+- try {
+- if (localMember != null) {
+- localMember.setHostname(listenHost);
+- localMember.setPort(listenPort);
+- } else {
+- localMember = new MemberImpl(listenHost, listenPort, 0);
+- localMember.setUniqueId(UUIDGenerator.randomUUID(true));
+- localMember.setPayload(getPayload());
+- localMember.setDomain(getDomain());
+- }
+- localMember.getData(true, true);
+- }catch ( IOException x ) {
+- throw new IllegalArgumentException(x);
+- }
+- }
+-
+- public void setAddress(String addr) {
+- properties.setProperty("mcastAddress", addr);
+- }
+-
+- /**
+- * @deprecated use setAddress
+- * @param addr String
+- */
+- public void setMcastAddr(String addr) {
+- setAddress(addr);
+- }
+-
+- public String getAddress() {
+- return properties.getProperty("mcastAddress");
+- }
+-
+- /**
+- * @deprecated use getAddress
+- * @return String
+- */
+- public String getMcastAddr() {
+- return getAddress();
+- }
+-
+- public void setMcastBindAddress(String bindaddr) {
+- setBind(bindaddr);
+- }
+-
+- public void setBind(String bindaddr) {
+- properties.setProperty("mcastBindAddress", bindaddr);
+- }
+- /**
+- * @deprecated use getBind
+- * @return String
+- */
+- public String getMcastBindAddress() {
+- return getBind();
+- }
+-
+- public String getBind() {
+- return properties.getProperty("mcastBindAddress");
+- }
+-
+- /**
+- * @deprecated use setPort
+- * @param port int
+- */
+- public void setMcastPort(int port) {
+- setPort(port);
+- }
+-
+- public void setPort(int port) {
+- properties.setProperty("mcastPort", String.valueOf(port));
+- }
+-
+- public void setRecoveryCounter(int recoveryCounter) {
+- properties.setProperty("recoveryCounter",
String.valueOf(recoveryCounter));
+- }
+-
+- public void setRecoveryEnabled(boolean recoveryEnabled) {
+- properties.setProperty("recoveryEnabled",
String.valueOf(recoveryEnabled));
+- }
+-
+- public void setRecoverySleepTime(long recoverySleepTime) {
+- properties.setProperty("recoverySleepTime",
String.valueOf(recoverySleepTime));
+- }
+-
+-
+- /**
+- * @deprecated use getPort()
+- * @return int
+- */
+- public int getMcastPort() {
+- return getPort();
+- }
+- public int getPort() {
+- String p = properties.getProperty("mcastPort");
+- return new Integer(p).intValue();
+- }
+-
+- /**
+- * @deprecated use setFrequency
+- * @param time long
+- */
+- public void setMcastFrequency(long time) {
+- setFrequency(time);
+- }
+-
+- public void setFrequency(long time) {
+- properties.setProperty("mcastFrequency", String.valueOf(time));
+- }
+-
+- /**
+- * @deprecated use getFrequency
+- * @return long
+- */
+- public long getMcastFrequency() {
+- return getFrequency();
+- }
+-
+- public long getFrequency() {
+- String p = properties.getProperty("mcastFrequency");
+- return new Long(p).longValue();
+- }
+-
+- public void setMcastDropTime(long time) {
+- setDropTime(time);
+- }
+- public void setDropTime(long time) {
+- properties.setProperty("memberDropTime", String.valueOf(time));
+- }
+-
+- /**
+- * @deprecated use getDropTime
+- * @return long
+- */
+- public long getMcastDropTime() {
+- return getDropTime();
+- }
+-
+- public long getDropTime() {
+- String p = properties.getProperty("memberDropTime");
+- return new Long(p).longValue();
+- }
+-
+- /**
+- * Check if a required property is available.
+- * @param properties The set of properties
+- * @param name The property to check for
+- */
+- protected void hasProperty(Properties properties, String name){
+- if ( properties.getProperty(name)==null) throw new
IllegalArgumentException("McastService:Required property
\""+name+"\" is missing.");
+- }
+-
+- /**
+- * Start broadcasting and listening to membership pings
+- * @throws java.lang.Exception if a IO error occurs
+- */
+- public void start() throws java.lang.Exception {
+- start(MembershipService.MBR_RX);
+- start(MembershipService.MBR_TX);
+- }
+-
+- public void start(int level) throws java.lang.Exception {
+- hasProperty(properties,"mcastPort");
+- hasProperty(properties,"mcastAddress");
+- hasProperty(properties,"memberDropTime");
+- hasProperty(properties,"mcastFrequency");
+- hasProperty(properties,"tcpListenPort");
+- hasProperty(properties,"tcpListenHost");
+-
+- if ( impl != null ) {
+- impl.start(level);
+- return;
+- }
+- String host = getProperties().getProperty("tcpListenHost");
+- int port =
Integer.parseInt(getProperties().getProperty("tcpListenPort"));
+-
+- if ( localMember == null ) {
+- localMember = new MemberImpl(host, port, 100);
+- localMember.setUniqueId(UUIDGenerator.randomUUID(true));
+- } else {
+- localMember.setHostname(host);
+- localMember.setPort(port);
+- localMember.setMemberAliveTime(100);
+- }
+- if ( this.payload != null ) localMember.setPayload(payload);
+- if ( this.domain != null ) localMember.setDomain(domain);
+- localMember.setServiceStartTime(System.currentTimeMillis());
+- java.net.InetAddress bind = null;
+- if ( properties.getProperty("mcastBindAddress")!= null ) {
+- bind =
java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));
+- }
+- int ttl = -1;
+- int soTimeout = -1;
+- if ( properties.getProperty("mcastTTL") != null ) {
+- try {
+- ttl = Integer.parseInt(properties.getProperty("mcastTTL"));
+- } catch ( Exception x ) {
+- log.error("Unable to parse
mcastTTL="+properties.getProperty("mcastTTL"),x);
+- }
+- }
+- if ( properties.getProperty("mcastSoTimeout") != null ) {
+- try {
+- soTimeout =
Integer.parseInt(properties.getProperty("mcastSoTimeout"));
+- } catch ( Exception x ) {
+- log.error("Unable to parse
mcastSoTimeout="+properties.getProperty("mcastSoTimeout"),x);
+- }
+- }
+-
+- impl = new
McastServiceImpl((MemberImpl)localMember,Long.parseLong(properties.getProperty("mcastFrequency")),
+-
Long.parseLong(properties.getProperty("memberDropTime")),
+-
Integer.parseInt(properties.getProperty("mcastPort")),
+- bind,
+-
java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),
+- ttl,
+- soTimeout,
+- this);
+- String value =
properties.getProperty("recoveryEnabled","true");
+- boolean recEnabled = Boolean.valueOf(value).booleanValue() ;
+- impl.setRecoveryEnabled(recEnabled);
+- int recCnt =
Integer.parseInt(properties.getProperty("recoveryCounter","10"));
+- impl.setRecoveryCounter(recCnt);
+- long recSlpTime =
Long.parseLong(properties.getProperty("recoverySleepTime","5000"));
+- impl.setRecoverySleepTime(recSlpTime);
+-
+-
+- impl.start(level);
+-
+-
+- }
+-
+-
+- /**
+- * Stop broadcasting and listening to membership pings
+- */
+- public void stop(int svc) {
+- try {
+- if ( impl != null && impl.stop(svc) ) impl = null;
+- } catch ( Exception x) {
+- log.error("Unable to stop the mcast service,
level:"+svc+".",x);
+- }
+- }
+-
+-
+- /**
+- * Return all the members by name
+- */
+- public String[] getMembersByName() {
+- Member[] currentMembers = getMembers();
+- String [] membernames ;
+- if(currentMembers != null) {
+- membernames = new String[currentMembers.length];
+- for (int i = 0; i < currentMembers.length; i++) {
+- membernames[i] = currentMembers[i].toString() ;
+- }
+- } else
+- membernames = new String[0] ;
+- return membernames ;
+- }
+-
+- /**
+- * Return the member by name
+- */
+- public Member findMemberByName(String name) {
+- Member[] currentMembers = getMembers();
+- for (int i = 0; i < currentMembers.length; i++) {
+- if (name.equals(currentMembers[i].toString()))
+- return currentMembers[i];
+- }
+- return null;
+- }
+-
+- /**
+- * has members?
+- */
+- public boolean hasMembers() {
+- if ( impl == null || impl.membership == null ) return false;
+- return impl.membership.hasMembers();
+- }
+-
+- public Member getMember(Member mbr) {
+- if ( impl == null || impl.membership == null ) return null;
+- return impl.membership.getMember(mbr);
+- }
+-
+- /**
+- * Return all the members
+- */
+- protected static final Member[]EMPTY_MEMBERS = new Member[0];
+- public Member[] getMembers() {
+- if ( impl == null || impl.membership == null ) return EMPTY_MEMBERS;
+- return impl.membership.getMembers();
+- }
+- /**
+- * Add a membership listener, this version only supports one listener per service,
+- * so calling this method twice will result in only the second listener being
active.
+- * @param listener The listener
+- */
+- public void setMembershipListener(MembershipListener listener) {
+- this.listener = listener;
+- }
+- /**
+- * Remove the membership listener
+- */
+- public void removeMembershipListener(){
+- listener = null;
+- }
+-
+- public void memberAdded(Member member) {
+- if ( listener!=null ) listener.memberAdded(member);
+- }
+-
+- /**
+- * Callback from the impl when a new member has been received
+- * @param member The member
+- */
+- public void memberDisappeared(Member member)
+- {
+- if ( listener!=null ) listener.memberDisappeared(member);
+- }
+-
+- /**
+- * @deprecated use getSoTimeout
+- * @return int
+- */
+- public int getMcastSoTimeout() {
+- return getSoTimeout();
+- }
+-
+- public int getSoTimeout() {
+- return mcastSoTimeout;
+- }
+-
+- /**
+- * @deprecated use setSoTimeout
+- * @param mcastSoTimeout int
+- */
+- public void setMcastSoTimeout(int mcastSoTimeout) {
+- setSoTimeout(mcastSoTimeout);
+- }
+-
+- public void setSoTimeout(int mcastSoTimeout) {
+- this.mcastSoTimeout = mcastSoTimeout;
+- properties.setProperty("mcastSoTimeout",
String.valueOf(mcastSoTimeout));
+- }
+-
+- /**
+- * @deprecated use getTtl
+- * @return int
+- */
+- public int getMcastTTL() {
+- return getTtl();
+- }
+-
+- public int getTtl() {
+- return mcastTTL;
+- }
+-
+- public byte[] getPayload() {
+- return payload;
+- }
+-
+- public byte[] getDomain() {
+- return domain;
+- }
+-
+- /**
+- * @deprecated use setTtl
+- * @param mcastTTL int
+- */
+- public void setMcastTTL(int mcastTTL) {
+- setTtl(mcastTTL);
+- }
+-
+- public void setTtl(int mcastTTL) {
+- this.mcastTTL = mcastTTL;
+- properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
+- }
+-
+- public void setPayload(byte[] payload) {
+- this.payload = payload;
+- if ( localMember != null ) {
+- localMember.setPayload(payload);
+- localMember.getData(true,true);
+- try {
+- if (impl != null) impl.send(false);
+- }catch ( Exception x ) {
+- log.error("Unable to send payload update.",x);
+- }
+- }
+- }
+-
+- public void setDomain(byte[] domain) {
+- this.domain = domain;
+- if ( localMember != null ) {
+- localMember.setDomain(domain);
+- localMember.getData(true,true);
+- try {
+- if (impl != null) impl.send(false);
+- }catch ( Exception x ) {
+- log.error("Unable to send domain update.",x);
+- }
+- }
+- }
+-
+- /**
+- * Simple test program
+- * @param args Command-line arguments
+- * @throws Exception If an error occurs
+- */
+- public static void main(String args[]) throws Exception {
+- if(log.isInfoEnabled())
+- log.info("Usage McastService hostname tcpport");
+- McastService service = new McastService();
+- java.util.Properties p = new java.util.Properties();
+- p.setProperty("mcastPort","5555");
+- p.setProperty("mcastAddress","224.10.10.10");
+- p.setProperty("mcastClusterDomain","catalina");
+- p.setProperty("bindAddress","localhost");
+- p.setProperty("memberDropTime","3000");
+- p.setProperty("mcastFrequency","500");
+- p.setProperty("tcpListenPort","4000");
+- p.setProperty("tcpListenHost","127.0.0.1");
+- service.setProperties(p);
+- service.start();
+- Thread.sleep(60*1000*60);
+- }
+-}
+Index: java/org/apache/catalina/tribes/membership/MemberImpl.java
+===================================================================
+--- java/org/apache/catalina/tribes/membership/MemberImpl.java (revision 590752)
++++ java/org/apache/catalina/tribes/membership/MemberImpl.java (working copy)
+@@ -1,601 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.membership;
+-
+-import java.io.IOException;
+-import java.io.ObjectInput;
+-import java.io.ObjectOutput;
+-import java.util.Arrays;
+-
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.transport.SenderState;
+-
+-/**
+- * A <b>membership</b> implementation using simple multicast.
+- * This is the representation of a multicast member.
+- * Carries the host, and port of the this or other cluster nodes.
+- *
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public class MemberImpl implements Member, java.io.Externalizable {
+-
+- /**
+- * Public properties specific to this implementation
+- */
+- public static final transient String TCP_LISTEN_PORT = "tcpListenPort";
+- public static final transient String TCP_LISTEN_HOST = "tcpListenHost";
+- public static final transient String MEMBER_NAME = "memberName";
+-
+- public static final transient byte[] TRIBES_MBR_BEGIN = new byte[] {84, 82, 73, 66,
69, 83, 45, 66};
+- public static final transient byte[] TRIBES_MBR_END = new byte[] {84, 82, 73, 66,
69, 83, 45, 69};
+-
+- /**
+- * The listen host for this member
+- */
+- protected byte[] host;
+- protected transient String hostname;
+- /**
+- * The tcp listen port for this member
+- */
+- protected int port;
+-
+- /**
+- * The tcp/SSL listen port for this member
+- */
+- protected int securePort = -1;
+-
+- /**
+- * Counter for how many broadcast messages have been sent from this member
+- */
+- protected int msgCount = 0;
+- /**
+- * The number of milliseconds since this members was
+- * created, is kept track of using the start time
+- */
+- protected long memberAliveTime = 0;
+-
+- /**
+- * For the local member only
+- */
+- protected transient long serviceStartTime;
+-
+- /**
+- * To avoid serialization over and over again, once the local dataPkg
+- * has been set, we use that to transmit data
+- */
+- protected transient byte[] dataPkg = null;
+-
+- /**
+- * Unique session Id for this member
+- */
+- protected byte[] uniqueId = new byte[16];
+-
+- /**
+- * Custom payload that an app framework can broadcast
+- * Also used to transport stop command.
+- */
+- protected byte[] payload = new byte[0];
+-
+- /**
+- * Command, so that the custom payload doesn't have to be used
+- * This is for internal tribes use, such as SHUTDOWN_COMMAND
+- */
+- protected byte[] command = new byte[0];
+-
+- /**
+- * Domain if we want to filter based on domain.
+- */
+- protected byte[] domain = new byte[0];
+-
+- /**
+- * Empty constructor for serialization
+- */
+- public MemberImpl() {
+-
+- }
+-
+- /**
+- * Construct a new member object
+- * @param name - the name of this member, cluster unique
+- * @param domain - the cluster domain name of this member
+- * @param host - the tcp listen host
+- * @param port - the tcp listen port
+- */
+- public MemberImpl(String host,
+- int port,
+- long aliveTime) throws IOException {
+- setHostname(host);
+- this.port = port;
+- this.memberAliveTime=aliveTime;
+- }
+-
+- public MemberImpl(String host,
+- int port,
+- long aliveTime,
+- byte[] payload) throws IOException {
+- this(host,port,aliveTime);
+- setPayload(payload);
+- }
+-
+- public boolean isReady() {
+- return SenderState.getSenderState(this).isReady();
+- }
+- public boolean isSuspect() {
+- return SenderState.getSenderState(this).isSuspect();
+- }
+- public boolean isFailing() {
+- return SenderState.getSenderState(this).isFailing();
+- }
+-
+- /**
+- * Increment the message count.
+- */
+- protected void inc() {
+- msgCount++;
+- }
+-
+- /**
+- * Create a data package to send over the wire representing this member.
+- * This is faster than serialization.
+- * @return - the bytes for this member deserialized
+- * @throws Exception
+- */
+- public byte[] getData() {
+- return getData(true);
+- }
+- /**
+- * Highly optimized version of serializing a member into a byte array
+- * Returns a cached byte[] reference, do not modify this data
+- * @param getalive boolean
+- * @return byte[]
+- */
+- public byte[] getData(boolean getalive) {
+- return getData(getalive,false);
+- }
+-
+-
+- public int getDataLength() {
+- return TRIBES_MBR_BEGIN.length+ //start pkg
+- 4+ //data length
+- 8+ //alive time
+- 4+ //port
+- 4+ //secure port
+- 1+ //host length
+- host.length+ //host
+- 4+ //command length
+- command.length+ //command
+- 4+ //domain length
+- domain.length+ //domain
+- 16+ //unique id
+- 4+ //payload length
+- payload.length+ //payload
+- TRIBES_MBR_END.length; //end pkg
+- }
+-
+- /**
+- *
+- * @param getalive boolean - calculate memberAlive time
+- * @param reset boolean - reset the cached data package, and create a new one
+- * @return byte[]
+- */
+- public byte[] getData(boolean getalive, boolean reset) {
+- if ( reset ) dataPkg = null;
+- //look in cache first
+- if ( dataPkg!=null ) {
+- if ( getalive ) {
+- //you'd be surprised, but System.currentTimeMillis
+- //shows up on the profiler
+- long alive=System.currentTimeMillis()-getServiceStartTime();
+- XByteBuffer.toBytes( (long) alive, dataPkg, TRIBES_MBR_BEGIN.length+4);
+- }
+- return dataPkg;
+- }
+-
+- //package looks like
+- //start package TRIBES_MBR_BEGIN.length
+- //package length - 4 bytes
+- //alive - 8 bytes
+- //port - 4 bytes
+- //secure port - 4 bytes
+- //host length - 1 byte
+- //host - hl bytes
+- //clen - 4 bytes
+- //command - clen bytes
+- //dlen - 4 bytes
+- //domain - dlen bytes
+- //uniqueId - 16 bytes
+- //payload length - 4 bytes
+- //payload plen bytes
+- //end package TRIBES_MBR_END.length
+- byte[] addr = host;
+- long alive=System.currentTimeMillis()-getServiceStartTime();
+- byte hl = (byte)addr.length;
+- byte[] data = new byte[getDataLength()];
+-
+- int bodylength = (getDataLength() - TRIBES_MBR_BEGIN.length -
TRIBES_MBR_END.length - 4);
+-
+- int pos = 0;
+-
+- //TRIBES_MBR_BEGIN
+- System.arraycopy(TRIBES_MBR_BEGIN,0,data,pos,TRIBES_MBR_BEGIN.length);
+- pos += TRIBES_MBR_BEGIN.length;
+-
+- //body length
+- XByteBuffer.toBytes(bodylength,data,pos);
+- pos += 4;
+-
+- //alive data
+- XByteBuffer.toBytes((long)alive,data,pos);
+- pos += 8;
+- //port
+- XByteBuffer.toBytes(port,data,pos);
+- pos += 4;
+- //secure port
+- XByteBuffer.toBytes(securePort,data,pos);
+- pos += 4;
+- //host length
+- data[pos++] = hl;
+- //host
+- System.arraycopy(addr,0,data,pos,addr.length);
+- pos+=addr.length;
+- //clen - 4 bytes
+- XByteBuffer.toBytes(command.length,data,pos);
+- pos+=4;
+- //command - clen bytes
+- System.arraycopy(command,0,data,pos,command.length);
+- pos+=command.length;
+- //dlen - 4 bytes
+- XByteBuffer.toBytes(domain.length,data,pos);
+- pos+=4;
+- //domain - dlen bytes
+- System.arraycopy(domain,0,data,pos,domain.length);
+- pos+=domain.length;
+- //unique Id
+- System.arraycopy(uniqueId,0,data,pos,uniqueId.length);
+- pos+=uniqueId.length;
+- //payload
+- XByteBuffer.toBytes(payload.length,data,pos);
+- pos+=4;
+- System.arraycopy(payload,0,data,pos,payload.length);
+- pos+=payload.length;
+-
+- //TRIBES_MBR_END
+- System.arraycopy(TRIBES_MBR_END,0,data,pos,TRIBES_MBR_END.length);
+- pos += TRIBES_MBR_END.length;
+-
+- //create local data
+- dataPkg = data;
+- return data;
+- }
+- /**
+- * Deserializes a member from data sent over the wire
+- * @param data - the bytes received
+- * @return a member object.
+- */
+- public static MemberImpl getMember(byte[] data, MemberImpl member) {
+- return getMember(data,0,data.length,member);
+- }
+-
+- public static MemberImpl getMember(byte[] data, int offset, int length, MemberImpl
member) {
+- //package looks like
+- //start package TRIBES_MBR_BEGIN.length
+- //package length - 4 bytes
+- //alive - 8 bytes
+- //port - 4 bytes
+- //secure port - 4 bytes
+- //host length - 1 byte
+- //host - hl bytes
+- //clen - 4 bytes
+- //command - clen bytes
+- //dlen - 4 bytes
+- //domain - dlen bytes
+- //uniqueId - 16 bytes
+- //payload length - 4 bytes
+- //payload plen bytes
+- //end package TRIBES_MBR_END.length
+-
+- int pos = offset;
+-
+- if (XByteBuffer.firstIndexOf(data,offset,TRIBES_MBR_BEGIN)!=pos) {
+- throw new IllegalArgumentException("Invalid package, should start
with:"+org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_BEGIN));
+- }
+-
+- if ( length < (TRIBES_MBR_BEGIN.length+4) ) {
+- throw new ArrayIndexOutOfBoundsException("Member package to small to
validate.");
+- }
+-
+- pos += TRIBES_MBR_BEGIN.length;
+-
+- int bodylength = XByteBuffer.toInt(data,pos);
+- pos += 4;
+-
+- if ( length < (bodylength+4+TRIBES_MBR_BEGIN.length+TRIBES_MBR_END.length) )
{
+- throw new ArrayIndexOutOfBoundsException("Not enough bytes in member
package.");
+- }
+-
+- int endpos = pos+bodylength;
+- if (XByteBuffer.firstIndexOf(data,endpos,TRIBES_MBR_END)!=endpos) {
+- throw new IllegalArgumentException("Invalid package, should end
with:"+org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_END));
+- }
+-
+-
+- byte[] alived = new byte[8];
+- System.arraycopy(data, pos, alived, 0, 8);
+- pos += 8;
+- byte[] portd = new byte[4];
+- System.arraycopy(data, pos, portd, 0, 4);
+- pos += 4;
+-
+- byte[] sportd = new byte[4];
+- System.arraycopy(data, pos, sportd, 0, 4);
+- pos += 4;
+-
+-
+-
+- byte hl = data[pos++];
+- byte[] addr = new byte[hl];
+- System.arraycopy(data, pos, addr, 0, hl);
+- pos += hl;
+-
+- int cl = XByteBuffer.toInt(data, pos);
+- pos += 4;
+-
+- byte[] command = new byte[cl];
+- System.arraycopy(data, pos, command, 0, command.length);
+- pos += command.length;
+-
+- int dl = XByteBuffer.toInt(data, pos);
+- pos += 4;
+-
+- byte[] domain = new byte[dl];
+- System.arraycopy(data, pos, domain, 0, domain.length);
+- pos += domain.length;
+-
+- byte[] uniqueId = new byte[16];
+- System.arraycopy(data, pos, uniqueId, 0, 16);
+- pos += 16;
+-
+- int pl = XByteBuffer.toInt(data, pos);
+- pos += 4;
+-
+- byte[] payload = new byte[pl];
+- System.arraycopy(data, pos, payload, 0, payload.length);
+- pos += payload.length;
+-
+- member.setHost(addr);
+- member.setPort(XByteBuffer.toInt(portd, 0));
+- member.setSecurePort(XByteBuffer.toInt(sportd, 0));
+- member.setMemberAliveTime(XByteBuffer.toLong(alived, 0));
+- member.setUniqueId(uniqueId);
+- member.payload = payload;
+- member.domain = domain;
+- member.command = command;
+-
+- member.dataPkg = new byte[length];
+- System.arraycopy(data, offset, member.dataPkg, 0, length);
+-
+- return member;
+- }
+-
+- public static MemberImpl getMember(byte[] data) {
+- return getMember(data,new MemberImpl());
+- }
+-
+- public static MemberImpl getMember(byte[] data, int offset, int length) {
+- return getMember(data,offset,length,new MemberImpl());
+- }
+-
+- /**
+- * Return the name of this object
+- * @return a unique name to the cluster
+- */
+- public String getName() {
+- return "tcp://"+getHostname()+":"+getPort();
+- }
+-
+- /**
+- * Return the listen port of this member
+- * @return - tcp listen port
+- */
+- public int getPort() {
+- return this.port;
+- }
+-
+- /**
+- * Return the TCP listen host for this member
+- * @return IP address or host name
+- */
+- public byte[] getHost() {
+- return host;
+- }
+-
+- public String getHostname() {
+- if ( this.hostname != null ) return hostname;
+- else {
+- try {
+- this.hostname = java.net.InetAddress.getByAddress(host).getHostName();
+- return this.hostname;
+- }catch ( IOException x ) {
+- throw new RuntimeException("Unable to parse hostname.",x);
+- }
+- }
+- }
+-
+- /**
+- * Contains information on how long this member has been online.
+- * The result is the number of milli seconds this member has been
+- * broadcasting its membership to the cluster.
+- * @return nr of milliseconds since this member started.
+- */
+- public long getMemberAliveTime() {
+- return memberAliveTime;
+- }
+-
+- public long getServiceStartTime() {
+- return serviceStartTime;
+- }
+-
+- public byte[] getUniqueId() {
+- return uniqueId;
+- }
+-
+- public byte[] getPayload() {
+- return payload;
+- }
+-
+- public byte[] getCommand() {
+- return command;
+- }
+-
+- public byte[] getDomain() {
+- return domain;
+- }
+-
+- public int getSecurePort() {
+- return securePort;
+- }
+-
+- public void setMemberAliveTime(long time) {
+- memberAliveTime=time;
+- }
+-
+-
+-
+- /**
+- * String representation of this object
+- */
+- public String toString() {
+- StringBuffer buf = new
StringBuffer("org.apache.catalina.tribes.membership.MemberImpl[");
+- buf.append(getName()).append(",");
+- buf.append(getHostname()).append(",");
+- buf.append(port).append(", alive=");
+- buf.append(memberAliveTime).append(",");
+- buf.append("id=").append(bToS(this.uniqueId)).append(", ");
+- buf.append("payload=").append(bToS(this.payload,8)).append(",
");
+- buf.append("command=").append(bToS(this.command,8)).append(",
");
+- buf.append("domain=").append(bToS(this.domain,8)).append(",
");
+- buf.append("]");
+- return buf.toString();
+- }
+- public static String bToS(byte[] data) {
+- return bToS(data,data.length);
+- }
+- public static String bToS(byte[] data, int max) {
+- StringBuffer buf = new StringBuffer(4*16);
+- buf.append("{");
+- for (int i=0; data!=null && i<data.length; i++ ) {
+- buf.append(String.valueOf(data[i])).append(" ");
+- if ( i==max ) {
+- buf.append("...("+data.length+")");
+- break;
+- }
+- }
+- buf.append("}");
+- return buf.toString();
+- }
+-
+- /**
+- * @see java.lang.Object#hashCode()
+- * @return The hash code
+- */
+- public int hashCode() {
+- return getHost()[0]+getHost()[1]+getHost()[2]+getHost()[3];
+- }
+-
+- /**
+- * Returns true if the param o is a McastMember with the same name
+- * @param o
+- */
+- public boolean equals(Object o) {
+- if ( o instanceof MemberImpl ) {
+- return Arrays.equals(this.getHost(),((MemberImpl)o).getHost()) &&
+- this.getPort() == ((MemberImpl)o).getPort() &&
+- Arrays.equals(this.getUniqueId(),((MemberImpl)o).getUniqueId());
+- }
+- else
+- return false;
+- }
+-
+- public void setHost(byte[] host) {
+- this.host = host;
+- }
+-
+- public void setHostname(String host) throws IOException {
+- hostname = host;
+- this.host = java.net.InetAddress.getByName(host).getAddress();
+- }
+-
+- public void setMsgCount(int msgCount) {
+- this.msgCount = msgCount;
+- }
+-
+- public void setPort(int port) {
+- this.port = port;
+- this.dataPkg = null;
+- }
+-
+- public void setServiceStartTime(long serviceStartTime) {
+- this.serviceStartTime = serviceStartTime;
+- }
+-
+- public void setUniqueId(byte[] uniqueId) {
+- this.uniqueId = uniqueId!=null?uniqueId:new byte[16];
+- getData(true,true);
+- }
+-
+- public void setPayload(byte[] payload) {
+- byte[] oldpayload = this.payload;
+- this.payload = payload!=null?payload:new byte[0];
+- if ( this.getData(true,true).length > McastServiceImpl.MAX_PACKET_SIZE ) {
+- this.payload = oldpayload;
+- throw new IllegalArgumentException("Payload is to large for tribes to
handle.");
+- }
+-
+- }
+-
+- public void setCommand(byte[] command) {
+- this.command = command!=null?command:new byte[0];
+- getData(true,true);
+- }
+-
+- public void setDomain(byte[] domain) {
+- this.domain = domain!=null?domain:new byte[0];
+- getData(true,true);
+- }
+-
+- public void setSecurePort(int securePort) {
+- this.securePort = securePort;
+- }
+-
+- public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
+- int length = in.readInt();
+- byte[] message = new byte[length];
+- in.read(message);
+- getMember(message,this);
+-
+- }
+-
+- public void writeExternal(ObjectOutput out) throws IOException {
+- byte[] data = this.getData();
+- out.writeInt(data.length);
+- out.write(data);
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/membership/McastServiceImpl.java
+===================================================================
+--- java/org/apache/catalina/tribes/membership/McastServiceImpl.java (revision 590752)
++++ java/org/apache/catalina/tribes/membership/McastServiceImpl.java (working copy)
+@@ -1,534 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.membership;
+-
+-
+-import java.io.IOException;
+-import java.net.DatagramPacket;
+-import java.net.InetAddress;
+-import java.net.InetSocketAddress;
+-import java.net.MulticastSocket;
+-import java.net.SocketTimeoutException;
+-import java.util.Arrays;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import java.net.BindException;
+-
+-/**
+- * A <b>membership</b> implementation using simple multicast.
+- * This is the representation of a multicast membership service.
+- * This class is responsible for maintaining a list of active cluster nodes in the
cluster.
+- * If a node fails to send out a heartbeat, the node will be dismissed.
+- * This is the low level implementation that handles the multicasting sockets.
+- * Need to fix this, could use java.nio and only need one thread to send and receive,
or
+- * just use a timeout on the receive
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public class McastServiceImpl
+-{
+- private static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog( McastService.class );
+-
+- protected static int MAX_PACKET_SIZE = 65535;
+- /**
+- * Internal flag used for the listen thread that listens to the multicasting
socket.
+- */
+- protected boolean doRunSender = false;
+- protected boolean doRunReceiver = false;
+- protected int startLevel = 0;
+- /**
+- * Socket that we intend to listen to
+- */
+- protected MulticastSocket socket;
+- /**
+- * The local member that we intend to broad cast over and over again
+- */
+- protected MemberImpl member;
+- /**
+- * The multicast address
+- */
+- protected InetAddress address;
+- /**
+- * The multicast port
+- */
+- protected int port;
+- /**
+- * The time it takes for a member to expire.
+- */
+- protected long timeToExpiration;
+- /**
+- * How often to we send out a broadcast saying we are alive, must be smaller than
timeToExpiration
+- */
+- protected long sendFrequency;
+- /**
+- * Reuse the sendPacket, no need to create a new one everytime
+- */
+- protected DatagramPacket sendPacket;
+- /**
+- * Reuse the receivePacket, no need to create a new one everytime
+- */
+- protected DatagramPacket receivePacket;
+- /**
+- * The membership, used so that we calculate memberships when they arrive or
don't arrive
+- */
+- protected Membership membership;
+- /**
+- * The actual listener, for callback when shits goes down
+- */
+- protected MembershipListener service;
+- /**
+- * Thread to listen for pings
+- */
+- protected ReceiverThread receiver;
+- /**
+- * Thread to send pings
+- */
+- protected SenderThread sender;
+-
+- /**
+- * When was the service started
+- */
+- protected long serviceStartTime = System.currentTimeMillis();
+-
+- /**
+- * Time to live for the multicast packets that are being sent out
+- */
+- protected int mcastTTL = -1;
+- /**
+- * Read timeout on the mcast socket
+- */
+- protected int mcastSoTimeout = -1;
+- /**
+- * bind address
+- */
+- protected InetAddress mcastBindAddress = null;
+-
+- /**
+- * nr of times the system has to fail before a recovery is initiated
+- */
+- protected int recoveryCounter = 10;
+-
+- /**
+- * The time the recovery thread sleeps between recovery attempts
+- */
+- protected long recoverySleepTime = 5000;
+-
+- /**
+- * Add the ability to turn on/off recovery
+- */
+- protected boolean recoveryEnabled = true;
+- /**
+- * Create a new mcast service impl
+- * @param member - the local member
+- * @param sendFrequency - the time (ms) in between pings sent out
+- * @param expireTime - the time (ms) for a member to expire
+- * @param port - the mcast port
+- * @param bind - the bind address (not sure this is used yet)
+- * @param mcastAddress - the mcast address
+- * @param service - the callback service
+- * @throws IOException
+- */
+- public McastServiceImpl(
+- MemberImpl member,
+- long sendFrequency,
+- long expireTime,
+- int port,
+- InetAddress bind,
+- InetAddress mcastAddress,
+- int ttl,
+- int soTimeout,
+- MembershipListener service)
+- throws IOException {
+- this.member = member;
+- this.address = mcastAddress;
+- this.port = port;
+- this.mcastSoTimeout = soTimeout;
+- this.mcastTTL = ttl;
+- this.mcastBindAddress = bind;
+- this.timeToExpiration = expireTime;
+- this.service = service;
+- this.sendFrequency = sendFrequency;
+- init();
+- }
+-
+- public void init() throws IOException {
+- setupSocket();
+- sendPacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
+- sendPacket.setAddress(address);
+- sendPacket.setPort(port);
+- receivePacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
+- receivePacket.setAddress(address);
+- receivePacket.setPort(port);
+- member.setCommand(new byte[0]);
+- member.getData(true, true);
+- if ( membership == null ) membership = new Membership(member);
+- }
+-
+- protected void setupSocket() throws IOException {
+- if (mcastBindAddress != null) {
+- try {
+- log.info("Attempting to bind the multicast socket to
"+address+":"+port);
+- socket = new MulticastSocket(new InetSocketAddress(address,port));
+- } catch (BindException e) {
+- /*
+- * On some plattforms (e.g. Linux) it is not possible to bind
+- * to the multicast address. In this case only bind to the
+- * port.
+- */
+- log.info("Binding to multicast address, failed. Binding to port
only.");
+- socket = new MulticastSocket(port);
+- }
+- } else {
+- socket = new MulticastSocket(port);
+- }
+- socket.setLoopbackMode(false); //hint that we don't need loop back messages
+- if (mcastBindAddress != null) {
+- if(log.isInfoEnabled())
+- log.info("Setting multihome multicast interface to:"
+mcastBindAddress);
+- socket.setInterface(mcastBindAddress);
+- } //end if
+- //force a so timeout so that we don't block forever
+- if ( mcastSoTimeout <= 0 ) mcastSoTimeout = (int)sendFrequency;
+- if(log.isInfoEnabled())
+- log.info("Setting cluster mcast soTimeout to "+mcastSoTimeout);
+- socket.setSoTimeout(mcastSoTimeout);
+-
+- if ( mcastTTL >= 0 ) {
+- if(log.isInfoEnabled())
+- log.info("Setting cluster mcast TTL to " + mcastTTL);
+- socket.setTimeToLive(mcastTTL);
+- }
+- }
+-
+-
+-
+- /**
+- * Start the service
+- * @param level 1 starts the receiver, level 2 starts the sender
+- * @throws IOException if the service fails to start
+- * @throws IllegalStateException if the service is already started
+- */
+- public synchronized void start(int level) throws IOException {
+- boolean valid = false;
+- if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {
+- if ( receiver != null ) throw new
IllegalStateException("McastService.receive already running.");
+- if ( sender == null ) socket.joinGroup(address);
+- doRunReceiver = true;
+- receiver = new ReceiverThread();
+- receiver.setDaemon(true);
+- receiver.start();
+- valid = true;
+- }
+- if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {
+- if ( sender != null ) throw new
IllegalStateException("McastService.send already running.");
+- if ( receiver == null ) socket.joinGroup(address);
+- //make sure at least one packet gets out there
+- send(false);
+- doRunSender = true;
+- serviceStartTime = System.currentTimeMillis();
+- sender = new SenderThread(sendFrequency);
+- sender.setDaemon(true);
+- sender.start();
+- //we have started the receiver, but not yet waited for membership to
establish
+- valid = true;
+- }
+- if (!valid) {
+- throw new IllegalArgumentException("Invalid start level. Only
acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ");
+- }
+- //pause, once or twice
+- waitForMembers(level);
+- startLevel = (startLevel | level);
+- }
+-
+- private void waitForMembers(int level) {
+- long memberwait = sendFrequency*2;
+- if(log.isInfoEnabled())
+- log.info("Sleeping for "+memberwait+" milliseconds to
establish cluster membership, start level:"+level);
+- try {Thread.sleep(memberwait);}catch (InterruptedException ignore){}
+- if(log.isInfoEnabled())
+- log.info("Done sleeping, membership established, start
level:"+level);
+- }
+-
+- /**
+- * Stops the service
+- * @throws IOException if the service fails to disconnect from the sockets
+- */
+- public synchronized boolean stop(int level) throws IOException {
+- boolean valid = false;
+-
+- if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {
+- valid = true;
+- doRunReceiver = false;
+- if ( receiver !=null ) receiver.interrupt();
+- receiver = null;
+- }
+- if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {
+- valid = true;
+- doRunSender = false;
+- if ( sender != null )sender.interrupt();
+- sender = null;
+- }
+-
+- if (!valid) {
+- throw new IllegalArgumentException("Invalid stop level. Only acceptable
levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ");
+- }
+- startLevel = (startLevel & (~level));
+- //we're shutting down, send a shutdown message and close the socket
+- if ( startLevel == 0 ) {
+- //send a stop message
+- member.setCommand(Member.SHUTDOWN_PAYLOAD);
+- member.getData(true, true);
+- send(false);
+- //leave mcast group
+- try {socket.leaveGroup(address);}catch ( Exception ignore){}
+- serviceStartTime = Long.MAX_VALUE;
+- }
+- return (startLevel == 0);
+- }
+-
+- /**
+- * Receive a datagram packet, locking wait
+- * @throws IOException
+- */
+- public void receive() throws IOException {
+- try {
+- socket.receive(receivePacket);
+- if(receivePacket.getLength() > MAX_PACKET_SIZE) {
+- log.error("Multicast packet received was too long, dropping
package:"+receivePacket.getLength());
+- } else {
+- byte[] data = new byte[receivePacket.getLength()];
+- System.arraycopy(receivePacket.getData(), receivePacket.getOffset(),
data, 0, data.length);
+- final MemberImpl m = MemberImpl.getMember(data);
+- if (log.isTraceEnabled()) log.trace("Mcast receive ping from member
" + m);
+- Thread t = null;
+- if (Arrays.equals(m.getCommand(), Member.SHUTDOWN_PAYLOAD)) {
+- if (log.isDebugEnabled()) log.debug("Member has shutdown:"
+ m);
+- membership.removeMember(m);
+- t = new Thread() {
+- public void run() {
+- service.memberDisappeared(m);
+- }
+- };
+- } else if (membership.memberAlive(m)) {
+- if (log.isDebugEnabled()) log.debug("Mcast add member " +
m);
+- t = new Thread() {
+- public void run() {
+- service.memberAdded(m);
+- }
+- };
+- } //end if
+- if ( t != null ) t.start();
+- }
+- } catch (SocketTimeoutException x ) {
+- //do nothing, this is normal, we don't want to block forever
+- //since the receive thread is the same thread
+- //that does membership expiration
+- }
+- checkExpired();
+- }
+-
+- protected Object expiredMutex = new Object();
+- protected void checkExpired() {
+- synchronized (expiredMutex) {
+- MemberImpl[] expired = membership.expire(timeToExpiration);
+- for (int i = 0; i < expired.length; i++) {
+- final MemberImpl member = expired[i];
+- if (log.isDebugEnabled())
+- log.debug("Mcast exipre member " + expired[i]);
+- try {
+- Thread t = new Thread() {
+- public void run() {
+- service.memberDisappeared(member);
+- }
+- };
+- t.start();
+- } catch (Exception x) {
+- log.error("Unable to process member disappeared message.",
x);
+- }
+- }
+- }
+- }
+-
+- /**
+- * Send a ping
+- * @throws Exception
+- */
+- public void send(boolean checkexpired) throws IOException{
+- //ignore if we haven't started the sender
+- //if ( (startLevel&Channel.MBR_TX_SEQ) != Channel.MBR_TX_SEQ ) return;
+- member.inc();
+- if(log.isTraceEnabled())
+- log.trace("Mcast send ping from member " + member);
+- byte[] data = member.getData();
+- DatagramPacket p = new DatagramPacket(data,data.length);
+- p.setAddress(address);
+- p.setPort(port);
+- socket.send(p);
+- if ( checkexpired ) checkExpired();
+- }
+-
+- public long getServiceStartTime() {
+- return this.serviceStartTime;
+- }
+-
+- public int getRecoveryCounter() {
+- return recoveryCounter;
+- }
+-
+- public boolean isRecoveryEnabled() {
+- return recoveryEnabled;
+- }
+-
+- public long getRecoverySleepTime() {
+- return recoverySleepTime;
+- }
+-
+- public class ReceiverThread extends Thread {
+- int errorCounter = 0;
+- public ReceiverThread() {
+- super();
+- setName("Tribes-MembershipReceiver");
+- }
+- public void run() {
+- while ( doRunReceiver ) {
+- try {
+- receive();
+- errorCounter=0;
+- } catch ( ArrayIndexOutOfBoundsException ax ) {
+- //we can ignore this, as it means we have an invalid package
+- //but we will log it to debug
+- if ( log.isDebugEnabled() )
+- log.debug("Invalid member mcast package.",ax);
+- } catch ( Exception x ) {
+- if (errorCounter==0) log.warn("Error receiving mcast package.
Sleeping 500ms",x);
+- else log.debug("Error receiving mcast package. Sleeping
500ms",x);
+- try { Thread.sleep(500); } catch ( Exception ignore ){}
+- if ( (++errorCounter)>=recoveryCounter ) {
+- errorCounter=0;
+- new RecoveryThread(McastServiceImpl.this);
+- }
+- }
+- }
+- }
+- }//class ReceiverThread
+-
+- public class SenderThread extends Thread {
+- long time;
+- int errorCounter=0;
+- public SenderThread(long time) {
+- this.time = time;
+- setName("Tribes-MembershipSender");
+-
+- }
+- public void run() {
+- while ( doRunSender ) {
+- try {
+- send(true);
+- errorCounter = 0;
+- } catch ( Exception x ) {
+- if (errorCounter==0) log.warn("Unable to send mcast
message.",x);
+- else log.debug("Unable to send mcast message.",x);
+- if ( (++errorCounter)>=recoveryCounter ) {
+- errorCounter=0;
+- new RecoveryThread(McastServiceImpl.this);
+- }
+- }
+- try { Thread.sleep(time); } catch ( Exception ignore ) {}
+- }
+- }
+- }//class SenderThread
+-
+- protected static class RecoveryThread extends Thread {
+- static boolean running = false;
+- McastServiceImpl parent = null;
+- public RecoveryThread(McastServiceImpl parent) {
+- this.parent = parent;
+- if (!init(this)) parent = null;
+- }
+-
+- public static synchronized boolean init(RecoveryThread t) {
+- if ( running ) return false;
+- if ( !t.parent.isRecoveryEnabled()) return false;
+- running = true;
+- t.setName("Tribes-MembershipRecovery");
+- t.setDaemon(true);
+- t.start();
+- return true;
+- }
+-
+- public boolean stopService() {
+- try {
+- parent.stop(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ);
+- return true;
+- } catch (Exception x) {
+- log.warn("Recovery thread failed to stop membership service.",
x);
+- return false;
+- }
+- }
+- public boolean startService() {
+- try {
+- parent.init();
+- parent.start(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ);
+- return true;
+- } catch (Exception x) {
+- log.warn("Recovery thread failed to start membership
service.", x);
+- return false;
+- }
+- }
+- public void run() {
+- boolean success = false;
+- int attempt = 0;
+- try {
+- while (!success) {
+- if(log.isInfoEnabled())
+- log.info("Tribes membership, running recovery thread,
multicasting is not functional.");
+- if (stopService() & startService()) {
+- success = true;
+- if(log.isInfoEnabled())
+- log.info("Membership recovery was successful.");
+- }
+- try {
+- if (!success) {
+- if(log.isInfoEnabled())
+- log.info("Recovery attempt
"+(++attempt)+" failed, trying again in " +parent.recoverySleepTime+ "
seconds");
+- Thread.sleep(parent.recoverySleepTime);
+- }
+- }catch (InterruptedException ignore) {
+- }
+- }
+- }finally {
+- running = false;
+- }
+- }
+- }
+-
+- public void setRecoveryCounter(int recoveryCounter) {
+- this.recoveryCounter = recoveryCounter;
+- }
+-
+- public void setRecoveryEnabled(boolean recoveryEnabled) {
+- this.recoveryEnabled = recoveryEnabled;
+- }
+-
+- public void setRecoverySleepTime(long recoverySleepTime) {
+- this.recoverySleepTime = recoverySleepTime;
+- }
+-}
+Index: java/org/apache/catalina/tribes/membership/Membership.java
+===================================================================
+--- java/org/apache/catalina/tribes/membership/Membership.java (revision 590752)
++++ java/org/apache/catalina/tribes/membership/Membership.java (working copy)
+@@ -1,324 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.membership;
+-
+-
+-import java.util.ArrayList;
+-import java.util.Arrays;
+-import java.util.HashMap;
+-import java.util.Iterator;
+-
+-import org.apache.catalina.tribes.Member;
+-import java.util.Comparator;
+-
+-/**
+- * A <b>membership</b> implementation using simple multicast.
+- * This is the representation of a multicast membership.
+- * This class is responsible for maintaining a list of active cluster nodes in the
cluster.
+- * If a node fails to send out a heartbeat, the node will be dismissed.
+- *
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$, $Date$
+- */
+-public class Membership
+-{
+- protected static final MemberImpl[] EMPTY_MEMBERS = new MemberImpl[0];
+-
+- /**
+- * The name of this membership, has to be the same as the name for the local
+- * member
+- */
+- protected MemberImpl local;
+-
+- /**
+- * A map of all the members in the cluster.
+- */
+- protected HashMap map = new HashMap();
+-
+- /**
+- * A list of all the members in the cluster.
+- */
+- protected MemberImpl[] members = EMPTY_MEMBERS;
+-
+- /**
+- * sort members by alive time
+- */
+- protected Comparator memberComparator = new MemberComparator();
+-
+- public Object clone() {
+- synchronized (members) {
+- Membership clone = new Membership(local, memberComparator);
+- clone.map = (HashMap) map.clone();
+- clone.members = new MemberImpl[members.length];
+- System.arraycopy(members,0,clone.members,0,members.length);
+- return clone;
+- }
+- }
+-
+- /**
+- * Constructs a new membership
+- * @param name - has to be the name of the local member. Used to filter the local
member from the cluster membership
+- */
+- public Membership(MemberImpl local, boolean includeLocal) {
+- this.local = local;
+- if ( includeLocal ) addMember(local);
+- }
+-
+- public Membership(MemberImpl local) {
+- this(local,false);
+- }
+-
+- public Membership(MemberImpl local, Comparator comp) {
+- this(local,comp,false);
+- }
+-
+- public Membership(MemberImpl local, Comparator comp, boolean includeLocal) {
+- this(local,includeLocal);
+- this.memberComparator = comp;
+- }
+- /**
+- * Reset the membership and start over fresh.
+- * Ie, delete all the members and wait for them to ping again and join this
membership
+- */
+- public synchronized void reset() {
+- map.clear();
+- members = EMPTY_MEMBERS ;
+- }
+-
+- /**
+- * Notify the membership that this member has announced itself.
+- *
+- * @param member - the member that just pinged us
+- * @return - true if this member is new to the cluster, false otherwise.
+- * @return - false if this member is the local member or updated.
+- */
+- public synchronized boolean memberAlive(MemberImpl member) {
+- boolean result = false;
+- //ignore ourselves
+- if ( member.equals(local) ) return result;
+-
+- //return true if the membership has changed
+- MbrEntry entry = (MbrEntry)map.get(member);
+- if ( entry == null ) {
+- entry = addMember(member);
+- result = true;
+- } else {
+- //update the member alive time
+- MemberImpl updateMember = entry.getMember() ;
+- if(updateMember.getMemberAliveTime() != member.getMemberAliveTime()) {
+- //update fields that can change
+- updateMember.setMemberAliveTime(member.getMemberAliveTime());
+- updateMember.setPayload(member.getPayload());
+- updateMember.setCommand(member.getCommand());
+- Arrays.sort(members, memberComparator);
+- }
+- }
+- entry.accessed();
+- return result;
+- }
+-
+- /**
+- * Add a member to this component and sort array with memberComparator
+- * @param member The member to add
+- */
+- public synchronized MbrEntry addMember(MemberImpl member) {
+- synchronized (members) {
+- MbrEntry entry = new MbrEntry(member);
+- if (!map.containsKey(member) ) {
+- map.put(member, entry);
+- MemberImpl results[] = new MemberImpl[members.length + 1];
+- for (int i = 0; i < members.length; i++) results[i] = members[i];
+- results[members.length] = member;
+- members = results;
+- Arrays.sort(members, memberComparator);
+- }
+- return entry;
+- }
+- }
+-
+- /**
+- * Remove a member from this component.
+- *
+- * @param member The member to remove
+- */
+- public void removeMember(MemberImpl member) {
+- map.remove(member);
+- synchronized (members) {
+- int n = -1;
+- for (int i = 0; i < members.length; i++) {
+- if (members[i] == member || members[i].equals(member)) {
+- n = i;
+- break;
+- }
+- }
+- if (n < 0) return;
+- MemberImpl results[] = new MemberImpl[members.length - 1];
+- int j = 0;
+- for (int i = 0; i < members.length; i++) {
+- if (i != n)
+- results[j++] = members[i];
+- }
+- members = results;
+- }
+- }
+-
+- /**
+- * Runs a refresh cycle and returns a list of members that has expired.
+- * This also removes the members from the membership, in such a way that
+- * getMembers() = getMembers() - expire()
+- * @param maxtime - the max time a member can remain unannounced before it is
considered dead.
+- * @return the list of expired members
+- */
+- public synchronized MemberImpl[] expire(long maxtime) {
+- if(!hasMembers() )
+- return EMPTY_MEMBERS;
+-
+- ArrayList list = null;
+- Iterator i = map.values().iterator();
+- while(i.hasNext()) {
+- MbrEntry entry = (MbrEntry)i.next();
+- if( entry.hasExpired(maxtime) ) {
+- if(list == null) // only need a list when members are expired (smaller
gc)
+- list = new java.util.ArrayList();
+- list.add(entry.getMember());
+- }
+- }
+-
+- if(list != null) {
+- MemberImpl[] result = new MemberImpl[list.size()];
+- list.toArray(result);
+- for( int j=0; j<result.length; j++) {
+- removeMember(result[j]);
+- }
+- return result;
+- } else {
+- return EMPTY_MEMBERS ;
+- }
+- }
+-
+- /**
+- * Returning that service has members or not
+- */
+- public boolean hasMembers() {
+- return members.length > 0 ;
+- }
+-
+-
+- public MemberImpl getMember(Member mbr) {
+- if(hasMembers()) {
+- MemberImpl result = null;
+- for ( int i=0; i<this.members.length && result==null; i++ ) {
+- if ( members[i].equals(mbr) ) result = members[i];
+- }//for
+- return result;
+- } else {
+- return null;
+- }
+- }
+-
+- public boolean contains(Member mbr) {
+- return getMember(mbr)!=null;
+- }
+-
+- /**
+- * Returning a list of all the members in the membership
+- * We not need a copy: add and remove generate new arrays.
+- */
+- public MemberImpl[] getMembers() {
+- if(hasMembers()) {
+- return members;
+- } else {
+- return EMPTY_MEMBERS;
+- }
+- }
+-
+- /**
+- * get a copy from all member entries
+- */
+- protected synchronized MbrEntry[] getMemberEntries()
+- {
+- MbrEntry[] result = new MbrEntry[map.size()];
+- java.util.Iterator i = map.entrySet().iterator();
+- int pos = 0;
+- while ( i.hasNext() )
+- result[pos++] = ((MbrEntry)((java.util.Map.Entry)i.next()).getValue());
+- return result;
+- }
+-
+- // --------------------------------------------- Inner Class
+-
+- private class MemberComparator implements java.util.Comparator {
+-
+- public int compare(Object o1, Object o2) {
+- try {
+- return compare((MemberImpl) o1, (MemberImpl) o2);
+- } catch (ClassCastException x) {
+- return 0;
+- }
+- }
+-
+- public int compare(MemberImpl m1, MemberImpl m2) {
+- //longer alive time, means sort first
+- long result = m2.getMemberAliveTime() - m1.getMemberAliveTime();
+- if (result < 0)
+- return -1;
+- else if (result == 0)
+- return 0;
+- else
+- return 1;
+- }
+- }
+-
+- /**
+- * Inner class that represents a member entry
+- */
+- protected static class MbrEntry
+- {
+-
+- protected MemberImpl mbr;
+- protected long lastHeardFrom;
+-
+- public MbrEntry(MemberImpl mbr) {
+- this.mbr = mbr;
+- }
+-
+- /**
+- * Indicate that this member has been accessed.
+- */
+- public void accessed(){
+- lastHeardFrom = System.currentTimeMillis();
+- }
+-
+- /**
+- * Return the actual Member object
+- */
+- public MemberImpl getMember() {
+- return mbr;
+- }
+-
+- /**
+- * Check if this dude has expired
+- * @param maxtime The time threshold
+- */
+- public boolean hasExpired(long maxtime) {
+- long delta = System.currentTimeMillis() - lastHeardFrom;
+- return delta > maxtime;
+- }
+- }
+-}
+Index: java/org/apache/catalina/tribes/membership/Constants.java
+===================================================================
+--- java/org/apache/catalina/tribes/membership/Constants.java (revision 590752)
++++ java/org/apache/catalina/tribes/membership/Constants.java (working copy)
+@@ -1,40 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.tribes.membership;
+-
+-import org.apache.catalina.tribes.util.Arrays;
+-
+-
+-/**
+- * Manifest constants for the
<code>org.apache.catalina.tribes.membership</code>
+- * package.
+- *
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- * @author Filip Hanik
+- */
+-
+-public class Constants {
+-
+- public static final String Package =
"org.apache.catalina.tribes.membership";
+- public static void main(String[] args) throws Exception {
+- System.out.println(Arrays.toString("TRIBES-B".getBytes()));
+- System.out.println(Arrays.toString("TRIBES-E".getBytes()));
+- }
+-}
+Index: java/org/apache/catalina/tribes/Constants.java
+===================================================================
+--- java/org/apache/catalina/tribes/Constants.java (revision 590752)
++++ java/org/apache/catalina/tribes/Constants.java (working copy)
+@@ -1,32 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.tribes;
+-
+-/**
+- * Manifest constants for the <code>org.apache.catalina.tribes</code>
+- * package.
+- *
+- * @author Bip Thelin
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public final class Constants {
+- public static final String Package = "org.apache.catalina.tribes";
+-}
+Index: java/org/apache/catalina/tribes/MembershipService.java
+===================================================================
+--- java/org/apache/catalina/tribes/MembershipService.java (revision 590752)
++++ java/org/apache/catalina/tribes/MembershipService.java (working copy)
+@@ -1,135 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes;
+-
+-
+-/**
+- * MembershipService Interface<br>
+- * The <code>MembershipService</code> interface is the membership component
+- * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel}
interface).<br>
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-
+-public interface MembershipService {
+-
+- public static final int MBR_RX = Channel.MBR_RX_SEQ;
+- public static final int MBR_TX = Channel.MBR_TX_SEQ;
+-
+- /**
+- * Sets the properties for the membership service. This must be called before
+- * the <code>start()</code> method is called.
+- * The properties are implementation specific.
+- * @param properties - to be used to configure the membership service.
+- */
+- public void setProperties(java.util.Properties properties);
+- /**
+- * Returns the properties for the configuration used.
+- */
+- public java.util.Properties getProperties();
+- /**
+- * Starts the membership service. If a membership listeners is added
+- * the listener will start to receive membership events.
+- * Performs a start level 1 and 2
+- * @throws java.lang.Exception if the service fails to start.
+- */
+- public void start() throws java.lang.Exception;
+-
+- /**
+- * Starts the membership service. If a membership listeners is added
+- * the listener will start to receive membership events.
+- * @param level - level MBR_RX starts listening for members, level MBR_TX
+- * starts broad casting the server
+- * @throws java.lang.Exception if the service fails to start.
+- * @throws java.lang.IllegalArgumentException if the level is incorrect.
+- */
+- public void start(int level) throws java.lang.Exception;
+-
+-
+- /**
+- * Starts the membership service. If a membership listeners is added
+- * the listener will start to receive membership events.
+- * @param level - level MBR_RX stops listening for members, level MBR_TX
+- * stops broad casting the server
+- * @throws java.lang.Exception if the service fails to stop
+- * @throws java.lang.IllegalArgumentException if the level is incorrect.
+- */
+-
+- public void stop(int level);
+-
+- /**
+- * @return true if the the group contains members
+- */
+- public boolean hasMembers();
+-
+-
+- /**
+- *
+- * @param mbr Member
+- * @return Member
+- */
+- public Member getMember(Member mbr);
+- /**
+- * Returns a list of all the members in the cluster.
+- */
+-
+- public Member[] getMembers();
+-
+- /**
+- * Returns the member object that defines this member
+- */
+- public Member getLocalMember(boolean incAliveTime);
+-
+- /**
+- * Return all members by name
+- */
+- public String[] getMembersByName() ;
+-
+- /**
+- * Return the member by name
+- */
+- public Member findMemberByName(String name) ;
+-
+- /**
+- * Sets the local member properties for broadcasting
+- */
+- public void setLocalMemberProperties(String listenHost, int listenPort);
+-
+- /**
+- * Sets the membership listener, only one listener can be added.
+- * If you call this method twice, the last listener will be used.
+- * @param listener The listener
+- */
+- public void setMembershipListener(MembershipListener listener);
+-
+- /**
+- * removes the membership listener.
+- */
+- public void removeMembershipListener();
+-
+- /**
+- * Set a payload to be broadcasted with each membership
+- * broadcast.
+- * @param payload byte[]
+- */
+- public void setPayload(byte[] payload);
+-
+- public void setDomain(byte[] domain);
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/LocalStrings.properties
+===================================================================
+--- java/org/apache/catalina/tribes/transport/LocalStrings.properties (revision 590752)
++++ java/org/apache/catalina/tribes/transport/LocalStrings.properties (working copy)
+@@ -1,84 +0,0 @@
+-# Licensed to the Apache Software Foundation (ASF) under one or more
+-# contributor license agreements. See the NOTICE file distributed with
+-# this work for additional information regarding copyright ownership.
+-# The ASF licenses this file to You under the Apache License, Version 2.0
+-# (the "License"); you may not use this file except in compliance with
+-# the License. You may obtain a copy of the License at
+-#
+-#
http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS,
+-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-# See the License for the specific language governing permissions and
+-# limitations under the License.
+-
+-AsyncSocketSender.create.thread=Create sender [{0}:{1,number,integer}] queue thread to
tcp background replication
+-AsyncSocketSender.queue.message=Queue message to [{0}:{1,number,integer}] id=[{2}]
size={3}
+-AsyncSocketSender.send.error=Unable to asynchronously send session with id=[{0}] -
message will be ignored.
+-AsyncSocketSender.queue.empty=Queue in sender [{0}:{1,number,integer}] returned null
element!
+-cluster.mbean.register.already=MBean {0} already registered!
+-IDataSender.ack.eof=EOF reached at local port [{0}:{1,number,integer}]
+-IDataSender.ack.receive=Got ACK at local port [{0}:{1,number,integer}]
+-IDataSender.ack.missing=Unable to read acknowledgement from [{0}:{1,number,integer}] in
{2,number,integer} ms. Disconnecting socket, and trying again.
+-IDataSender.ack.read=Read wait ack char '{2}' [{0}:{1,number,integer}]
+-IDataSender.ack.start=Waiting for ACK message [{0}:{1,number,integer}]
+-IDataSender.ack.wrong=Missing correct ACK after 10 bytes read at local port
[{0}:{1,number,integer}]
+-IDataSender.closeSocket=Sender close socket to [{0}:{1,number,integer}] (close count
{2,number,integer})
+-IDataSender.connect=Sender connect to [{0}:{1,number,integer}] (connect count
{2,number,integer})
+-IDataSender.create=Create sender [{0}:{1,number,integer}]
+-IDataSender.disconnect=Sender disconnect from [{0}:{1,number,integer}] (disconnect count
{2,number,integer})
+-IDataSender.message.disconnect=Message transfered: Sender can't disconnect from
[{0}:{1,number,integer}]
+-IDataSender.message.create=Message transfered: Sender can't create current socket
[{0}:{1,number,integer}]
+-IDataSender.openSocket=Sender open socket to [{0}:{1,number,integer}] (open count
{2,number,integer})
+-IDataSender.openSocket.failure=Open sender socket [{0}:{1,number,integer}] failure!
(open failure count {2,number,integer})
+-IDataSender.send.again=Send data again to [{0}:{1,number,integer}]
+-IDataSender.send.crash=Send message crashed [{0}:{1,number,integer}] type=[{2}],
id=[{3}]
+-IDataSender.send.message=Send message to [{0}:{1,number,integer}] id=[{2}]
size={3,number,integer}
+-IDataSender.send.lost=Message lost: [{0}:{1,number,integer}] type=[{2}], id=[{3}]
+-IDataSender.senderModes.Configured=Configured a data replication sender for mode {0}
+-IDataSender.senderModes.Instantiate=Can't instantiate a data replication sender of
class {0}
+-IDataSender.senderModes.Missing=Can't configure a data replication sender for mode
{0}
+-IDataSender.senderModes.Resources=Can't load data replication sender mapping list
+-IDataSender.stats=Send stats from [{0}:{1,number,integer}], Nr of bytes
sent={2,number,integer} over {3} = {4,number,integer} bytes/request, processing time
{5,number,integer} msec, avg processing time {6,number,integer} msec
+-PoolSocketSender.senderQueue.sender.failed=PoolSocketSender create new sender to
[{0}:{1,number,integer}] failed
+-PoolSocketSender.noMoreSender=No socket sender available for client
[{0}:{1,number,integer}] did it disappeared?
+-ReplicationTransmitter.getProperty=get property {0}
+-ReplicationTransmitter.setProperty=set property {0}: {1} old value {2}
+-ReplicationTransmitter.started=Start ClusterSender at cluster {0} with name {1}
+-ReplicationTransmitter.stopped=Stopped ClusterSender at cluster {0} with name {1}
+-ReplicationValve.crossContext.add=add Cross Context session replication container to
replicationValve threadlocal
+-ReplicationValve.crossContext.registerSession=register Cross context session id={0} from
context {1}
+-ReplicationValve.crossContext.remove=remove Cross Context session replication container
from replicationValve threadlocal
+-ReplicationValve.crossContext.sendDelta=send Cross Context session delta from context
{0}.
+-ReplicationValve.filter.loading=Loading request filters={0}
+-ReplicationValve.filter.token=Request filter={0}
+-ReplicationValve.filter.token.failure=Unable to compile filter={0}
+-ReplicationValve.invoke.uri=Invoking replication request on {0}
+-ReplicationValve.nocluster=No cluster configured for this request.
+-ReplicationValve.resetDeltaRequest=Cluster is standalone: reset Session Request Delta at
context {0}
+-ReplicationValve.send.failure=Unable to perform replication request.
+-ReplicationValve.send.invalid.failure=Unable to send session [id={0}] invalid message
over cluster.
+-ReplicationValve.session.found=Context {0}: Found session {1} but it isn't a
ClusterSession.
+-ReplicationValve.session.indicator=Context {0}: Primarity of session {0} in request
attribute {1} is {2}.
+-ReplicationValve.session.invalid=Context {0}: Requested session {1} is invalid, removed
or not replicated at this node.
+-ReplicationValve.stats=Average request time= {0} ms for Cluster overhead time={1} ms for
{2} requests {3} filter requests {4} send requests {5} cross context requests (Request={6}
ms Cluster={7} ms).
+-SimpleTcpCluster.event.log=Cluster receive listener event {0} with data {1}
+-SimpleTcpCluster.getProperty=get property {0}
+-SimpleTcpCluster.setProperty=set property {0}: {1} old value {2}
+-SimpleTcpCluster.default.addClusterListener=Add Default ClusterListener at cluster {0}
+-SimpleTcpCluster.default.addClusterValves=Add Default ClusterValves at cluster {0}
+-SimpleTcpCluster.default.addClusterReceiver=Add Default ClusterReceiver at cluster {0}
+-SimpleTcpCluster.default.addClusterSender=Add Default ClusterSender at cluster {0}
+-SimpleTcpCluster.default.addMembershipService=Add Default Membership Service at cluster
{0}
+-SimpleTcpCluster.log.receive=RECEIVE {0,date}:{0,time} {1,number} {2}:{3,number,integer}
{4} {5}
+-SimpleTcpCluster.log.send=SEND {0,date}:{0,time} {1,number} {2}:{3,number,integer} {4}
+-SimpleTcpCluster.log.send.all=SEND {0,date}:{0,time} {1,number} - {2}
+-SocketReplictionListener.allreadyExists=ServerSocket [{0}:{1}] allready started!
+-SocketReplictionListener.accept.failure=ServerSocket [{0}:{1}] - Exception to start
thread or accept server socket
+-SocketReplictionListener.open=Open Socket at [{0}:{1}]
+-SocketReplictionListener.openclose.failure=ServerSocket [{0}:{1}] - Exception to open or
close server socket
+-SocketReplictionListener.portbusy=Port busy at [{0}:{i}] - reason [{2}]
+-SocketReplictionListener.serverSocket.notExists=Fatal error: Receiver socket not bound
address={0} port={1} maxport={2}
+-SocketReplictionListener.timeout=Receiver ServerSocket no started [{0}:{1}] - reason:
timeout={2} or listen={3}
+-SocketReplictionListener.unlockSocket.failure=UnLocksocket failure at ServerSocket
[{0:{1}]
+Index: java/org/apache/catalina/tribes/transport/ReceiverBase.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/ReceiverBase.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/ReceiverBase.java (working copy)
+@@ -1,482 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport;
+-
+-import java.io.IOException;
+-import java.net.InetAddress;
+-import java.net.InetSocketAddress;
+-import java.net.ServerSocket;
+-import java.util.concurrent.ExecutorService;
+-import java.util.concurrent.LinkedBlockingQueue;
+-import java.util.concurrent.ThreadPoolExecutor;
+-import java.util.concurrent.TimeUnit;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.ChannelReceiver;
+-import org.apache.catalina.tribes.MessageListener;
+-import org.apache.catalina.tribes.io.ListenCallback;
+-import org.apache.juli.logging.Log;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public abstract class ReceiverBase implements ChannelReceiver, ListenCallback,
RxTaskPool.TaskCreator {
+-
+- public static final int OPTION_DIRECT_BUFFER = 0x0004;
+-
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ReceiverBase.class);
+-
+- private MessageListener listener;
+- private String host = "auto";
+- private InetAddress bind;
+- private int port = 4000;
+- private int securePort = -1;
+- private int rxBufSize = 43800;
+- private int txBufSize = 25188;
+- private boolean listen = false;
+- private RxTaskPool pool;
+- private boolean direct = true;
+- private long tcpSelectorTimeout = 5000;
+- //how many times to search for an available socket
+- private int autoBind = 100;
+- private int maxThreads = Integer.MAX_VALUE;
+- private int minThreads = 6;
+- private int maxTasks = 100;
+- private int minTasks = 10;
+- private boolean tcpNoDelay = true;
+- private boolean soKeepAlive = false;
+- private boolean ooBInline = true;
+- private boolean soReuseAddress = true;
+- private boolean soLingerOn = true;
+- private int soLingerTime = 3;
+- private int soTrafficClass = 0x04 | 0x08 | 0x010;
+- private int timeout = 3000; //3 seconds
+- private boolean useBufferPool = true;
+-
+- private ExecutorService executor;
+-
+-
+- public ReceiverBase() {
+- }
+-
+- public void start() throws IOException {
+- if ( executor == null ) {
+- executor = new
ThreadPoolExecutor(minThreads,maxThreads,60,TimeUnit.SECONDS,new
LinkedBlockingQueue<Runnable>());
+- }
+- }
+-
+- public void stop() {
+- if ( executor != null ) executor.shutdownNow();//ignore left overs
+- executor = null;
+- }
+-
+- /**
+- * getMessageListener
+- *
+- * @return MessageListener
+- * @todo Implement this org.apache.catalina.tribes.ChannelReceiver method
+- */
+- public MessageListener getMessageListener() {
+- return listener;
+- }
+-
+- /**
+- *
+- * @return The port
+- * @todo Implement this org.apache.catalina.tribes.ChannelReceiver method
+- */
+- public int getPort() {
+- return port;
+- }
+-
+- public int getRxBufSize() {
+- return rxBufSize;
+- }
+-
+- public int getTxBufSize() {
+- return txBufSize;
+- }
+-
+- /**
+- * @deprecated use getMinThreads()/getMaxThreads()
+- * @return int
+- */
+- public int getTcpThreadCount() {
+- return getMaxThreads();
+- }
+-
+- /**
+- * setMessageListener
+- *
+- * @param listener MessageListener
+- * @todo Implement this org.apache.catalina.tribes.ChannelReceiver method
+- */
+- public void setMessageListener(MessageListener listener) {
+- this.listener = listener;
+- }
+-
+- /**
+- * @deprecated use setPort
+- * @param tcpListenPort int
+- */
+- public void setTcpListenPort(int tcpListenPort) {
+- setPort(tcpListenPort);
+- }
+-
+- /**
+- * @deprecated use setAddress
+- * @param tcpListenHost String
+- */
+- public void setTcpListenAddress(String tcpListenHost) {
+- setAddress(tcpListenHost);
+- }
+-
+- public void setRxBufSize(int rxBufSize) {
+- this.rxBufSize = rxBufSize;
+- }
+-
+- public void setTxBufSize(int txBufSize) {
+- this.txBufSize = txBufSize;
+- }
+-
+- /**
+- * @deprecated use setMaxThreads/setMinThreads
+- * @param tcpThreadCount int
+- */
+- public void setTcpThreadCount(int tcpThreadCount) {
+- setMaxThreads(tcpThreadCount);
+- setMinThreads(tcpThreadCount);
+- }
+-
+- /**
+- * @return Returns the bind.
+- */
+- public InetAddress getBind() {
+- if (bind == null) {
+- try {
+- if ("auto".equals(host)) {
+- host = java.net.InetAddress.getLocalHost().getHostAddress();
+- }
+- if (log.isDebugEnabled())
+- log.debug("Starting replication listener on address:"+
host);
+- bind = java.net.InetAddress.getByName(host);
+- } catch (IOException ioe) {
+- log.error("Failed bind replication listener on address:"+
host, ioe);
+- }
+- }
+- return bind;
+- }
+-
+- /**
+- * recursive bind to find the next available port
+- * @param socket ServerSocket
+- * @param portstart int
+- * @param retries int
+- * @return int
+- * @throws IOException
+- */
+- protected int bind(ServerSocket socket, int portstart, int retries) throws
IOException {
+- InetSocketAddress addr = null;
+- while ( retries > 0 ) {
+- try {
+- addr = new InetSocketAddress(getBind(), portstart);
+- socket.bind(addr);
+- setPort(portstart);
+- log.info("Receiver Server Socket bound to:"+addr);
+- return 0;
+- }catch ( IOException x) {
+- retries--;
+- if ( retries <= 0 ) {
+- log.info("Unable to bind server socket to:"+addr+"
throwing error.");
+- throw x;
+- }
+- portstart++;
+- try {Thread.sleep(25);}catch( InterruptedException
ti){Thread.currentThread().interrupted();}
+- retries = bind(socket,portstart,retries);
+- }
+- }
+- return retries;
+- }
+-
+- public void messageDataReceived(ChannelMessage data) {
+- if ( this.listener != null ) {
+- if ( listener.accept(data) ) listener.messageReceived(data);
+- }
+- }
+-
+- public int getWorkerThreadOptions() {
+- int options = 0;
+- if ( getDirect() ) options = options | OPTION_DIRECT_BUFFER;
+- return options;
+- }
+-
+-
+- /**
+- * @param bind The bind to set.
+- */
+- public void setBind(java.net.InetAddress bind) {
+- this.bind = bind;
+- }
+-
+- /**
+- * @deprecated use getPort
+- * @return int
+- */
+- public int getTcpListenPort() {
+- return getPort();
+- }
+-
+-
+- public boolean getDirect() {
+- return direct;
+- }
+-
+-
+-
+- public void setDirect(boolean direct) {
+- this.direct = direct;
+- }
+-
+-
+- public String getAddress() {
+- getBind();
+- return this.host;
+- }
+-
+- public String getHost() {
+- return getAddress();
+- }
+-
+- public long getSelectorTimeout() {
+- return tcpSelectorTimeout;
+- }
+- /**
+- * @deprecated use getSelectorTimeout
+- * @return long
+- */
+- public long getTcpSelectorTimeout() {
+- return getSelectorTimeout();
+- }
+-
+- public boolean doListen() {
+- return listen;
+- }
+-
+- public MessageListener getListener() {
+- return listener;
+- }
+-
+- public RxTaskPool getTaskPool() {
+- return pool;
+- }
+-
+- /**
+- * @deprecated use getAddress
+- * @return String
+- */
+- public String getTcpListenAddress() {
+- return getAddress();
+- }
+-
+- public int getAutoBind() {
+- return autoBind;
+- }
+-
+- public int getMaxThreads() {
+- return maxThreads;
+- }
+-
+- public int getMinThreads() {
+- return minThreads;
+- }
+-
+- public boolean getTcpNoDelay() {
+- return tcpNoDelay;
+- }
+-
+- public boolean getSoKeepAlive() {
+- return soKeepAlive;
+- }
+-
+- public boolean getOoBInline() {
+- return ooBInline;
+- }
+-
+-
+- public boolean getSoLingerOn() {
+- return soLingerOn;
+- }
+-
+- public int getSoLingerTime() {
+- return soLingerTime;
+- }
+-
+- public boolean getSoReuseAddress() {
+- return soReuseAddress;
+- }
+-
+- public int getSoTrafficClass() {
+- return soTrafficClass;
+- }
+-
+- public int getTimeout() {
+- return timeout;
+- }
+-
+- public boolean getUseBufferPool() {
+- return useBufferPool;
+- }
+-
+- public int getSecurePort() {
+- return securePort;
+- }
+-
+- public int getMinTasks() {
+- return minTasks;
+- }
+-
+- public int getMaxTasks() {
+- return maxTasks;
+- }
+-
+- public ExecutorService getExecutor() {
+- return executor;
+- }
+-
+- public boolean isListening() {
+- return listen;
+- }
+-
+- /**
+- * @deprecated use setSelectorTimeout
+- * @param selTimeout long
+- */
+- public void setTcpSelectorTimeout(long selTimeout) {
+- setSelectorTimeout(selTimeout);
+- }
+-
+- public void setSelectorTimeout(long selTimeout) {
+- tcpSelectorTimeout = selTimeout;
+- }
+-
+- public void setListen(boolean doListen) {
+- this.listen = doListen;
+- }
+-
+-
+- public void setAddress(String host) {
+- this.host = host;
+- }
+- public void setHost(String host) {
+- setAddress(host);
+- }
+-
+- public void setListener(MessageListener listener) {
+- this.listener = listener;
+- }
+-
+- public void setLog(Log log) {
+- this.log = log;
+- }
+-
+- public void setPool(RxTaskPool pool) {
+- this.pool = pool;
+- }
+-
+- public void setPort(int port) {
+- this.port = port;
+- }
+-
+- public void setAutoBind(int autoBind) {
+- this.autoBind = autoBind;
+- if ( this.autoBind <= 0 ) this.autoBind = 1;
+- }
+-
+- public void setMaxThreads(int maxThreads) {
+- this.maxThreads = maxThreads;
+- }
+-
+- public void setMinThreads(int minThreads) {
+- this.minThreads = minThreads;
+- }
+-
+- public void setTcpNoDelay(boolean tcpNoDelay) {
+- this.tcpNoDelay = tcpNoDelay;
+- }
+-
+- public void setSoKeepAlive(boolean soKeepAlive) {
+- this.soKeepAlive = soKeepAlive;
+- }
+-
+- public void setOoBInline(boolean ooBInline) {
+- this.ooBInline = ooBInline;
+- }
+-
+-
+- public void setSoLingerOn(boolean soLingerOn) {
+- this.soLingerOn = soLingerOn;
+- }
+-
+- public void setSoLingerTime(int soLingerTime) {
+- this.soLingerTime = soLingerTime;
+- }
+-
+- public void setSoReuseAddress(boolean soReuseAddress) {
+- this.soReuseAddress = soReuseAddress;
+- }
+-
+- public void setSoTrafficClass(int soTrafficClass) {
+- this.soTrafficClass = soTrafficClass;
+- }
+-
+- public void setTimeout(int timeout) {
+- this.timeout = timeout;
+- }
+-
+- public void setUseBufferPool(boolean useBufferPool) {
+- this.useBufferPool = useBufferPool;
+- }
+-
+- public void setSecurePort(int securePort) {
+- this.securePort = securePort;
+- }
+-
+- public void setMinTasks(int minTasks) {
+- this.minTasks = minTasks;
+- }
+-
+- public void setMaxTasks(int maxTasks) {
+- this.maxTasks = maxTasks;
+- }
+-
+- public void setExecutor(ExecutorService executor) {
+- this.executor = executor;
+- }
+-
+- public void heartbeat() {
+- //empty operation
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/AbstractRxTask.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/AbstractRxTask.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/AbstractRxTask.java (working copy)
+@@ -1,89 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport;
+-
+-import org.apache.catalina.tribes.io.ListenCallback;
+-
+-
+-
+-
+-/**
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- */
+-public abstract class AbstractRxTask implements Runnable
+-{
+-
+- public static final int OPTION_DIRECT_BUFFER = ReceiverBase.OPTION_DIRECT_BUFFER;
+-
+- private ListenCallback callback;
+- private RxTaskPool pool;
+- private boolean doRun = true;
+- private int options;
+- protected boolean useBufferPool = true;
+-
+- public AbstractRxTask(ListenCallback callback) {
+- this.callback = callback;
+- }
+-
+- public void setTaskPool(RxTaskPool pool) {
+- this.pool = pool;
+- }
+-
+- public void setOptions(int options) {
+- this.options = options;
+- }
+-
+- public void setCallback(ListenCallback callback) {
+- this.callback = callback;
+- }
+-
+- public void setDoRun(boolean doRun) {
+- this.doRun = doRun;
+- }
+-
+- public RxTaskPool getTaskPool() {
+- return pool;
+- }
+-
+- public int getOptions() {
+- return options;
+- }
+-
+- public ListenCallback getCallback() {
+- return callback;
+- }
+-
+- public boolean isDoRun() {
+- return doRun;
+- }
+-
+- public void close()
+- {
+- doRun = false;
+- notify();
+- }
+-
+- public void setUseBufferPool(boolean usebufpool) {
+- useBufferPool = usebufpool;
+- }
+-
+- public boolean getUseBufferPool() {
+- return useBufferPool;
+- }
+-}
+Index: java/org/apache/catalina/tribes/transport/SenderState.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/SenderState.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/SenderState.java (working copy)
+@@ -1,115 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport;
+-
+-import org.apache.catalina.tribes.Member;
+-import java.util.HashMap;
+-
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- * @since 5.5.16
+- */
+-
+-public class SenderState {
+-
+- public static final int READY = 0;
+- public static final int SUSPECT = 1;
+- public static final int FAILING = 2;
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "SenderState/1.0";
+-
+-
+- protected static HashMap memberStates = new HashMap();
+-
+- public static SenderState getSenderState(Member member) {
+- return getSenderState(member,true);
+- }
+-
+- public static SenderState getSenderState(Member member, boolean create) {
+- SenderState state = (SenderState)memberStates.get(member);
+- if ( state == null && create) {
+- synchronized ( memberStates ) {
+- state = (SenderState)memberStates.get(member);
+- if ( state == null ) {
+- state = new SenderState();
+- memberStates.put(member,state);
+- }
+- }
+- }
+- return state;
+- }
+-
+- public static void removeSenderState(Member member) {
+- synchronized ( memberStates ) {
+- memberStates.remove(member);
+- }
+- }
+-
+-
+- // ----------------------------------------------------- Instance Variables
+-
+- private int state = READY;
+-
+- // ----------------------------------------------------- Constructor
+-
+-
+- private SenderState() {
+- this(READY);
+- }
+-
+- private SenderState(int state) {
+- this.state = state;
+- }
+-
+- /**
+- *
+- * @return boolean
+- */
+- public boolean isSuspect() {
+- return (state == SUSPECT) || (state == FAILING);
+- }
+-
+- public void setSuspect() {
+- state = SUSPECT;
+- }
+-
+- public boolean isReady() {
+- return state == READY;
+- }
+-
+- public void setReady() {
+- state = READY;
+- }
+-
+- public boolean isFailing() {
+- return state == FAILING;
+- }
+-
+- public void setFailing() {
+- state = FAILING;
+- }
+-
+-
+- // ----------------------------------------------------- Public Properties
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/PooledSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/PooledSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/PooledSender.java (working copy)
+@@ -1,215 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport;
+-
+-import java.io.IOException;
+-import java.util.List;
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public abstract class PooledSender extends AbstractSender implements MultiPointSender {
+-
+- private SenderQueue queue = null;
+- private int poolSize = 25;
+- public PooledSender() {
+- queue = new SenderQueue(this,poolSize);
+- }
+-
+- public abstract DataSender getNewDataSender();
+-
+- public DataSender getSender() {
+- return queue.getSender(getTimeout());
+- }
+-
+- public void returnSender(DataSender sender) {
+- sender.keepalive();
+- queue.returnSender(sender);
+- }
+-
+- public synchronized void connect() throws IOException {
+- //do nothing, happens in the socket sender itself
+- queue.open();
+- setConnected(true);
+- }
+-
+- public synchronized void disconnect() {
+- queue.close();
+- setConnected(false);
+- }
+-
+-
+- public int getInPoolSize() {
+- return queue.getInPoolSize();
+- }
+-
+- public int getInUsePoolSize() {
+- return queue.getInUsePoolSize();
+- }
+-
+-
+- public void setPoolSize(int poolSize) {
+- this.poolSize = poolSize;
+- queue.setLimit(poolSize);
+- }
+-
+- public int getPoolSize() {
+- return poolSize;
+- }
+-
+- public boolean keepalive() {
+- //do nothing, the pool checks on every return
+- return (queue==null)?false:queue.checkIdleKeepAlive();
+- }
+-
+- public void add(Member member) {
+- // no op, senders created upon demans
+- }
+-
+- public void remove(Member member) {
+- //no op for now, should not cancel out any keys
+- //can create serious sync issues
+- //all TCP connections are cleared out through keepalive
+- //and if remote node disappears
+- }
+- // ----------------------------------------------------- Inner Class
+-
+- private class SenderQueue {
+- private int limit = 25;
+-
+- PooledSender parent = null;
+-
+- private List notinuse = null;
+-
+- private List inuse = null;
+-
+- private boolean isOpen = true;
+-
+- public SenderQueue(PooledSender parent, int limit) {
+- this.limit = limit;
+- this.parent = parent;
+- notinuse = new java.util.LinkedList();
+- inuse = new java.util.LinkedList();
+- }
+-
+- /**
+- * @return Returns the limit.
+- */
+- public int getLimit() {
+- return limit;
+- }
+- /**
+- * @param limit The limit to set.
+- */
+- public void setLimit(int limit) {
+- this.limit = limit;
+- }
+- /**
+- * @return
+- */
+- public int getInUsePoolSize() {
+- return inuse.size();
+- }
+-
+- /**
+- * @return
+- */
+- public int getInPoolSize() {
+- return notinuse.size();
+- }
+-
+- public synchronized boolean checkIdleKeepAlive() {
+- DataSender[] list = new DataSender[notinuse.size()];
+- notinuse.toArray(list);
+- boolean result = false;
+- for (int i=0; i<list.length; i++) {
+- result = result | list[i].keepalive();
+- }
+- return result;
+- }
+-
+- public synchronized DataSender getSender(long timeout) {
+- long start = System.currentTimeMillis();
+- while ( true ) {
+- if (!isOpen)throw new IllegalStateException("Queue is
closed");
+- DataSender sender = null;
+- if (notinuse.size() == 0 && inuse.size() < limit) {
+- sender = parent.getNewDataSender();
+- } else if (notinuse.size() > 0) {
+- sender = (DataSender) notinuse.remove(0);
+- }
+- if (sender != null) {
+- inuse.add(sender);
+- return sender;
+- }//end if
+- long delta = System.currentTimeMillis() - start;
+- if ( delta > timeout && timeout>0) return null;
+- else {
+- try {
+- wait(Math.max(timeout - delta,1));
+- }catch (InterruptedException x){}
+- }//end if
+- }
+- }
+-
+- public synchronized void returnSender(DataSender sender) {
+- if ( !isOpen) {
+- sender.disconnect();
+- return;
+- }
+- //to do
+- inuse.remove(sender);
+- //just in case the limit has changed
+- if ( notinuse.size() < this.getLimit() ) notinuse.add(sender);
+- else try {sender.disconnect(); } catch ( Exception ignore){}
+- notify();
+- }
+-
+- public synchronized void close() {
+- isOpen = false;
+- Object[] unused = notinuse.toArray();
+- Object[] used = inuse.toArray();
+- for (int i = 0; i < unused.length; i++) {
+- DataSender sender = (DataSender) unused[i];
+- sender.disconnect();
+- }//for
+- for (int i = 0; i < used.length; i++) {
+- DataSender sender = (DataSender) used[i];
+- sender.disconnect();
+- }//for
+- notinuse.clear();
+- inuse.clear();
+- notify();
+-
+-
+-
+- }
+-
+- public synchronized void open() {
+- isOpen = true;
+- notify();
+- }
+- }
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/tribes/transport/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/tribes/transport/mbeans-descriptors.xml (working copy)
+@@ -1,851 +0,0 @@
+-<?xml version="1.0" encoding="UTF-8"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE mbeans-descriptors PUBLIC
+- "-//Apache Software Foundation//DTD Model MBeans Configuration File"
+- "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
+-<mbeans-descriptors>
+-
+- <mbean name="SimpleTcpCluster"
+- description="Tcp Cluster implementation"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.tcp.SimpleTcpCluster">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="notifyLifecycleListenerOnFailure"
+- description="notify lifecycleListener from message transfer
failure"
+- is="true"
+- type="boolean"/>
+- <attribute name="clusterName"
+- description="name of cluster"
+- type="java.lang.String"/>
+- <attribute name="managerClassName"
+- description="session mananager classname"
+- type="java.lang.String"/>
+- <attribute name="clusterLogName"
+- description="Name of cluster transfer log device"
+- type="java.lang.String"/>
+- <attribute name="doClusterLog"
+- is="true"
+- description="enable cluster log transfer logging"
+- type="boolean"/>
+- <operation name="setProperty"
+- description="set a property to all cluster managers (with prefix
'manager.')"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="key"
+- description="Property name"
+- type="java.lang.String"/>
+- <parameter name="value"
+- description="Property value"
+- type="java.lang.String"/>
+- </operation>
+-
+- <operation name="send"
+- description="send message to all cluster members"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="message"
+- description="replication message"
+- type="org.apache.catalina.ha.ClusterMessage"/>
+- </operation>
+-
+- <operation name="sendClusterDomain"
+- description="send message to all cluster members with same
domain"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="message"
+- description="replication message"
+- type="org.apache.catalina.ha.ClusterMessage"/>
+- </operation>
+-
+- <operation name="sendToMember"
+- description="send message to one cluster member"
+- impact="ACTION"
+- returnType="void">
+- <parameter name="message"
+- description="replication message"
+- type="org.apache.catalina..cluster.ClusterMessage"/>
+- <parameter name="member"
+- description="cluster member"
+- type="java.lang.String"/>
+- </operation>
+-
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="ClusterReceiverBase"
+- description="Tcp Cluster NioReceiver implementation"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.tcp.ClusterReceiverBase">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="tcpListenAddress"
+- description="tcp listener address"
+- type="java.lang.String"/>
+- <attribute name="tcpListenPort"
+- description="tcp listener port"
+- type="int"/>
+- <attribute name="tcpThreadCount"
+- description="number of tcp listener worker threads"
+- type="int"/>
+- <attribute name="tcpSelectorTimeout"
+- description="tcp listener Selector timeout"
+- type="long"/>
+- <attribute name="nrOfMsgsReceived"
+- description="number of messages received from other nodes"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedTime"
+- description="total time message send time"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedProcessingTime"
+- description="received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minReceivedProcessingTime"
+- description="minimal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgReceivedProcessingTime"
+- description="received processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxReceivedProcessingTime"
+- description="maximal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doReceivedProcessingStats"
+- description="create received processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="avgTotalReceivedBytes"
+- description="received totalReceivedBytes / nrOfMsgsReceived"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalReceivedBytes"
+- description="number of bytes received"
+- type="long"
+- writeable="false"/>
+- <attribute name="sendAck"
+- description="send ack after data received"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="compress"
+- description="data received compressed"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="doListen"
+- description="is port really started"
+- is="true"
+- type="boolean"
+- writeable="false" />
+-
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="SocketReplicationListener"
+- description="Tcp Cluster SocketReplicationListener implementation"
+- domain="Catalina"
+- group="Cluster"
+-
type="org.apache.catalina.ha.tcp.SocketReplicationListener">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="tcpListenAddress"
+- description="tcp listener address"
+- type="java.lang.String"/>
+- <attribute name="tcpListenPort"
+- description="tcp listener port"
+- type="int"/>
+- <attribute name="tcpListenMaxPort"
+- description="max tcp listen used port"
+- type="int"/>
+- <attribute name="tcpListenTimeout"
+- description="max tcp listen timeout (sec) wait for ServerSocket
start"
+- type="int"/>
+- <attribute name="nrOfMsgsReceived"
+- description="number of messages received from other nodes"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedTime"
+- description="total time message send time"
+- type="long"
+- writeable="false"/>
+- <attribute name="receivedProcessingTime"
+- description="received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minReceivedProcessingTime"
+- description="minimal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgReceivedProcessingTime"
+- description="received processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxReceivedProcessingTime"
+- description="maximal received processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doReceivedProcessingStats"
+- description="create received processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="avgTotalReceivedBytes"
+- description="received totalReceivedBytes / nrOfMsgsReceived"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalReceivedBytes"
+- description="number of bytes received"
+- type="long"
+- writeable="false"/>
+- <attribute name="sendAck"
+- description="send ack after data received"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="compress"
+- description="data received compressed"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="doListen"
+- description="is port really started"
+- is="true"
+- type="boolean"
+- writeable="false" />
+-
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="ReplicationTransmitter"
+- description="Tcp replication transmitter"
+- domain="Catalina"
+- group="ClusterSender"
+- type="org.apache.catalina.ha.tcp.ReplicationTransmitter">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="replicationMode"
+- description="replication mode
(synchnous,pooled.asynchnous,fastasyncqueue)"
+- type="java.lang.String"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="autoConnect"
+- description="is sender disabled, fork a new socket"
+- is="true"
+- type="boolean" />
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doTransmitterProcessingStats"
+- description="create processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="failureCounter"
+- description="number of wrong transfers"
+- type="long"
+- writeable="false"/>
+- <attribute name="senderObjectNames"
+- description="get all sender object names"
+- type="[Ljavax.management.ObjectName;"
+- writeable="false"/>
+- <operation name="start"
+- description="Start the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="stop"
+- description="Stop the cluster"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check all sender connection for close socket
(keepalive)"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- </mbean>
+-
+- <mbean name="AsyncSocketSender"
+- description="Async Cluster Sender"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.AsyncSocketSender">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long"/>
+- <attribute name="queueSize"
+- writeable="false"
+- description="queue size"
+- type="int"/>
+- <attribute name="queuedNrOfBytes"
+- writeable="false"
+- description="number of bytes over all queued messages"
+- type="long"/>
+- <attribute name="messageTransferStarted"
+- description="message is in transfer"
+- type="boolean"
+- is="true"
+- writeable="false"/>
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="keepAliveCount"
+- description="keep Alive request count"
+- type="int"
+- writeable="false"/>
+- <attribute name="keepAliveConnectTime"
+- description="Connect time for keep alive"
+- type="long"
+- writeable="false"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doProcessingStats"
+- description="create processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="waitAckTime"
+- description="sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minWaitAckTime"
+- description="minimal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgWaitAckTime"
+- description="waitAck time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxWaitAckTime"
+- description="maximal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doWaitAckStats"
+- description="create waitAck time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenCounter"
+- description="counts open socket (KeepAlive and connects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenFailureCounter"
+- description="counts open socket failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketCloseCounter"
+- description="counts closed socket (KeepAlive and disconnects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="missingAckCounter"
+- description="counts missing ack"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataResendCounter"
+- description="counts data resends"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataFailureCounter"
+- description="counts data send failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="inQueueCounter"
+- description="counts all queued messages"
+- type="long"
+- writeable="false"/>
+- <attribute name="outQueueCounter"
+- description="counts all successfully sended messages"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="connect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="disconnect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check connection for close socket"
+- impact="ACTION"
+- returnType="boolean">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="MultiSocketSender"
+- description="Multi Socket Sender, more than one socket per member"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.PooledSocketSender">
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="maxPoolSocketLimit"
+- description="Max parallel sockets"
+- type="int"/>
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="start Queue to connect to ohter replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="stop Queue to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="SocketSender"
+- description="Sync Cluster Sender"
+- domain="Catalina"
+- group="IDataSender"
+- type="org.apache.catalina.ha.tcp.SocketSender">
+- <attribute name="address"
+- description="sender ip address"
+- type="java.net.InetAddress"
+- writeable="false"/>
+- <attribute name="port"
+- description="sender port"
+- type="int"
+- writeable="false" />
+- <attribute name="suspect"
+- description="Socket is gone"
+- type="boolean"/>
+- <attribute name="ackTimeout"
+- description="acknowledge timeout"
+- type="long"/>
+- <attribute name="waitForAck"
+- description="Wait for ack after data send"
+- is="true"
+- type="boolean"
+- writeable="false" />
+- <attribute name="keepAliveTimeout"
+- description="active socket keep alive timeout"
+- type="long"/>
+- <attribute name="keepAliveMaxRequestCount"
+- description="max request over this socket"
+- type="int"/>
+- <attribute name="messageTransferStarted"
+- description="message is in transfer"
+- type="boolean"
+- is="true"
+- writeable="false"/>
+- <attribute name="keepAliveCount"
+- description="keep Alive request count"
+- type="int"
+- writeable="false"/>
+- <attribute name="keepAliveConnectTime"
+- description="Connect time for keep alive"
+- type="long"
+- writeable="false"/>
+- <attribute name="resend"
+- description="after send failure make a resend"
+- is="true"
+- type="boolean" />
+- <attribute name="connected"
+- is="true"
+- description="socket connected"
+- type="boolean"
+- writeable="false"/>
+- <attribute name="avgMessageSize"
+- writeable="false"
+- description="avg message size (totalbytes/nrOfRequests"
+- type="long"/>
+- <attribute name="nrOfRequests"
+- description="number of send messages to other members"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalBytes"
+- description="number of bytes transfered"
+- type="long"
+- writeable="false"/>
+- <attribute name="processingTime"
+- description="sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minProcessingTime"
+- description="minimal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgProcessingTime"
+- description="processing time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxProcessingTime"
+- description="maximal sending processing time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doProcessingStats"
+- description="create Processing time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="waitAckTime"
+- description="sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="minWaitAckTime"
+- description="minimal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="avgWaitAckTime"
+- description="waitAck time / nrOfRequests"
+- type="double"
+- writeable="false"/>
+- <attribute name="maxWaitAckTime"
+- description="maximal sending waitAck time"
+- type="long"
+- writeable="false"/>
+- <attribute name="doWaitAckStats"
+- description="create waitAck time stats"
+- is="true"
+- type="boolean" />
+- <attribute name="connectCounter"
+- description="counts connects"
+- type="long"
+- writeable="false"/>
+- <attribute name="disconnectCounter"
+- description="counts disconnects"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketCloseCounter"
+- description="counts closed socket (KeepAlive and disconnects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenFailureCounter"
+- description="counts open socket failures"
+- type="long"
+- writeable="false"/>
+- <attribute name="socketOpenCounter"
+- description="counts open socket (KeepAlive and connects)"
+- type="long"
+- writeable="false"/>
+- <attribute name="missingAckCounter"
+- description="counts missing ack"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataResendCounter"
+- description="counts data resends"
+- type="long"
+- writeable="false"/>
+- <attribute name="dataFailureCounter"
+- description="counts data send failures"
+- type="long"
+- writeable="false"/>
+- <operation name="connect"
+- description="connect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="disconnect"
+- description="disconnect to other replication node"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+- <operation name="checkKeepAlive"
+- description="Check connection for close socket"
+- impact="ACTION"
+- returnType="boolean">
+- </operation>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+- <mbean name="ReplicationValve"
+- description="Valve for simple tcp replication"
+- domain="Catalina"
+- group="Valve"
+- type="org.apache.catalina.ha.tcp.ReplicationValve">
+- <attribute name="info"
+- description="Class version info"
+- type="java.lang.String"
+- writeable="false"/>
+- <attribute name="filter"
+- description="resource filter to disable session replication check"
+- type="java.lang.String"/>
+- <attribute name="primaryIndicator"
+- is="true"
+- description="set indicator that request processing is at primary session
node"
+- type="boolean"/>
+- <attribute name="primaryIndicatorName"
+- description="Request attribute name to indicate that request processing
is at primary session node"
+- type="java.lang.String"/>
+- <attribute name="doProcessingStats"
+- is="true"
+- description="active statistics counting"
+- type="boolean"/>
+- <attribute name="nrOfRequests"
+- description="number of replicated requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="nrOfFilterRequests"
+- description="number of filtered requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="nrOfSendRequests"
+- description="number of send requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="nrOfCrossContextSendRequests"
+- description="number of send cross context session requests"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalRequestTime"
+- description="total replicated request time"
+- type="long"
+- writeable="false"/>
+- <attribute name="totalSendTime"
+- description="total replicated send time"
+- type="long"
+- writeable="false"/>
+- <attribute name="lastSendTime"
+- description="last replicated request time"
+- type="long"
+- writeable="false"/>
+- <operation name="resetStatistics"
+- description="Reset all statistics"
+- impact="ACTION"
+- returnType="void">
+- </operation>
+-
+- </mbean>
+-
+-
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/tribes/transport/AbstractSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/AbstractSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/AbstractSender.java (working copy)
+@@ -1,309 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport;
+-
+-import java.io.IOException;
+-import java.net.InetAddress;
+-import java.net.UnknownHostException;
+-
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public abstract class AbstractSender implements DataSender {
+-
+- private boolean connected = false;
+- private int rxBufSize = 25188;
+- private int txBufSize = 43800;
+- private boolean directBuffer = false;
+- private int keepAliveCount = -1;
+- private int requestCount = 0;
+- private long connectTime;
+- private long keepAliveTime = -1;
+- private long timeout = 3000;
+- private Member destination;
+- private InetAddress address;
+- private int port;
+- private int maxRetryAttempts = 1;//1 resends
+- private int attempt;
+- private boolean tcpNoDelay = true;
+- private boolean soKeepAlive = false;
+- private boolean ooBInline = true;
+- private boolean soReuseAddress = true;
+- private boolean soLingerOn = false;
+- private int soLingerTime = 3;
+- private int soTrafficClass = 0x04 | 0x08 | 0x010;
+- private boolean throwOnFailedAck = true;
+-
+- /**
+- * transfers sender properties from one sender to another
+- * @param from AbstractSender
+- * @param to AbstractSender
+- */
+- public static void transferProperties(AbstractSender from, AbstractSender to) {
+- to.rxBufSize = from.rxBufSize;
+- to.txBufSize = from.txBufSize;
+- to.directBuffer = from.directBuffer;
+- to.keepAliveCount = from.keepAliveCount;
+- to.keepAliveTime = from.keepAliveTime;
+- to.timeout = from.timeout;
+- to.destination = from.destination;
+- to.address = from.address;
+- to.port = from.port;
+- to.maxRetryAttempts = from.maxRetryAttempts;
+- to.tcpNoDelay = from.tcpNoDelay;
+- to.soKeepAlive = from.soKeepAlive;
+- to.ooBInline = from.ooBInline;
+- to.soReuseAddress = from.soReuseAddress;
+- to.soLingerOn = from.soLingerOn;
+- to.soLingerTime = from.soLingerTime;
+- to.soTrafficClass = from.soTrafficClass;
+- to.throwOnFailedAck = from.throwOnFailedAck;
+- }
+-
+-
+- public AbstractSender() {
+-
+- }
+-
+- /**
+- * connect
+- *
+- * @throws IOException
+- * @todo Implement this org.apache.catalina.tribes.transport.DataSender method
+- */
+- public abstract void connect() throws IOException;
+-
+- /**
+- * disconnect
+- *
+- * @todo Implement this org.apache.catalina.tribes.transport.DataSender method
+- */
+- public abstract void disconnect();
+-
+- /**
+- * keepalive
+- *
+- * @return boolean
+- * @todo Implement this org.apache.catalina.tribes.transport.DataSender method
+- */
+- public boolean keepalive() {
+- boolean disconnect = false;
+- if ( keepAliveCount >= 0 && requestCount>keepAliveCount )
disconnect = true;
+- else if ( keepAliveTime >= 0 &&
(System.currentTimeMillis()-connectTime)>keepAliveTime ) disconnect = true;
+- if ( disconnect ) disconnect();
+- return disconnect;
+- }
+-
+- protected void setConnected(boolean connected){
+- this.connected = connected;
+- }
+-
+- public boolean isConnected() {
+- return connected;
+- }
+-
+- public long getConnectTime() {
+- return connectTime;
+- }
+-
+- public Member getDestination() {
+- return destination;
+- }
+-
+-
+- public int getKeepAliveCount() {
+- return keepAliveCount;
+- }
+-
+- public long getKeepAliveTime() {
+- return keepAliveTime;
+- }
+-
+- public int getRequestCount() {
+- return requestCount;
+- }
+-
+- public int getRxBufSize() {
+- return rxBufSize;
+- }
+-
+- public long getTimeout() {
+- return timeout;
+- }
+-
+- public int getTxBufSize() {
+- return txBufSize;
+- }
+-
+- public InetAddress getAddress() {
+- return address;
+- }
+-
+- public int getPort() {
+- return port;
+- }
+-
+- public int getMaxRetryAttempts() {
+- return maxRetryAttempts;
+- }
+-
+- public void setDirect(boolean direct) {
+- setDirectBuffer(direct);
+- }
+-
+- public void setDirectBuffer(boolean directBuffer) {
+- this.directBuffer = directBuffer;
+- }
+-
+- public boolean getDirect() {
+- return getDirectBuffer();
+- }
+-
+- public boolean getDirectBuffer() {
+- return this.directBuffer;
+- }
+-
+- public int getAttempt() {
+- return attempt;
+- }
+-
+- public boolean getTcpNoDelay() {
+- return tcpNoDelay;
+- }
+-
+- public boolean getSoKeepAlive() {
+- return soKeepAlive;
+- }
+-
+- public boolean getOoBInline() {
+- return ooBInline;
+- }
+-
+- public boolean getSoReuseAddress() {
+- return soReuseAddress;
+- }
+-
+- public boolean getSoLingerOn() {
+- return soLingerOn;
+- }
+-
+- public int getSoLingerTime() {
+- return soLingerTime;
+- }
+-
+- public int getSoTrafficClass() {
+- return soTrafficClass;
+- }
+-
+- public boolean getThrowOnFailedAck() {
+- return throwOnFailedAck;
+- }
+-
+- public void setKeepAliveCount(int keepAliveCount) {
+- this.keepAliveCount = keepAliveCount;
+- }
+-
+- public void setKeepAliveTime(long keepAliveTime) {
+- this.keepAliveTime = keepAliveTime;
+- }
+-
+- public void setRequestCount(int requestCount) {
+- this.requestCount = requestCount;
+- }
+-
+- public void setRxBufSize(int rxBufSize) {
+- this.rxBufSize = rxBufSize;
+- }
+-
+- public void setTimeout(long timeout) {
+- this.timeout = timeout;
+- }
+-
+- public void setTxBufSize(int txBufSize) {
+- this.txBufSize = txBufSize;
+- }
+-
+- public void setConnectTime(long connectTime) {
+- this.connectTime = connectTime;
+- }
+-
+- public void setMaxRetryAttempts(int maxRetryAttempts) {
+- this.maxRetryAttempts = maxRetryAttempts;
+- }
+-
+- public void setAttempt(int attempt) {
+- this.attempt = attempt;
+- }
+-
+- public void setTcpNoDelay(boolean tcpNoDelay) {
+- this.tcpNoDelay = tcpNoDelay;
+- }
+-
+- public void setSoKeepAlive(boolean soKeepAlive) {
+- this.soKeepAlive = soKeepAlive;
+- }
+-
+- public void setOoBInline(boolean ooBInline) {
+- this.ooBInline = ooBInline;
+- }
+-
+- public void setSoReuseAddress(boolean soReuseAddress) {
+- this.soReuseAddress = soReuseAddress;
+- }
+-
+- public void setSoLingerOn(boolean soLingerOn) {
+- this.soLingerOn = soLingerOn;
+- }
+-
+- public void setSoLingerTime(int soLingerTime) {
+- this.soLingerTime = soLingerTime;
+- }
+-
+- public void setSoTrafficClass(int soTrafficClass) {
+- this.soTrafficClass = soTrafficClass;
+- }
+-
+- public void setThrowOnFailedAck(boolean throwOnFailedAck) {
+- this.throwOnFailedAck = throwOnFailedAck;
+- }
+-
+- public void setDestination(Member destination) throws UnknownHostException {
+- this.destination = destination;
+- this.address = InetAddress.getByAddress(destination.getHost());
+- this.port = destination.getPort();
+-
+- }
+-
+- public void setPort(int port) {
+- this.port = port;
+- }
+-
+- public void setAddress(InetAddress address) {
+- this.address = address;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java (working
copy)
+@@ -1,82 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport.nio;
+-
+-import java.io.IOException;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.transport.DataSender;
+-import org.apache.catalina.tribes.transport.MultiPointSender;
+-import org.apache.catalina.tribes.transport.PooledSender;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class PooledParallelSender extends PooledSender implements MultiPointSender {
+- protected boolean connected = true;
+- public PooledParallelSender() {
+- super();
+- }
+-
+- public void sendMessage(Member[] destination, ChannelMessage message) throws
ChannelException {
+- if ( !connected ) throw new ChannelException("Sender not
connected.");
+- ParallelNioSender sender = (ParallelNioSender)getSender();
+- if (sender == null) {
+- ChannelException cx = new ChannelException("Unable to retrieve a data
sender, time out error.");
+- for (int i = 0; i < destination.length; i++)
cx.addFaultyMember(destination[i], new NullPointerException("Unable to retrieve a
sender from the sender pool"));
+- throw cx;
+- } else {
+- try {
+- sender.sendMessage(destination, message);
+- sender.keepalive();
+- } finally {
+- if (!connected) disconnect();
+- returnSender(sender);
+- }
+- }
+- }
+-
+- public DataSender getNewDataSender() {
+- try {
+- ParallelNioSender sender = new ParallelNioSender();
+- sender.transferProperties(this,sender);
+- return sender;
+- } catch ( IOException x ) {
+- throw new RuntimeException("Unable to open NIO selector.",x);
+- }
+- }
+-
+- public synchronized void disconnect() {
+- this.connected = false;
+- super.disconnect();
+- }
+-
+- public synchronized void connect() throws IOException {
+- this.connected = true;
+- super.connect();
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java (working copy)
+@@ -1,302 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.nio;
+-import java.io.IOException;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.SocketChannel;
+-
+-import org.apache.catalina.tribes.io.ObjectReader;
+-import org.apache.catalina.tribes.transport.Constants;
+-import org.apache.catalina.tribes.transport.AbstractRxTask;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.io.ListenCallback;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.BufferPool;
+-import java.nio.channels.CancelledKeyException;
+-import org.apache.catalina.tribes.UniqueId;
+-import org.apache.catalina.tribes.RemoteProcessException;
+-import org.apache.catalina.tribes.util.Logs;
+-
+-/**
+- * A worker thread class which can drain channels and echo-back the input. Each
+- * instance is constructed with a reference to the owning thread pool object.
+- * When started, the thread loops forever waiting to be awakened to service the
+- * channel associated with a SelectionKey object. The worker is tasked by
+- * calling its serviceChannel() method with a SelectionKey object. The
+- * serviceChannel() method stores the key reference in the thread object then
+- * calls notify() to wake it up. When the channel has been drained, the worker
+- * thread returns itself to its parent pool.
+- *
+- * @author Filip Hanik
+- *
+- * @version $Revision$, $Date$
+- */
+-public class NioReplicationTask extends AbstractRxTask {
+-
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( NioReplicationTask.class );
+-
+- private ByteBuffer buffer = null;
+- private SelectionKey key;
+- private int rxBufSize;
+- private NioReceiver receiver;
+- public NioReplicationTask (ListenCallback callback, NioReceiver receiver)
+- {
+- super(callback);
+- this.receiver = receiver;
+- }
+-
+- // loop forever waiting for work to do
+- public synchronized void run() {
+- if ( buffer == null ) {
+- if ( (getOptions() & OPTION_DIRECT_BUFFER) == OPTION_DIRECT_BUFFER) {
+- buffer = ByteBuffer.allocateDirect(getRxBufSize());
+- } else {
+- buffer = ByteBuffer.allocate(getRxBufSize());
+- }
+- } else {
+- buffer.clear();
+- }
+- if (key == null) {
+- return; // just in case
+- }
+- if ( log.isTraceEnabled() )
+- log.trace("Servicing key:"+key);
+-
+- try {
+- ObjectReader reader = (ObjectReader)key.attachment();
+- if ( reader == null ) {
+- if ( log.isTraceEnabled() )
+- log.trace("No object reader, cancelling:"+key);
+- cancelKey(key);
+- } else {
+- if ( log.isTraceEnabled() )
+- log.trace("Draining channel:"+key);
+-
+- drainChannel(key, reader);
+- }
+- } catch (Exception e) {
+- //this is common, since the sockets on the other
+- //end expire after a certain time.
+- if ( e instanceof CancelledKeyException ) {
+- //do nothing
+- } else if ( e instanceof IOException ) {
+- //dont spew out stack traces for IO exceptions unless debug is enabled.
+- if (log.isDebugEnabled()) log.debug ("IOException in replication
worker, unable to drain channel. Probable cause: Keep alive socket
closed["+e.getMessage()+"].", e);
+- else log.warn ("IOException in replication worker, unable to drain
channel. Probable cause: Keep alive socket closed["+e.getMessage()+"].");
+- } else if ( log.isErrorEnabled() ) {
+- //this is a real error, log it.
+- log.error("Exception caught in
TcpReplicationThread.drainChannel.",e);
+- }
+- cancelKey(key);
+- } finally {
+-
+- }
+- key = null;
+- // done, ready for more, return to pool
+- getTaskPool().returnWorker (this);
+- }
+-
+- /**
+- * Called to initiate a unit of work by this worker thread
+- * on the provided SelectionKey object. This method is
+- * synchronized, as is the run() method, so only one key
+- * can be serviced at a given time.
+- * Before waking the worker thread, and before returning
+- * to the main selection loop, this key's interest set is
+- * updated to remove OP_READ. This will cause the selector
+- * to ignore read-readiness for this channel while the
+- * worker thread is servicing it.
+- */
+- public synchronized void serviceChannel (SelectionKey key) {
+- if ( log.isTraceEnabled() ) log.trace("About to service key:"+key);
+- ObjectReader reader = (ObjectReader)key.attachment();
+- if ( reader != null ) reader.setLastAccess(System.currentTimeMillis());
+- this.key = key;
+- key.interestOps (key.interestOps() & (~SelectionKey.OP_READ));
+- key.interestOps (key.interestOps() & (~SelectionKey.OP_WRITE));
+- }
+-
+- /**
+- * The actual code which drains the channel associated with
+- * the given key. This method assumes the key has been
+- * modified prior to invocation to turn off selection
+- * interest in OP_READ. When this method completes it
+- * re-enables OP_READ and calls wakeup() on the selector
+- * so the selector will resume watching this channel.
+- */
+- protected void drainChannel (final SelectionKey key, ObjectReader reader) throws
Exception {
+- reader.setLastAccess(System.currentTimeMillis());
+- reader.access();
+- SocketChannel channel = (SocketChannel) key.channel();
+- int count;
+- buffer.clear(); // make buffer empty
+-
+- // loop while data available, channel is non-blocking
+- while ((count = channel.read (buffer)) > 0) {
+- buffer.flip(); // make buffer readable
+- if ( buffer.hasArray() )
+- reader.append(buffer.array(),0,count,false);
+- else
+- reader.append(buffer,count,false);
+- buffer.clear(); // make buffer empty
+- //do we have at least one package?
+- if ( reader.hasPackage() ) break;
+- }
+-
+- int pkgcnt = reader.count();
+-
+- if (count < 0 && pkgcnt == 0 ) {
+- //end of stream, and no more packages to process
+- remoteEof(key);
+- return;
+- }
+-
+- ChannelMessage[] msgs = pkgcnt == 0? ChannelData.EMPTY_DATA_ARRAY :
reader.execute();
+-
+- registerForRead(key,reader);//register to read new data, before we send it off
to avoid dead locks
+-
+- for ( int i=0; i<msgs.length; i++ ) {
+- /**
+- * Use send ack here if you want to ack the request to the remote
+- * server before completing the request
+- * This is considered an asynchronized request
+- */
+- if (ChannelData.sendAckAsync(msgs[i].getOptions()))
sendAck(key,channel,Constants.ACK_COMMAND);
+- try {
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- try {
+- Logs.MESSAGES.trace("NioReplicationThread - Received
msg:" + new UniqueId(msgs[i].getUniqueId()) + " at " + new
java.sql.Timestamp(System.currentTimeMillis()));
+- }catch ( Throwable t ) {}
+- }
+- //process the message
+- getCallback().messageDataReceived(msgs[i]);
+- /**
+- * Use send ack here if you want the request to complete on this
+- * server before sending the ack to the remote server
+- * This is considered a synchronized request
+- */
+- if (ChannelData.sendAckSync(msgs[i].getOptions()))
sendAck(key,channel,Constants.ACK_COMMAND);
+- }catch ( RemoteProcessException e ) {
+- if ( log.isDebugEnabled() ) log.error("Processing of cluster
message failed.",e);
+- if (ChannelData.sendAckSync(msgs[i].getOptions()))
sendAck(key,channel,Constants.FAIL_ACK_COMMAND);
+- }catch ( Exception e ) {
+- log.error("Processing of cluster message failed.",e);
+- if (ChannelData.sendAckSync(msgs[i].getOptions()))
sendAck(key,channel,Constants.FAIL_ACK_COMMAND);
+- }
+- if ( getUseBufferPool() ) {
+- BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());
+- msgs[i].setMessage(null);
+- }
+- }
+-
+- if (count < 0) {
+- remoteEof(key);
+- return;
+- }
+- }
+-
+- private void remoteEof(SelectionKey key) {
+- // close channel on EOF, invalidates the key
+- if ( log.isDebugEnabled() ) log.debug("Channel closed on the remote end,
disconnecting");
+- cancelKey(key);
+- }
+-
+- protected void registerForRead(final SelectionKey key, ObjectReader reader) {
+- if ( log.isTraceEnabled() )
+- log.trace("Adding key for read event:"+key);
+- reader.finish();
+- //register our OP_READ interest
+- Runnable r = new Runnable() {
+- public void run() {
+- try {
+- if (key.isValid()) {
+- // cycle the selector so this key is active again
+- key.selector().wakeup();
+- // resume interest in OP_READ, OP_WRITE
+- int resumeOps = key.interestOps() | SelectionKey.OP_READ;
+- key.interestOps(resumeOps);
+- if ( log.isTraceEnabled() )
+- log.trace("Registering key for read:"+key);
+- }
+- } catch (CancelledKeyException ckx ) {
+- NioReceiver.cancelledKey(key);
+- if ( log.isTraceEnabled() )
+- log.trace("CKX Cancelling key:"+key);
+-
+- } catch (Exception x) {
+- log.error("Error registering key for read:"+key,x);
+- }
+- }
+- };
+- receiver.addEvent(r);
+- }
+-
+- private void cancelKey(final SelectionKey key) {
+- if ( log.isTraceEnabled() )
+- log.trace("Adding key for cancel event:"+key);
+-
+- ObjectReader reader = (ObjectReader)key.attachment();
+- if ( reader != null ) {
+- reader.setCancelled(true);
+- reader.finish();
+- }
+- Runnable cx = new Runnable() {
+- public void run() {
+- if ( log.isTraceEnabled() )
+- log.trace("Cancelling key:"+key);
+-
+- NioReceiver.cancelledKey(key);
+- }
+- };
+- receiver.addEvent(cx);
+- }
+-
+-
+-
+-
+-
+- /**
+- * send a reply-acknowledgement (6,2,3)
+- * @param key
+- * @param channel
+- */
+- protected void sendAck(SelectionKey key, SocketChannel channel, byte[] command) {
+-
+- try {
+- ByteBuffer buf = ByteBuffer.wrap(command);
+- int total = 0;
+- while ( total < command.length ) {
+- total += channel.write(buf);
+- }
+- if (log.isTraceEnabled()) {
+- log.trace("ACK sent to " + channel.socket().getPort());
+- }
+- } catch ( java.io.IOException x ) {
+- log.warn("Unable to send ACK back through channel, channel
disconnected?: "+x.getMessage());
+- }
+- }
+-
+- public void setRxBufSize(int rxBufSize) {
+- this.rxBufSize = rxBufSize;
+- }
+-
+- public int getRxBufSize() {
+- return rxBufSize;
+- }
+-}
+Index: java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java (working copy)
+@@ -1,308 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport.nio;
+-
+-
+-import java.io.IOException;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.Selector;
+-import java.util.HashMap;
+-import java.util.Iterator;
+-import java.util.Map;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.transport.MultiPointSender;
+-import org.apache.catalina.tribes.transport.SenderState;
+-import org.apache.catalina.tribes.transport.AbstractSender;
+-import java.net.UnknownHostException;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.util.Logs;
+-import org.apache.catalina.tribes.UniqueId;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class ParallelNioSender extends AbstractSender implements MultiPointSender {
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ParallelNioSender.class);
+- protected long selectTimeout = 5000; //default 5 seconds, same as send timeout
+- protected Selector selector;
+- protected HashMap nioSenders = new HashMap();
+-
+- public ParallelNioSender() throws IOException {
+- selector = Selector.open();
+- setConnected(true);
+- }
+-
+-
+- public synchronized void sendMessage(Member[] destination, ChannelMessage msg)
throws ChannelException {
+- long start = System.currentTimeMillis();
+- byte[] data = XByteBuffer.createDataPackage((ChannelData)msg);
+- NioSender[] senders = setupForSend(destination);
+- connect(senders);
+- setData(senders,data);
+-
+- int remaining = senders.length;
+- ChannelException cx = null;
+- try {
+- //loop until complete, an error happens, or we timeout
+- long delta = System.currentTimeMillis() - start;
+- boolean waitForAck = (Channel.SEND_OPTIONS_USE_ACK & msg.getOptions())
== Channel.SEND_OPTIONS_USE_ACK;
+- while ( (remaining>0) && (delta<getTimeout()) ) {
+- try {
+- remaining -= doLoop(selectTimeout,
getMaxRetryAttempts(),waitForAck,msg);
+- } catch (Exception x ) {
+- int faulty = (cx == null)?0:cx.getFaultyMembers().length;
+- if ( cx == null ) {
+- if ( x instanceof ChannelException ) cx = (ChannelException)x;
+- else cx = new ChannelException("Parallel NIO send
failed.", x);
+- } else {
+- if (x instanceof ChannelException) cx.addFaultyMember( (
(ChannelException) x).getFaultyMembers());
+- }
+- //count down the remaining on an error
+- if (faulty<cx.getFaultyMembers().length) remaining -=
(cx.getFaultyMembers().length-faulty);
+- }
+- //bail out if all remaining senders are failing
+- if ( cx != null && cx.getFaultyMembers().length == remaining )
throw cx;
+- delta = System.currentTimeMillis() - start;
+- }
+- if ( remaining > 0 ) {
+- //timeout has occured
+- ChannelException cxtimeout = new ChannelException("Operation has
timed out("+getTimeout()+" ms.).");
+- if ( cx==null ) cx = new ChannelException("Operation has timed
out("+getTimeout()+" ms.).");
+- for (int i=0; i<senders.length; i++ ) {
+- if (!senders[i].isComplete() )
cx.addFaultyMember(senders[i].getDestination(),cxtimeout);
+- }
+- throw cx;
+- } else if ( cx != null ) {
+- //there was an error
+- throw cx;
+- }
+- } catch (Exception x ) {
+- try { this.disconnect(); } catch (Exception ignore) {}
+- if ( x instanceof ChannelException ) throw (ChannelException)x;
+- else throw new ChannelException(x);
+- }
+-
+- }
+-
+- private int doLoop(long selectTimeOut, int maxAttempts, boolean waitForAck,
ChannelMessage msg) throws IOException, ChannelException {
+- int completed = 0;
+- int selectedKeys = selector.select(selectTimeOut);
+-
+- if (selectedKeys == 0) {
+- return 0;
+- }
+-
+- Iterator it = selector.selectedKeys().iterator();
+- while (it.hasNext()) {
+- SelectionKey sk = (SelectionKey) it.next();
+- it.remove();
+- int readyOps = sk.readyOps();
+- sk.interestOps(sk.interestOps() & ~readyOps);
+- NioSender sender = (NioSender) sk.attachment();
+- try {
+- if (sender.process(sk,waitForAck)) {
+- completed++;
+- sender.setComplete(true);
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("ParallelNioSender - Sent msg:" +
new UniqueId(msg.getUniqueId()) + " at " +new
java.sql.Timestamp(System.currentTimeMillis())+ " to
"+sender.getDestination().getName());
+- }
+- SenderState.getSenderState(sender.getDestination()).setReady();
+- }//end if
+- } catch (Exception x) {
+- SenderState state =
SenderState.getSenderState(sender.getDestination());
+- int attempt = sender.getAttempt()+1;
+- boolean retry = (sender.getAttempt() <= maxAttempts &&
maxAttempts>0);
+- synchronized (state) {
+-
+- //sk.cancel();
+- if (state.isSuspect()) state.setFailing();
+- if (state.isReady()) {
+- state.setSuspect();
+- if ( retry )
+- log.warn("Member send is failing for:" +
sender.getDestination().getName() +" ; Setting to suspect and retrying.");
+- else
+- log.warn("Member send is failing for:" +
sender.getDestination().getName() +" ; Setting to suspect.", x);
+- }
+- }
+- if ( !isConnected() ) {
+- log.warn("Not retrying send for:" +
sender.getDestination().getName() + "; Sender is disconnected.");
+- ChannelException cx = new ChannelException("Send failed, and
sender is disconnected. Not retrying.",x);
+- cx.addFaultyMember(sender.getDestination(),x);
+- throw cx;
+- }
+-
+- byte[] data = sender.getMessage();
+- if ( retry ) {
+- try {
+- sender.disconnect();
+- sender.connect();
+- sender.setAttempt(attempt);
+- sender.setMessage(data);
+- }catch ( Exception ignore){
+- state.setFailing();
+- }
+- } else {
+- ChannelException cx = new ChannelException("Send failed,
attempt:"+sender.getAttempt()+" max:"+maxAttempts,x);
+- cx.addFaultyMember(sender.getDestination(),x);
+- throw cx;
+- }//end if
+- }
+- }
+- return completed;
+-
+- }
+-
+- private void connect(NioSender[] senders) throws ChannelException {
+- ChannelException x = null;
+- for (int i=0; i<senders.length; i++ ) {
+- try {
+- if (!senders[i].isConnected()) senders[i].connect();
+- }catch ( IOException io ) {
+- if ( x==null ) x = new ChannelException(io);
+- x.addFaultyMember(senders[i].getDestination(),io);
+- }
+- }
+- if ( x != null ) throw x;
+- }
+-
+- private void setData(NioSender[] senders, byte[] data) throws ChannelException {
+- ChannelException x = null;
+- for (int i=0; i<senders.length; i++ ) {
+- try {
+- senders[i].setMessage(data);
+- }catch ( IOException io ) {
+- if ( x==null ) x = new ChannelException(io);
+- x.addFaultyMember(senders[i].getDestination(),io);
+- }
+- }
+- if ( x != null ) throw x;
+- }
+-
+-
+- private NioSender[] setupForSend(Member[] destination) throws ChannelException {
+- ChannelException cx = null;
+- NioSender[] result = new NioSender[destination.length];
+- for ( int i=0; i<destination.length; i++ ) {
+- NioSender sender = (NioSender)nioSenders.get(destination[i]);
+- try {
+-
+- if (sender == null) {
+- sender = new NioSender();
+- sender.transferProperties(this, sender);
+- nioSenders.put(destination[i], sender);
+- }
+- if (sender != null) {
+- sender.reset();
+- sender.setDestination(destination[i]);
+- sender.setSelector(selector);
+- result[i] = sender;
+- }
+- }catch ( UnknownHostException x ) {
+- if (cx == null) cx = new ChannelException("Unable to setup
NioSender.", x);
+- cx.addFaultyMember(destination[i], x);
+- }
+- }
+- if ( cx != null ) throw cx;
+- else return result;
+- }
+-
+- public void connect() {
+- //do nothing, we connect on demand
+- setConnected(true);
+- }
+-
+-
+- private synchronized void close() throws ChannelException {
+- ChannelException x = null;
+- Object[] members = nioSenders.keySet().toArray();
+- for (int i=0; i<members.length; i++ ) {
+- Member mbr = (Member)members[i];
+- try {
+- NioSender sender = (NioSender)nioSenders.get(mbr);
+- sender.disconnect();
+- }catch ( Exception e ) {
+- if ( x == null ) x = new ChannelException(e);
+- x.addFaultyMember(mbr,e);
+- }
+- nioSenders.remove(mbr);
+- }
+- if ( x != null ) throw x;
+- }
+-
+- public void add(Member member) {
+-
+- }
+-
+- public void remove(Member member) {
+- //disconnect senders
+- NioSender sender = (NioSender)nioSenders.remove(member);
+- if ( sender != null ) sender.disconnect();
+- }
+-
+-
+- public synchronized void disconnect() {
+- setConnected(false);
+- try {close(); }catch (Exception x){}
+-
+- }
+-
+- public void finalize() {
+- try {disconnect(); }catch ( Exception ignore){}
+- }
+-
+- public boolean keepalive() {
+- boolean result = false;
+- for ( Iterator i = nioSenders.entrySet().iterator(); i.hasNext(); ) {
+- Map.Entry entry = (Map.Entry)i.next();
+- NioSender sender = (NioSender)entry.getValue();
+- if ( sender.keepalive() ) {
+- //nioSenders.remove(entry.getKey());
+- i.remove();
+- result = true;
+- } else {
+- try {
+- sender.read(null);
+- }catch ( IOException x ) {
+- sender.disconnect();
+- sender.reset();
+- //nioSenders.remove(entry.getKey());
+- i.remove();
+- result = true;
+- }catch ( Exception x ) {
+- log.warn("Error during keepalive test for
sender:"+sender,x);
+- }
+- }
+- }
+- //clean up any cancelled keys
+- if ( result ) try { selector.selectNow(); }catch (Exception ignore){}
+- return result;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/nio/NioSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/nio/NioSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/nio/NioSender.java (working copy)
+@@ -1,340 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.nio;
+-
+-import java.io.IOException;
+-import java.net.InetSocketAddress;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.Selector;
+-import java.nio.channels.SocketChannel;
+-import java.util.Arrays;
+-
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.transport.AbstractSender;
+-import org.apache.catalina.tribes.transport.DataSender;
+-import org.apache.catalina.tribes.RemoteProcessException;
+-import java.io.EOFException;
+-import java.net.*;
+-
+-/**
+- * This class is NOT thread safe and should never be used with more than one thread at a
time
+- *
+- * This is a state machine, handled by the process method
+- * States are:
+- * - NOT_CONNECTED -> connect() -> CONNECTED
+- * - CONNECTED -> setMessage() -> READY TO WRITE
+- * - READY_TO_WRITE -> write() -> READY TO WRITE | READY TO READ
+- * - READY_TO_READ -> read() -> READY_TO_READ | TRANSFER_COMPLETE
+- * - TRANSFER_COMPLETE -> CONNECTED
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class NioSender extends AbstractSender implements DataSender{
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(NioSender.class);
+-
+-
+-
+- protected Selector selector;
+- protected SocketChannel socketChannel;
+-
+- /*
+- * STATE VARIABLES *
+- */
+- protected ByteBuffer readbuf = null;
+- protected ByteBuffer writebuf = null;
+- protected byte[] current = null;
+- protected XByteBuffer ackbuf = new XByteBuffer(128,true);
+- protected int remaining = 0;
+- protected boolean complete;
+-
+- protected boolean connecting = false;
+-
+- public NioSender() {
+- super();
+-
+- }
+-
+- /**
+- * State machine to send data
+- * @param key SelectionKey
+- * @return boolean
+- * @throws IOException
+- */
+- public boolean process(SelectionKey key, boolean waitForAck) throws IOException {
+- int ops = key.readyOps();
+- key.interestOps(key.interestOps() & ~ops);
+- //in case disconnect has been called
+- if ((!isConnected()) && (!connecting)) throw new
IOException("Sender has been disconnected, can't selection key.");
+- if ( !key.isValid() ) throw new IOException("Key is not valid, it must have
been cancelled.");
+- if ( key.isConnectable() ) {
+- if ( socketChannel.finishConnect() ) {
+- completeConnect();
+- if ( current != null ) key.interestOps(key.interestOps() |
SelectionKey.OP_WRITE);
+- return false;
+- } else {
+- //wait for the connection to finish
+- key.interestOps(key.interestOps() | SelectionKey.OP_CONNECT);
+- return false;
+- }//end if
+- } else if ( key.isWritable() ) {
+- boolean writecomplete = write(key);
+- if ( writecomplete ) {
+- //we are completed, should we read an ack?
+- if ( waitForAck ) {
+- //register to read the ack
+- key.interestOps(key.interestOps() | SelectionKey.OP_READ);
+- } else {
+- //if not, we are ready, setMessage will reregister us for another
write interest
+- //do a health check, we have no way of verify a disconnected
+- //socket since we don't register for OP_READ on
waitForAck=false
+- read(key);//this causes overhead
+- setRequestCount(getRequestCount()+1);
+- return true;
+- }
+- } else {
+- //we are not complete, lets write some more
+- key.interestOps(key.interestOps()|SelectionKey.OP_WRITE);
+- }//end if
+- } else if ( key.isReadable() ) {
+- boolean readcomplete = read(key);
+- if ( readcomplete ) {
+- setRequestCount(getRequestCount()+1);
+- return true;
+- } else {
+- key.interestOps(key.interestOps() | SelectionKey.OP_READ);
+- }//end if
+- } else {
+- //unknown state, should never happen
+- log.warn("Data is in unknown state. readyOps="+ops);
+- throw new IOException("Data is in unknown state. readyOps="+ops);
+- }//end if
+- return false;
+- }
+-
+- private void completeConnect() throws SocketException {
+- //we connected, register ourselves for writing
+- setConnected(true);
+- connecting = false;
+- setRequestCount(0);
+- setConnectTime(System.currentTimeMillis());
+- socketChannel.socket().setSendBufferSize(getTxBufSize());
+- socketChannel.socket().setReceiveBufferSize(getRxBufSize());
+- socketChannel.socket().setSoTimeout((int)getTimeout());
+-
socketChannel.socket().setSoLinger(getSoLingerOn(),getSoLingerOn()?getSoLingerTime():0);
+- socketChannel.socket().setTcpNoDelay(getTcpNoDelay());
+- socketChannel.socket().setKeepAlive(getSoKeepAlive());
+- socketChannel.socket().setReuseAddress(getSoReuseAddress());
+- socketChannel.socket().setOOBInline(getOoBInline());
+- socketChannel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());
+- socketChannel.socket().setTrafficClass(getSoTrafficClass());
+- }
+-
+-
+-
+- protected boolean read(SelectionKey key) throws IOException {
+- //if there is no message here, we are done
+- if ( current == null ) return true;
+- int read = socketChannel.read(readbuf);
+- //end of stream
+- if ( read == -1 ) throw new IOException("Unable to receive an ack message.
EOF on socket channel has been reached.");
+- //no data read
+- else if ( read == 0 ) return false;
+- readbuf.flip();
+- ackbuf.append(readbuf,read);
+- readbuf.clear();
+- if (ackbuf.doesPackageExist() ) {
+- byte[] ackcmd = ackbuf.extractDataPackage(true).getBytes();
+- boolean ack =
Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.ACK_DATA);
+- boolean fack =
Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA);
+- if ( fack && getThrowOnFailedAck() ) throw new
RemoteProcessException("Received a failed
ack:org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA");
+- return ack || fack;
+- } else {
+- return false;
+- }
+- }
+-
+-
+- protected boolean write(SelectionKey key) throws IOException {
+- if ( (!isConnected()) || (this.socketChannel==null)) {
+- throw new IOException("NioSender is not connected, this should not
occur.");
+- }
+- if ( current != null ) {
+- if ( remaining > 0 ) {
+- //weve written everything, or we are starting a new package
+- //protect against buffer overwrite
+- int byteswritten = socketChannel.write(writebuf);
+- if (byteswritten == -1 ) throw new EOFException();
+- remaining -= byteswritten;
+- //if the entire message was written from the buffer
+- //reset the position counter
+- if ( remaining < 0 ) {
+- remaining = 0;
+- }
+- }
+- return (remaining==0);
+- }
+- //no message to send, we can consider that complete
+- return true;
+- }
+-
+- /**
+- * connect - blocking in this operation
+- *
+- * @throws IOException
+- * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method
+- */
+- public synchronized void connect() throws IOException {
+- if ( connecting ) return;
+- connecting = true;
+- if ( isConnected() ) throw new IOException("NioSender is already in
connected state.");
+- if ( readbuf == null ) {
+- readbuf = getReadBuffer();
+- } else {
+- readbuf.clear();
+- }
+- if ( writebuf == null ) {
+- writebuf = getWriteBuffer();
+- } else {
+- writebuf.clear();
+- }
+-
+- InetSocketAddress addr = new InetSocketAddress(getAddress(),getPort());
+- if ( socketChannel != null ) throw new IOException("Socket channel has
already been established. Connection might be in progress.");
+- socketChannel = SocketChannel.open();
+- socketChannel.configureBlocking(false);
+- if ( socketChannel.connect(addr) ) {
+- completeConnect();
+- socketChannel.register(getSelector(), SelectionKey.OP_WRITE, this);
+- } else {
+- socketChannel.register(getSelector(), SelectionKey.OP_CONNECT, this);
+- }
+- }
+-
+-
+- /**
+- * disconnect
+- *
+- * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method
+- */
+- public void disconnect() {
+- try {
+- connecting = false;
+- setConnected(false);
+- if ( socketChannel != null ) {
+- try {
+- try {socketChannel.socket().close();}catch ( Exception x){}
+- //error free close, all the way
+- //try {socket.shutdownOutput();}catch ( Exception x){}
+- //try {socket.shutdownInput();}catch ( Exception x){}
+- //try {socket.close();}catch ( Exception x){}
+- try {socketChannel.close();}catch ( Exception x){}
+- }finally {
+- socketChannel = null;
+- }
+- }
+- } catch ( Exception x ) {
+- log.error("Unable to disconnect NioSender. msg="+x.getMessage());
+- if ( log.isDebugEnabled() ) log.debug("Unable to disconnect NioSender.
msg="+x.getMessage(),x);
+- } finally {
+- }
+-
+- }
+-
+- public void reset() {
+- if ( isConnected() && readbuf == null) {
+- readbuf = getReadBuffer();
+- }
+- if ( readbuf != null ) readbuf.clear();
+- if ( writebuf != null ) writebuf.clear();
+- current = null;
+- ackbuf.clear();
+- remaining = 0;
+- complete = false;
+- setAttempt(0);
+- setRequestCount(0);
+- setConnectTime(-1);
+- }
+-
+- private ByteBuffer getReadBuffer() {
+- return getBuffer(getRxBufSize());
+- }
+-
+- private ByteBuffer getWriteBuffer() {
+- return getBuffer(getTxBufSize());
+- }
+-
+- private ByteBuffer getBuffer(int size) {
+- return
(getDirectBuffer()?ByteBuffer.allocateDirect(size):ByteBuffer.allocate(size));
+- }
+-
+- /**
+- * sendMessage
+- *
+- * @param data ChannelMessage
+- * @throws IOException
+- * @todo Implement this org.apache.catalina.tribes.transport.IDataSender method
+- */
+- public synchronized void setMessage(byte[] data) throws IOException {
+- setMessage(data,0,data.length);
+- }
+-
+- public synchronized void setMessage(byte[] data,int offset, int length) throws
IOException {
+- if ( data != null ) {
+- current = data;
+- remaining = length;
+- ackbuf.clear();
+- if ( writebuf != null ) writebuf.clear();
+- else writebuf = getBuffer(length);
+- if ( writebuf.capacity() < length ) writebuf = getBuffer(length);
+- writebuf.put(data,offset,length);
+- //writebuf.rewind();
+- //set the limit so that we don't write non wanted data
+- //writebuf.limit(length);
+- writebuf.flip();
+- if (isConnected()) {
+- socketChannel.register(getSelector(), SelectionKey.OP_WRITE, this);
+- }
+- }
+- }
+-
+- public byte[] getMessage() {
+- return current;
+- }
+-
+-
+-
+- public boolean isComplete() {
+- return complete;
+- }
+-
+- public Selector getSelector() {
+- return selector;
+- }
+-
+- public void setSelector(Selector selector) {
+- this.selector = selector;
+- }
+-
+-
+- public void setComplete(boolean complete) {
+- this.complete = complete;
+- }
+-}
+Index: java/org/apache/catalina/tribes/transport/nio/NioReceiver.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/nio/NioReceiver.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/nio/NioReceiver.java (working copy)
+@@ -1,386 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.nio;
+-
+-import java.io.IOException;
+-import java.net.ServerSocket;
+-import java.nio.channels.SelectableChannel;
+-import java.nio.channels.SelectionKey;
+-import java.nio.channels.Selector;
+-import java.nio.channels.ServerSocketChannel;
+-import java.nio.channels.SocketChannel;
+-import java.util.Iterator;
+-
+-import org.apache.catalina.tribes.ChannelReceiver;
+-import org.apache.catalina.tribes.io.ListenCallback;
+-import org.apache.catalina.tribes.io.ObjectReader;
+-import org.apache.catalina.tribes.transport.Constants;
+-import org.apache.catalina.tribes.transport.ReceiverBase;
+-import org.apache.catalina.tribes.transport.RxTaskPool;
+-import org.apache.catalina.tribes.transport.AbstractRxTask;
+-import org.apache.catalina.tribes.util.StringManager;
+-import java.util.LinkedList;
+-import java.util.Set;
+-import java.nio.channels.CancelledKeyException;
+-
+-/**
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- */
+-public class NioReceiver extends ReceiverBase implements Runnable, ChannelReceiver,
ListenCallback {
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(NioReceiver.class);
+-
+- /**
+- * The string manager for this package.
+- */
+- protected StringManager sm = StringManager.getManager(Constants.Package);
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "NioReceiver/1.0";
+-
+- private Selector selector = null;
+- private ServerSocketChannel serverChannel = null;
+-
+- protected LinkedList events = new LinkedList();
+-// private Object interestOpsMutex = new Object();
+-
+- public NioReceiver() {
+- }
+-
+- /**
+- * Return descriptive information about this implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+- return (info);
+- }
+-
+-// public Object getInterestOpsMutex() {
+-// return interestOpsMutex;
+-// }
+-
+- public void stop() {
+- this.stopListening();
+- super.stop();
+- }
+-
+- /**
+- * start cluster receiver
+- * @throws Exception
+- * @see org.apache.catalina.tribes.ClusterReceiver#start()
+- */
+- public void start() throws IOException {
+- super.start();
+- try {
+- setPool(new RxTaskPool(getMaxThreads(),getMinThreads(),this));
+- } catch (Exception x) {
+- log.fatal("ThreadPool can initilzed. Listener not started", x);
+- if ( x instanceof IOException ) throw (IOException)x;
+- else throw new IOException(x.getMessage());
+- }
+- try {
+- getBind();
+- bind();
+- Thread t = new Thread(this, "NioReceiver");
+- t.setDaemon(true);
+- t.start();
+- } catch (Exception x) {
+- log.fatal("Unable to start cluster receiver", x);
+- if ( x instanceof IOException ) throw (IOException)x;
+- else throw new IOException(x.getMessage());
+- }
+- }
+-
+- public AbstractRxTask createRxTask() {
+- NioReplicationTask thread = new NioReplicationTask(this,this);
+- thread.setUseBufferPool(this.getUseBufferPool());
+- thread.setRxBufSize(getRxBufSize());
+- thread.setOptions(getWorkerThreadOptions());
+- return thread;
+- }
+-
+-
+-
+- protected void bind() throws IOException {
+- // allocate an unbound server socket channel
+- serverChannel = ServerSocketChannel.open();
+- // Get the associated ServerSocket to bind it with
+- ServerSocket serverSocket = serverChannel.socket();
+- // create a new Selector for use below
+- selector = Selector.open();
+- // set the port the server channel will listen to
+- //serverSocket.bind(new InetSocketAddress(getBind(), getTcpListenPort()));
+- bind(serverSocket,getTcpListenPort(),getAutoBind());
+- // set non-blocking mode for the listening socket
+- serverChannel.configureBlocking(false);
+- // register the ServerSocketChannel with the Selector
+- serverChannel.register(selector, SelectionKey.OP_ACCEPT);
+-
+- }
+-
+- public void addEvent(Runnable event) {
+- if ( selector != null ) {
+- synchronized (events) {
+- events.add(event);
+- }
+- if ( log.isTraceEnabled() ) log.trace("Adding event to
selector:"+event);
+- if ( isListening() && selector!=null ) selector.wakeup();
+- }
+- }
+-
+- public void events() {
+- if ( events.size() == 0 ) return;
+- synchronized (events) {
+- Runnable r = null;
+- while ( (events.size() > 0) && (r =
(Runnable)events.removeFirst()) != null ) {
+- try {
+- if ( log.isTraceEnabled() ) log.trace("Processing event in
selector:"+r);
+- r.run();
+- } catch ( Exception x ) {
+- log.error("",x);
+- }
+- }
+- events.clear();
+- }
+- }
+-
+- public static void cancelledKey(SelectionKey key) {
+- ObjectReader reader = (ObjectReader)key.attachment();
+- if ( reader != null ) {
+- reader.setCancelled(true);
+- reader.finish();
+- }
+- key.cancel();
+- key.attach(null);
+- try { ((SocketChannel)key.channel()).socket().close(); } catch (IOException e) {
if (log.isDebugEnabled()) log.debug("", e); }
+- try { key.channel().close(); } catch (IOException e) { if (log.isDebugEnabled())
log.debug("", e); }
+-
+- }
+- protected long lastCheck = System.currentTimeMillis();
+- protected void socketTimeouts() {
+- long now = System.currentTimeMillis();
+- if ( (now-lastCheck) < getSelectorTimeout() ) return;
+- //timeout
+- Selector tmpsel = selector;
+- Set keys = (isListening()&&tmpsel!=null)?tmpsel.keys():null;
+- if ( keys == null ) return;
+- for (Iterator iter = keys.iterator(); iter.hasNext(); ) {
+- SelectionKey key = (SelectionKey) iter.next();
+- try {
+-// if (key.interestOps() == SelectionKey.OP_READ) {
+-// //only timeout sockets that we are waiting for a read from
+-// ObjectReader ka = (ObjectReader) key.attachment();
+-// long delta = now - ka.getLastAccess();
+-// if (delta > (long) getTimeout()) {
+-// cancelledKey(key);
+-// }
+-// }
+-// else
+- if ( key.interestOps() == 0 ) {
+- //check for keys that didn't make it in.
+- ObjectReader ka = (ObjectReader) key.attachment();
+- if ( ka != null ) {
+- long delta = now - ka.getLastAccess();
+- if (delta > (long) getTimeout() &&
(!ka.isAccessed())) {
+- log.warn("Channel key is registered, but has had no
interest ops for the last "+getTimeout()+" ms.
(cancelled:"+ka.isCancelled()+"):"+key+" last access:"+new
java.sql.Timestamp(ka.getLastAccess()));
+-//
System.out.println("Interest:"+key.interestOps());
+-// System.out.println("Ready
Ops:"+key.readyOps());
+-// System.out.println("Valid:"+key.isValid());
+- ka.setLastAccess(now);
+- //key.interestOps(SelectionKey.OP_READ);
+- }//end if
+- } else {
+- cancelledKey(key);
+- }//end if
+- }//end if
+- }catch ( CancelledKeyException ckx ) {
+- cancelledKey(key);
+- }
+- }
+- lastCheck = System.currentTimeMillis();
+- }
+-
+-
+- /**
+- * get data from channel and store in byte array
+- * send it to cluster
+- * @throws IOException
+- * @throws java.nio.channels.ClosedChannelException
+- */
+- protected void listen() throws Exception {
+- if (doListen()) {
+- log.warn("ServerSocketChannel already started");
+- return;
+- }
+-
+- setListen(true);
+-
+- while (doListen() && selector != null) {
+- // this may block for a long time, upon return the
+- // selected set contains keys of the ready channels
+- try {
+- events();
+- socketTimeouts();
+- int n = selector.select(getTcpSelectorTimeout());
+- if (n == 0) {
+- //there is a good chance that we got here
+- //because the TcpReplicationThread called
+- //selector wakeup().
+- //if that happens, we must ensure that that
+- //thread has enough time to call interestOps
+-// synchronized (interestOpsMutex) {
+- //if we got the lock, means there are no
+- //keys trying to register for the
+- //interestOps method
+-// }
+- continue; // nothing to do
+- }
+- // get an iterator over the set of selected keys
+- Iterator it = selector.selectedKeys().iterator();
+- // look at each key in the selected set
+- while (it.hasNext()) {
+- SelectionKey key = (SelectionKey) it.next();
+- // Is a new connection coming in?
+- if (key.isAcceptable()) {
+- ServerSocketChannel server = (ServerSocketChannel)
key.channel();
+- SocketChannel channel = server.accept();
+- channel.socket().setReceiveBufferSize(getRxBufSize());
+- channel.socket().setSendBufferSize(getTxBufSize());
+- channel.socket().setTcpNoDelay(getTcpNoDelay());
+- channel.socket().setKeepAlive(getSoKeepAlive());
+- channel.socket().setOOBInline(getOoBInline());
+- channel.socket().setReuseAddress(getSoReuseAddress());
+-
channel.socket().setSoLinger(getSoLingerOn(),getSoLingerTime());
+- channel.socket().setTrafficClass(getSoTrafficClass());
+- channel.socket().setSoTimeout(getTimeout());
+- Object attach = new ObjectReader(channel);
+- registerChannel(selector,
+- channel,
+- SelectionKey.OP_READ,
+- attach);
+- }
+- // is there data to read on this channel?
+- if (key.isReadable()) {
+- readDataFromSocket(key);
+- } else {
+- key.interestOps(key.interestOps() &
(~SelectionKey.OP_WRITE));
+- }
+-
+- // remove key from selected set, it's been handled
+- it.remove();
+- }
+- } catch (java.nio.channels.ClosedSelectorException cse) {
+- // ignore is normal at shutdown or stop listen socket
+- } catch (java.nio.channels.CancelledKeyException nx) {
+- log.warn("Replication client disconnected, error when polling key.
Ignoring client.");
+- } catch (Throwable x) {
+- try {
+- log.error("Unable to process request in NioReceiver", x);
+- }catch ( Throwable tx ) {
+- //in case an out of memory error, will affect the logging framework
as well
+- tx.printStackTrace();
+- }
+- }
+-
+- }
+- serverChannel.close();
+- if (selector != null)
+- selector.close();
+- }
+-
+-
+-
+- /**
+- * Close Selector.
+- *
+- * @see org.apache.catalina.tribes.transport.ClusterReceiverBase#stopListening()
+- */
+- protected void stopListening() {
+- setListen(false);
+- if (selector != null) {
+- try {
+- selector.wakeup();
+- selector.close();
+- } catch (Exception x) {
+- log.error("Unable to close cluster receiver selector.", x);
+- } finally {
+- selector = null;
+- }
+- }
+- }
+-
+- // ----------------------------------------------------------
+-
+- /**
+- * Register the given channel with the given selector for
+- * the given operations of interest
+- */
+- protected void registerChannel(Selector selector,
+- SelectableChannel channel,
+- int ops,
+- Object attach) throws Exception {
+- if (channel == null)return; // could happen
+- // set the new channel non-blocking
+- channel.configureBlocking(false);
+- // register it with the selector
+- channel.register(selector, ops, attach);
+- }
+-
+- /**
+- * Start thread and listen
+- */
+- public void run() {
+- try {
+- listen();
+- } catch (Exception x) {
+- log.error("Unable to run replication listener.", x);
+- }
+- }
+-
+- // ----------------------------------------------------------
+-
+- /**
+- * Sample data handler method for a channel with data ready to read.
+- * @param key A SelectionKey object associated with a channel
+- * determined by the selector to be ready for reading. If the
+- * channel returns an EOF condition, it is closed here, which
+- * automatically invalidates the associated key. The selector
+- * will then de-register the channel on the next select call.
+- */
+- protected void readDataFromSocket(SelectionKey key) throws Exception {
+- NioReplicationTask task = (NioReplicationTask) getTaskPool().getRxTask();
+- if (task == null) {
+- // No threads/tasks available, do nothing, the selection
+- // loop will keep calling this method until a
+- // thread becomes available, the thread pool itself has a waiting mechanism
+- // so we will not wait here.
+- if (log.isDebugEnabled()) log.debug("No TcpReplicationThread
available");
+- } else {
+- // invoking this wakes up the worker thread then returns
+- //add task to thread pool
+- task.serviceChannel(key);
+- getExecutor().execute(task);
+- }
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/MultiPointSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/MultiPointSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/MultiPointSender.java (working copy)
+@@ -1,38 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- * @since 5.5.16
+- */
+-
+-public interface MultiPointSender extends DataSender
+-{
+- public void sendMessage(Member[] destination, ChannelMessage data) throws
ChannelException;
+- public void setRxBufSize(int size);
+- public void setTxBufSize(int size);
+- public void setMaxRetryAttempts(int attempts);
+- public void setDirectBuffer(boolean directBuf);
+- public void add(Member member);
+- public void remove(Member member);
+-}
+Index: java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/ReplicationTransmitter.java (working copy)
+@@ -1,135 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.ChannelSender;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.util.StringManager;
+-import org.apache.catalina.tribes.transport.nio.PooledParallelSender;
+-
+-/**
+- * Transmit message to other cluster members
+- * Actual senders are created based on the replicationMode
+- * type
+- *
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- */
+-public class ReplicationTransmitter implements ChannelSender {
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ReplicationTransmitter.class);
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "ReplicationTransmitter/3.0";
+-
+- /**
+- * The string manager for this package.
+- */
+- protected StringManager sm = StringManager.getManager(Constants.Package);
+-
+-
+-
+- public ReplicationTransmitter() {
+- }
+-
+- private MultiPointSender transport = new PooledParallelSender();
+-
+- /**
+- * Return descriptive information about this implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+- return (info);
+- }
+-
+- public MultiPointSender getTransport() {
+- return transport;
+- }
+-
+- public void setTransport(MultiPointSender transport) {
+- this.transport = transport;
+- }
+-
+- // ------------------------------------------------------------- public
+-
+- /**
+- * Send data to one member
+- * @see
org.apache.catalina.tribes.ClusterSender#sendMessage(org.apache.catalina.tribes.ClusterMessage,
org.apache.catalina.tribes.Member)
+- */
+- public void sendMessage(ChannelMessage message, Member[] destination) throws
ChannelException {
+- MultiPointSender sender = getTransport();
+- sender.sendMessage(destination,message);
+- }
+-
+-
+- /**
+- * start the sender and register transmitter mbean
+- *
+- * @see org.apache.catalina.tribes.ClusterSender#start()
+- */
+- public void start() throws java.io.IOException {
+- getTransport().connect();
+- }
+-
+- /*
+- * stop the sender and deregister mbeans (transmitter, senders)
+- *
+- * @see org.apache.catalina.tribes.ClusterSender#stop()
+- */
+- public synchronized void stop() {
+- getTransport().disconnect();
+- }
+-
+- /**
+- * Call transmitter to check for sender socket status
+- *
+- * @see SimpleTcpCluster#backgroundProcess()
+- */
+-
+- public void heartbeat() {
+- if (getTransport()!=null) getTransport().keepalive();
+- }
+-
+- /**
+- * add new cluster member and create sender ( s. replicationMode) transfer
+- * current properties to sender
+- *
+- * @see
org.apache.catalina.tribes.ClusterSender#add(org.apache.catalina.tribes.Member)
+- */
+- public synchronized void add(Member member) {
+- getTransport().add(member);
+- }
+-
+- /**
+- * remove sender from transmitter. ( deregister mbean and disconnect sender )
+- *
+- * @see
org.apache.catalina.tribes.ClusterSender#remove(org.apache.catalina.tribes.Member)
+- */
+- public synchronized void remove(Member member) {
+- getTransport().remove(member);
+- }
+-
+- // ------------------------------------------------------------- protected
+-
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/RxTaskPool.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/RxTaskPool.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/RxTaskPool.java (working copy)
+@@ -1,159 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport;
+-import java.util.Iterator;
+-import java.util.LinkedList;
+-import java.util.List;
+-import java.util.concurrent.ThreadFactory;
+-
+-/**
+- * @author not attributable
+- * @version 1.0
+- */
+-
+-public class RxTaskPool
+-{
+- /**
+- * A very simple thread pool class. The pool size is set at
+- * construction time and remains fixed. Threads are cycled
+- * through a FIFO idle queue.
+- */
+-
+- List idle = new LinkedList();
+- List used = new LinkedList();
+-
+- Object mutex = new Object();
+- boolean running = true;
+-
+- private static int counter = 1;
+- private int maxTasks;
+- private int minTasks;
+-
+- private TaskCreator creator = null;
+-
+- private static synchronized int inc() {
+- return counter++;
+- }
+-
+-
+- public RxTaskPool (int maxTasks, int minTasks, TaskCreator creator) throws Exception
{
+- // fill up the pool with worker threads
+- this.maxTasks = maxTasks;
+- this.minTasks = minTasks;
+- this.creator = creator;
+- }
+-
+- protected void configureTask(AbstractRxTask task) {
+- synchronized (task) {
+- task.setTaskPool(this);
+-// task.setName(task.getClass().getName() + "[" + inc() +
"]");
+-// task.setDaemon(true);
+-// task.setPriority(Thread.MAX_PRIORITY);
+-// task.start();
+- }
+- }
+-
+- /**
+- * Find an idle worker thread, if any. Could return null.
+- */
+- public AbstractRxTask getRxTask()
+- {
+- AbstractRxTask worker = null;
+- synchronized (mutex) {
+- while ( worker == null && running ) {
+- if (idle.size() > 0) {
+- try {
+- worker = (AbstractRxTask) idle.remove(0);
+- } catch (java.util.NoSuchElementException x) {
+- //this means that there are no available workers
+- worker = null;
+- }
+- } else if ( used.size() < this.maxTasks && creator != null)
{
+- worker = creator.createRxTask();
+- configureTask(worker);
+- } else {
+- try { mutex.wait(); } catch ( java.lang.InterruptedException x )
{Thread.currentThread().interrupted();}
+- }
+- }//while
+- if ( worker != null ) used.add(worker);
+- }
+- return (worker);
+- }
+-
+- public int available() {
+- return idle.size();
+- }
+-
+- /**
+- * Called by the worker thread to return itself to the
+- * idle pool.
+- */
+- public void returnWorker (AbstractRxTask worker) {
+- if ( running ) {
+- synchronized (mutex) {
+- used.remove(worker);
+- //if ( idle.size() < minThreads && !idle.contains(worker))
idle.add(worker);
+- if ( idle.size() < maxTasks && !idle.contains(worker))
idle.add(worker); //let max be the upper limit
+- else {
+- worker.setDoRun(false);
+- synchronized (worker){worker.notify();}
+- }
+- mutex.notify();
+- }
+- }else {
+- worker.setDoRun(false);
+- synchronized (worker){worker.notify();}
+- }
+- }
+-
+- public int getMaxThreads() {
+- return maxTasks;
+- }
+-
+- public int getMinThreads() {
+- return minTasks;
+- }
+-
+- public void stop() {
+- running = false;
+- synchronized (mutex) {
+- Iterator i = idle.iterator();
+- while ( i.hasNext() ) {
+- AbstractRxTask worker = (AbstractRxTask)i.next();
+- returnWorker(worker);
+- i.remove();
+- }
+- }
+- }
+-
+- public void setMaxTasks(int maxThreads) {
+- this.maxTasks = maxThreads;
+- }
+-
+- public void setMinTasks(int minThreads) {
+- this.minTasks = minThreads;
+- }
+-
+- public TaskCreator getTaskCreator() {
+- return this.creator;
+- }
+-
+- public static interface TaskCreator {
+- public AbstractRxTask createRxTask();
+- }
+-}
+Index: java/org/apache/catalina/tribes/transport/bio/MultipointBioSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/MultipointBioSender.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/bio/MultipointBioSender.java (working
copy)
+@@ -1,144 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.bio;
+-
+-import java.io.IOException;
+-import java.util.HashMap;
+-import java.util.Map;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.transport.MultiPointSender;
+-import org.apache.catalina.tribes.transport.AbstractSender;
+-import org.apache.catalina.tribes.Channel;
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- *
+- */
+-public class MultipointBioSender extends AbstractSender implements MultiPointSender {
+- public MultipointBioSender() {
+- }
+-
+- protected long selectTimeout = 1000;
+- protected HashMap bioSenders = new HashMap();
+-
+- public synchronized void sendMessage(Member[] destination, ChannelMessage msg)
throws ChannelException {
+- byte[] data = XByteBuffer.createDataPackage((ChannelData)msg);
+- BioSender[] senders = setupForSend(destination);
+- ChannelException cx = null;
+- for ( int i=0; i<senders.length; i++ ) {
+- try {
+-
senders[i].sendMessage(data,(msg.getOptions()&Channel.SEND_OPTIONS_USE_ACK)==Channel.SEND_OPTIONS_USE_ACK);
+- } catch (Exception x) {
+- if (cx == null) cx = new ChannelException(x);
+- cx.addFaultyMember(destination[i],x);
+- }
+- }
+- if (cx!=null ) throw cx;
+- }
+-
+-
+-
+- protected BioSender[] setupForSend(Member[] destination) throws ChannelException {
+- ChannelException cx = null;
+- BioSender[] result = new BioSender[destination.length];
+- for ( int i=0; i<destination.length; i++ ) {
+- try {
+- BioSender sender = (BioSender) bioSenders.get(destination[i]);
+- if (sender == null) {
+- sender = new BioSender();
+- sender.transferProperties(this,sender);
+- sender.setDestination(destination[i]);
+- bioSenders.put(destination[i], sender);
+- }
+- result[i] = sender;
+- if (!result[i].isConnected() ) result[i].connect();
+- result[i].keepalive();
+- }catch (Exception x ) {
+- if ( cx== null ) cx = new ChannelException(x);
+- cx.addFaultyMember(destination[i],x);
+- }
+- }
+- if ( cx!=null ) throw cx;
+- else return result;
+- }
+-
+- public void connect() throws IOException {
+- //do nothing, we connect on demand
+- setConnected(true);
+- }
+-
+-
+- private synchronized void close() throws ChannelException {
+- ChannelException x = null;
+- Object[] members = bioSenders.keySet().toArray();
+- for (int i=0; i<members.length; i++ ) {
+- Member mbr = (Member)members[i];
+- try {
+- BioSender sender = (BioSender)bioSenders.get(mbr);
+- sender.disconnect();
+- }catch ( Exception e ) {
+- if ( x == null ) x = new ChannelException(e);
+- x.addFaultyMember(mbr,e);
+- }
+- bioSenders.remove(mbr);
+- }
+- if ( x != null ) throw x;
+- }
+-
+- public void add(Member member) {
+-
+- }
+-
+- public void remove(Member member) {
+- //disconnect senders
+- BioSender sender = (BioSender)bioSenders.remove(member);
+- if ( sender != null ) sender.disconnect();
+- }
+-
+-
+- public synchronized void disconnect() {
+- try {close(); }catch (Exception x){}
+- setConnected(false);
+- }
+-
+- public void finalize() {
+- try {disconnect(); }catch ( Exception ignore){}
+- }
+-
+-
+- public boolean keepalive() {
+- //throw new UnsupportedOperationException("Method
ParallelBioSender.checkKeepAlive() not implemented");
+- boolean result = false;
+- Map.Entry[] entries = (Map.Entry[])bioSenders.entrySet().toArray(new
Map.Entry[bioSenders.size()]);
+- for ( int i=0; i<entries.length; i++ ) {
+- BioSender sender = (BioSender)entries[i].getValue();
+- if ( sender.keepalive() ) {
+- bioSenders.remove(entries[i].getKey());
+- }
+- }
+- return result;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/bio/BioReplicationTask.java (working copy)
+@@ -1,166 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.bio;
+-
+-import org.apache.catalina.tribes.io.ObjectReader;
+-import org.apache.catalina.tribes.transport.Constants;
+-import org.apache.catalina.tribes.transport.AbstractRxTask;
+-import java.net.Socket;
+-import java.io.InputStream;
+-import java.io.OutputStream;
+-import org.apache.catalina.tribes.io.ListenCallback;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.BufferPool;
+-
+-/**
+- * A worker thread class which can drain channels and echo-back the input. Each
+- * instance is constructed with a reference to the owning thread pool object.
+- * When started, the thread loops forever waiting to be awakened to service the
+- * channel associated with a SelectionKey object. The worker is tasked by
+- * calling its serviceChannel() method with a SelectionKey object. The
+- * serviceChannel() method stores the key reference in the thread object then
+- * calls notify() to wake it up. When the channel has been drained, the worker
+- * thread returns itself to its parent pool.
+- *
+- * @author Filip Hanik
+- *
+- * @version $Revision$, $Date$
+- */
+-public class BioReplicationTask extends AbstractRxTask {
+-
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( BioReplicationTask.class );
+-
+- protected Socket socket;
+- protected ObjectReader reader;
+-
+- public BioReplicationTask (ListenCallback callback) {
+- super(callback);
+- }
+-
+- // loop forever waiting for work to do
+- public synchronized void run()
+- {
+- if ( socket == null ) return;
+- try {
+- drainSocket();
+- } catch ( Exception x ) {
+- log.error("Unable to service bio socket");
+- }finally {
+- try {socket.close();}catch ( Exception ignore){}
+- try {reader.close();}catch ( Exception ignore){}
+- reader = null;
+- socket = null;
+- }
+- // done, ready for more, return to pool
+- if ( getTaskPool() != null ) getTaskPool().returnWorker (this);
+- }
+-
+-
+- public synchronized void serviceSocket(Socket socket, ObjectReader reader) {
+- this.socket = socket;
+- this.reader = reader;
+- this.notify(); // awaken the thread
+- }
+-
+- protected void execute(ObjectReader reader) throws Exception{
+- int pkgcnt = reader.count();
+-
+- if ( pkgcnt > 0 ) {
+- ChannelMessage[] msgs = reader.execute();
+- for ( int i=0; i<msgs.length; i++ ) {
+- /**
+- * Use send ack here if you want to ack the request to the remote
+- * server before completing the request
+- * This is considered an asynchronized request
+- */
+- if (ChannelData.sendAckAsync(msgs[i].getOptions()))
sendAck(Constants.ACK_COMMAND);
+- try {
+- //process the message
+- getCallback().messageDataReceived(msgs[i]);
+- /**
+- * Use send ack here if you want the request to complete on this
+- * server before sending the ack to the remote server
+- * This is considered a synchronized request
+- */
+- if (ChannelData.sendAckSync(msgs[i].getOptions()))
sendAck(Constants.ACK_COMMAND);
+- }catch ( Exception x ) {
+- if (ChannelData.sendAckSync(msgs[i].getOptions()))
sendAck(Constants.FAIL_ACK_COMMAND);
+- log.error("Error thrown from messageDataReceived.",x);
+- }
+- if ( getUseBufferPool() ) {
+- BufferPool.getBufferPool().returnBuffer(msgs[i].getMessage());
+- msgs[i].setMessage(null);
+- }
+- }
+- }
+-
+-
+- }
+-
+- /**
+- * The actual code which drains the channel associated with
+- * the given key. This method assumes the key has been
+- * modified prior to invocation to turn off selection
+- * interest in OP_READ. When this method completes it
+- * re-enables OP_READ and calls wakeup() on the selector
+- * so the selector will resume watching this channel.
+- */
+- protected void drainSocket () throws Exception {
+- InputStream in = socket.getInputStream();
+- // loop while data available, channel is non-blocking
+- byte[] buf = new byte[1024];
+- int length = in.read(buf);
+- while ( length >= 0 ) {
+- int count = reader.append(buf,0,length,true);
+- if ( count > 0 ) execute(reader);
+- length = in.read(buf);
+- }
+- }
+-
+-
+-
+-
+- /**
+- * send a reply-acknowledgement (6,2,3)
+- * @param key
+- * @param channel
+- */
+- protected void sendAck(byte[] command) {
+- try {
+- OutputStream out = socket.getOutputStream();
+- out.write(command);
+- out.flush();
+- if (log.isTraceEnabled()) {
+- log.trace("ACK sent to " + socket.getPort());
+- }
+- } catch ( java.io.IOException x ) {
+- log.warn("Unable to send ACK back through channel, channel
disconnected?: "+x.getMessage());
+- }
+- }
+-
+- public void close() {
+- setDoRun(false);
+- try {socket.close();}catch ( Exception ignore){}
+- try {reader.close();}catch ( Exception ignore){}
+- reader = null;
+- socket = null;
+- super.close();
+- }
+-}
+Index: java/org/apache/catalina/tribes/transport/bio/BioSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/BioSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/bio/BioSender.java (working copy)
+@@ -1,296 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.bio;
+-
+-import java.io.IOException;
+-import java.io.InputStream;
+-import java.io.OutputStream;
+-import java.net.InetSocketAddress;
+-import java.net.Socket;
+-import java.util.Arrays;
+-
+-import org.apache.catalina.tribes.RemoteProcessException;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.transport.AbstractSender;
+-import org.apache.catalina.tribes.transport.Constants;
+-import org.apache.catalina.tribes.transport.DataSender;
+-import org.apache.catalina.tribes.transport.SenderState;
+-import org.apache.catalina.tribes.util.StringManager;
+-
+-/**
+- * Send cluster messages with only one socket. Ack and keep Alive Handling is
+- * supported
+- *
+- * @author Peter Rossbach
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- * @since 5.5.16
+- */
+-public class BioSender extends AbstractSender implements DataSender {
+-
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(BioSender.class);
+-
+- /**
+- * The string manager for this package.
+- */
+- protected static StringManager sm = StringManager.getManager(Constants.Package);
+-
+- // ----------------------------------------------------- Instance Variables
+-
+- /**
+- * The descriptive information about this implementation.
+- */
+- private static final String info = "DataSender/3.0";
+-
+-
+- /**
+- * current sender socket
+- */
+- private Socket socket = null;
+- private OutputStream soOut = null;
+- private InputStream soIn = null;
+-
+- protected XByteBuffer ackbuf = new XByteBuffer(Constants.ACK_COMMAND.length,true);
+-
+-
+- // ------------------------------------------------------------- Constructor
+-
+- public BioSender() {
+- }
+-
+-
+- // ------------------------------------------------------------- Properties
+-
+- /**
+- * Return descriptive information about this implementation and the
+- * corresponding version number, in the format
+- * <code><description>/<version></code>.
+- */
+- public String getInfo() {
+- return (info);
+- }
+-
+- // --------------------------------------------------------- Public Methods
+-
+- /**
+- * Connect other cluster member receiver
+- * @see org.apache.catalina.tribes.transport.IDataSender#connect()
+- */
+- public void connect() throws IOException {
+- openSocket();
+- }
+-
+-
+- /**
+- * disconnect and close socket
+- *
+- * @see IDataSender#disconnect()
+- */
+- public void disconnect() {
+- boolean connect = isConnected();
+- closeSocket();
+- if (connect) {
+- if (log.isDebugEnabled())
+- log.debug(sm.getString("IDataSender.disconnect",
getAddress().getHostAddress(), new Integer(getPort()), new Long(0)));
+- }
+-
+- }
+-
+- /**
+- * Send message
+- *
+- * @see org.apache.catalina.tribes.transport.IDataSender#sendMessage(,
+- * ChannelMessage)
+- */
+- public void sendMessage(byte[] data, boolean waitForAck) throws IOException {
+- IOException exception = null;
+- setAttempt(0);
+- try {
+- // first try with existing connection
+- pushMessage(data,false,waitForAck);
+- } catch (IOException x) {
+- SenderState.getSenderState(getDestination()).setSuspect();
+- exception = x;
+- if (log.isTraceEnabled())
log.trace(sm.getString("IDataSender.send.again",
getAddress().getHostAddress(),new Integer(getPort())),x);
+- while ( getAttempt()<getMaxRetryAttempts() ) {
+- try {
+- setAttempt(getAttempt()+1);
+- // second try with fresh connection
+- pushMessage(data, true,waitForAck);
+- exception = null;
+- } catch (IOException xx) {
+- exception = xx;
+- closeSocket();
+- }
+- }
+- } finally {
+- setRequestCount(getRequestCount()+1);
+- keepalive();
+- if ( exception != null ) throw exception;
+- }
+- }
+-
+-
+- /**
+- * Name of this SockerSender
+- */
+- public String toString() {
+- StringBuffer buf = new StringBuffer("DataSender[(");
+- buf.append(super.toString()).append(")");
+-
buf.append(getAddress()).append(":").append(getPort()).append("]");
+- return buf.toString();
+- }
+-
+- // --------------------------------------------------------- Protected Methods
+-
+- /**
+- * open real socket and set time out when waitForAck is enabled
+- * is socket open return directly
+- */
+- protected void openSocket() throws IOException {
+- if(isConnected()) return ;
+- try {
+- socket = new Socket();
+- InetSocketAddress sockaddr = new InetSocketAddress(getAddress(), getPort());
+- socket.connect(sockaddr,(int)getTimeout());
+- socket.setSendBufferSize(getTxBufSize());
+- socket.setReceiveBufferSize(getRxBufSize());
+- socket.setSoTimeout( (int) getTimeout());
+- socket.setTcpNoDelay(getTcpNoDelay());
+- socket.setKeepAlive(getSoKeepAlive());
+- socket.setReuseAddress(getSoReuseAddress());
+- socket.setOOBInline(getOoBInline());
+- socket.setSoLinger(getSoLingerOn(),getSoLingerTime());
+- socket.setTrafficClass(getSoTrafficClass());
+- setConnected(true);
+- soOut = socket.getOutputStream();
+- soIn = socket.getInputStream();
+- setRequestCount(0);
+- setConnectTime(System.currentTimeMillis());
+- if (log.isDebugEnabled())
+- log.debug(sm.getString("IDataSender.openSocket",
getAddress().getHostAddress(), new Integer(getPort()), new Long(0)));
+- } catch (IOException ex1) {
+- SenderState.getSenderState(getDestination()).setSuspect();
+- if (log.isDebugEnabled())
+-
log.debug(sm.getString("IDataSender.openSocket.failure",getAddress().getHostAddress(),
new Integer(getPort()),new Long(0)), ex1);
+- throw (ex1);
+- }
+-
+- }
+-
+- /**
+- * close socket
+- *
+- * @see DataSender#disconnect()
+- * @see DataSender#closeSocket()
+- */
+- protected void closeSocket() {
+- if(isConnected()) {
+- if (socket != null) {
+- try {
+- socket.close();
+- } catch (IOException x) {
+- } finally {
+- socket = null;
+- soOut = null;
+- soIn = null;
+- }
+- }
+- setRequestCount(0);
+- setConnected(false);
+- if (log.isDebugEnabled())
+-
log.debug(sm.getString("IDataSender.closeSocket",getAddress().getHostAddress(),
new Integer(getPort()),new Long(0)));
+- }
+- }
+-
+- /**
+- * Push messages with only one socket at a time
+- * Wait for ack is needed and make auto retry when write message is failed.
+- * After sending error close and reopen socket again.
+- *
+- * After successfull sending update stats
+- *
+- * WARNING: Subclasses must be very carefull that only one thread call this
pushMessage at once!!!
+- *
+- * @see #closeSocket()
+- * @see #openSocket()
+- * @see #writeData(ChannelMessage)
+- *
+- * @param data
+- * data to send
+- * @since 5.5.10
+- */
+-
+- protected void pushMessage(byte[] data, boolean reconnect, boolean waitForAck)
throws IOException {
+- keepalive();
+- if ( reconnect ) closeSocket();
+- if (!isConnected()) openSocket();
+- soOut.write(data);
+- soOut.flush();
+- if (waitForAck) waitForAck();
+- SenderState.getSenderState(getDestination()).setReady();
+-
+- }
+-
+- /**
+- * Wait for Acknowledgement from other server
+- * FIXME Please, not wait only for three charcters, better control that the wait ack
message is correct.
+- * @param timeout
+- * @throws java.io.IOException
+- * @throws java.net.SocketTimeoutException
+- */
+- protected void waitForAck() throws java.io.IOException {
+- try {
+- boolean ackReceived = false;
+- boolean failAckReceived = false;
+- ackbuf.clear();
+- int bytesRead = 0;
+- int i = soIn.read();
+- while ((i != -1) && (bytesRead < Constants.ACK_COMMAND.length))
{
+- bytesRead++;
+- byte d = (byte)i;
+- ackbuf.append(d);
+- if (ackbuf.doesPackageExist() ) {
+- byte[] ackcmd = ackbuf.extractDataPackage(true).getBytes();
+- ackReceived =
Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.ACK_DATA);
+- failAckReceived =
Arrays.equals(ackcmd,org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA);
+- ackReceived = ackReceived || failAckReceived;
+- break;
+- }
+- i = soIn.read();
+- }
+- if (!ackReceived) {
+- if (i == -1) throw new
IOException(sm.getString("IDataSender.ack.eof",getAddress(), new
Integer(socket.getLocalPort())));
+- else throw new
IOException(sm.getString("IDataSender.ack.wrong",getAddress(), new
Integer(socket.getLocalPort())));
+- } else if ( failAckReceived && getThrowOnFailedAck()) {
+- throw new RemoteProcessException("Received a failed
ack:org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA");
+- }
+- } catch (IOException x) {
+- String errmsg = sm.getString("IDataSender.ack.missing",
getAddress(),new Integer(socket.getLocalPort()), new Long(getTimeout()));
+- if ( SenderState.getSenderState(getDestination()).isReady() ) {
+- SenderState.getSenderState(getDestination()).setSuspect();
+- if ( log.isWarnEnabled() ) log.warn(errmsg, x);
+- } else {
+- if ( log.isDebugEnabled() )log.debug(errmsg, x);
+- }
+- throw x;
+- } finally {
+- ackbuf.clear();
+- }
+- }
+-}
+Index:
java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java
+===================================================================
+---
java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java (revision
590752)
++++
java/org/apache/catalina/tribes/transport/bio/util/SingleRemoveSynchronizedAddLock.java (working
copy)
+@@ -1,254 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.bio.util;
+-
+-/**
+- * The class <b>SingleRemoveSynchronizedAddLock</b> implement locking for
accessing the queue
+- * by a single remove thread and multiple add threads.
+- *
+- * A thread is only allowed to be either the remove or
+- * an add thread.
+- *
+- * The lock can either be owned by the remove thread
+- * or by a single add thread.
+- *
+- * If the remove thread tries to get the lock,
+- * but the queue is empty, it will block (poll)
+- * until an add threads adds an entry to the queue and
+- * releases the lock.
+- *
+- * If the remove thread and add threads compete for
+- * the lock and an add thread releases the lock, then
+- * the remove thread will get the lock first.
+- *
+- * The remove thread removes all entries in the queue
+- * at once and proceeses them without further
+- * polling the queue.
+- *
+- * The lock is not reentrant, in the sense, that all
+- * threads must release an owned lock before competing
+- * for the lock again!
+- *
+- * @author Rainer Jung
+- * @author Peter Rossbach
+- * @version 1.1
+- */
+-
+-public class SingleRemoveSynchronizedAddLock {
+-
+- public SingleRemoveSynchronizedAddLock() {
+- }
+-
+- public SingleRemoveSynchronizedAddLock(boolean dataAvailable) {
+- this.dataAvailable=dataAvailable;
+- }
+-
+- /**
+- * Time in milliseconds after which threads
+- * waiting for an add lock are woken up.
+- * This is used as a safety measure in case
+- * thread notification via the unlock methods
+- * has a bug.
+- */
+- private long addWaitTimeout = 10000L;
+-
+- /**
+- * Time in milliseconds after which threads
+- * waiting for a remove lock are woken up.
+- * This is used as a safety measure in case
+- * thread notification via the unlock methods
+- * has a bug.
+- */
+- private long removeWaitTimeout = 30000L;
+-
+- /**
+- * The current remove thread.
+- * It is set to the remove thread polling for entries.
+- * It is reset to null when the remove thread
+- * releases the lock and proceeds processing
+- * the removed entries.
+- */
+- private Thread remover = null;
+-
+- /**
+- * A flag indicating, if an add thread owns the lock.
+- */
+- private boolean addLocked = false;
+-
+- /**
+- * A flag indicating, if the remove thread owns the lock.
+- */
+- private boolean removeLocked = false;
+-
+- /**
+- * A flag indicating, if the remove thread is allowed
+- * to wait for the lock. The flag is set to false, when aborting.
+- */
+- private boolean removeEnabled = true;
+-
+- /**
+- * A flag indicating, if the remover needs polling.
+- * It indicates, if the locked object has data available
+- * to be removed.
+- */
+- private boolean dataAvailable = false;
+-
+- /**
+- * @return Value of addWaitTimeout
+- */
+- public synchronized long getAddWaitTimeout() {
+- return addWaitTimeout;
+- }
+-
+- /**
+- * Set value of addWaitTimeout
+- */
+- public synchronized void setAddWaitTimeout(long timeout) {
+- addWaitTimeout = timeout;
+- }
+-
+- /**
+- * @return Value of removeWaitTimeout
+- */
+- public synchronized long getRemoveWaitTimeout() {
+- return removeWaitTimeout;
+- }
+-
+- /**
+- * Set value of removeWaitTimeout
+- */
+- public synchronized void setRemoveWaitTimeout(long timeout) {
+- removeWaitTimeout = timeout;
+- }
+-
+- /**
+- * Check if the locked object has data available
+- * i.e. the remover can stop poling and get the lock.
+- * @return True iff the lock Object has data available.
+- */
+- public synchronized boolean isDataAvailable() {
+- return dataAvailable;
+- }
+-
+- /**
+- * Check if an add thread owns the lock.
+- * @return True iff an add thread owns the lock.
+- */
+- public synchronized boolean isAddLocked() {
+- return addLocked;
+- }
+-
+- /**
+- * Check if the remove thread owns the lock.
+- * @return True iff the remove thread owns the lock.
+- */
+- public synchronized boolean isRemoveLocked() {
+- return removeLocked;
+- }
+-
+- /**
+- * Check if the remove thread is polling.
+- * @return True iff the remove thread is polling.
+- */
+- public synchronized boolean isRemovePolling() {
+- if ( remover != null ) {
+- return true;
+- }
+- return false;
+- }
+-
+- /**
+- * Acquires the lock by an add thread and sets the add flag.
+- * If any add thread or the remove thread already acquired the lock
+- * this add thread will block until the lock is released.
+- */
+- public synchronized void lockAdd() {
+- if ( addLocked || removeLocked ) {
+- do {
+- try {
+- wait(addWaitTimeout);
+- } catch ( InterruptedException e ) {
+- Thread.currentThread().interrupted();
+- }
+- } while ( addLocked || removeLocked );
+- }
+- addLocked=true;
+- }
+-
+- /**
+- * Acquires the lock by the remove thread and sets the remove flag.
+- * If any add thread already acquired the lock or the queue is
+- * empty, the remove thread will block until the lock is released
+- * and the queue is not empty.
+- */
+- public synchronized boolean lockRemove() {
+- removeLocked=false;
+- removeEnabled=true;
+- if ( ( addLocked || ! dataAvailable ) && removeEnabled ) {
+- remover=Thread.currentThread();
+- do {
+- try {
+- wait(removeWaitTimeout);
+- } catch ( InterruptedException e ) {
+- Thread.currentThread().interrupted();
+- }
+- } while ( ( addLocked || ! dataAvailable ) && removeEnabled );
+- remover=null;
+- }
+- if ( removeEnabled ) {
+- removeLocked=true;
+- }
+- return removeLocked;
+- }
+-
+- /**
+- * Releases the lock by an add thread and reset the remove flag.
+- * If the reader thread is polling, notify it.
+- */
+- public synchronized void unlockAdd(boolean dataAvailable) {
+- addLocked=false;
+- this.dataAvailable=dataAvailable;
+- if ( ( remover != null ) && ( dataAvailable || ! removeEnabled ) ) {
+- remover.interrupt();
+- } else {
+- notifyAll();
+- }
+- }
+-
+- /**
+- * Releases the lock by the remove thread and reset the add flag.
+- * Notify all waiting add threads,
+- * that the lock has been released by the remove thread.
+- */
+- public synchronized void unlockRemove() {
+- removeLocked=false;
+- dataAvailable=false;
+- notifyAll();
+- }
+-
+- /**
+- * Abort any polling remover thread
+- */
+- public synchronized void abortRemove() {
+- removeEnabled=false;
+- if ( remover != null ) {
+- remover.interrupt();
+- }
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/bio/util/FastQueue.java (working copy)
+@@ -1,393 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.bio.util;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-
+-
+-
+-/**
+- * A fast queue that remover thread lock the adder thread. <br/>Limit the queue
+- * length when you have strange producer thread problemes.
+- *
+- * FIXME add i18n support to log messages
+- * @author Rainer Jung
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-public class FastQueue {
+-
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(FastQueue.class);
+-
+- /**
+- * This is the actual queue
+- */
+- private SingleRemoveSynchronizedAddLock lock = null;
+-
+- /**
+- * First Object at queue (consumer message)
+- */
+- private LinkObject first = null;
+-
+- /**
+- * Last object in queue (producer Object)
+- */
+- private LinkObject last = null;
+-
+- /**
+- * Current Queue elements size
+- */
+- private int size = 0;
+-
+- /**
+- * check lock to detect strange threadings things
+- */
+- private boolean checkLock = false;
+-
+- /**
+- * protocol the thread wait times
+- */
+- private boolean timeWait = false;
+-
+- private boolean inAdd = false;
+-
+- private boolean inRemove = false;
+-
+- private boolean inMutex = false;
+-
+- /**
+- * limit the queue legnth ( default is unlimited)
+- */
+- private int maxQueueLength = 0;
+-
+- /**
+- * addWaitTimeout for producer
+- */
+- private long addWaitTimeout = 10000L;
+-
+-
+- /**
+- * removeWaitTimeout for consumer
+- */
+- private long removeWaitTimeout = 30000L;
+-
+- /**
+- * enabled the queue
+- */
+- private boolean enabled = true;
+-
+- /**
+- * max queue size
+- */
+- private int maxSize = 0;
+-
+- /**
+- * avg size sample interval
+- */
+- private int sampleInterval = 100;
+-
+- /**
+- * Generate Queue SingleRemoveSynchronizedAddLock and set add and wait
+- * Timeouts
+- */
+- public FastQueue() {
+- lock = new SingleRemoveSynchronizedAddLock();
+- lock.setAddWaitTimeout(addWaitTimeout);
+- lock.setRemoveWaitTimeout(removeWaitTimeout);
+- }
+-
+- /**
+- * get current add wait timeout
+- *
+- * @return current wait timeout
+- */
+- public long getAddWaitTimeout() {
+- addWaitTimeout = lock.getAddWaitTimeout();
+- return addWaitTimeout;
+- }
+-
+- /**
+- * Set add wait timeout (default 10000 msec)
+- *
+- * @param timeout
+- */
+- public void setAddWaitTimeout(long timeout) {
+- addWaitTimeout = timeout;
+- lock.setAddWaitTimeout(addWaitTimeout);
+- }
+-
+- /**
+- * get current remove wait timeout
+- *
+- * @return The timeout
+- */
+- public long getRemoveWaitTimeout() {
+- removeWaitTimeout = lock.getRemoveWaitTimeout();
+- return removeWaitTimeout;
+- }
+-
+- /**
+- * set remove wait timeout ( default 30000 msec)
+- *
+- * @param timeout
+- */
+- public void setRemoveWaitTimeout(long timeout) {
+- removeWaitTimeout = timeout;
+- lock.setRemoveWaitTimeout(removeWaitTimeout);
+- }
+-
+- /**
+- * get Max Queue length
+- *
+- * @see org.apache.catalina.tribes.util.IQueue#getMaxQueueLength()
+- */
+- public int getMaxQueueLength() {
+- return maxQueueLength;
+- }
+-
+- public void setMaxQueueLength(int length) {
+- maxQueueLength = length;
+- }
+-
+- public boolean isEnabled() {
+- return enabled;
+- }
+-
+- public void setEnabled(boolean enable) {
+- enabled = enable;
+- if (!enabled) {
+- lock.abortRemove();
+- last = first = null;
+- }
+- }
+-
+- /**
+- * @return Returns the checkLock.
+- */
+- public boolean isCheckLock() {
+- return checkLock;
+- }
+-
+- /**
+- * @param checkLock The checkLock to set.
+- */
+- public void setCheckLock(boolean checkLock) {
+- this.checkLock = checkLock;
+- }
+-
+-
+- /**
+- * @return The max size
+- */
+- public int getMaxSize() {
+- return maxSize;
+- }
+-
+- /**
+- * @param size
+- */
+- public void setMaxSize(int size) {
+- maxSize = size;
+- }
+-
+-
+- /**
+- * unlock queue for next add
+- */
+- public void unlockAdd() {
+- lock.unlockAdd(size > 0 ? true : false);
+- }
+-
+- /**
+- * unlock queue for next remove
+- */
+- public void unlockRemove() {
+- lock.unlockRemove();
+- }
+-
+- /**
+- * start queuing
+- */
+- public void start() {
+- setEnabled(true);
+- }
+-
+- /**
+- * start queuing
+- */
+- public void stop() {
+- setEnabled(false);
+- }
+-
+- public int getSize() {
+- return size;
+- }
+-
+- public SingleRemoveSynchronizedAddLock getLock() {
+- return lock;
+- }
+-
+- /**
+- * Add new data to the queue
+- * @see org.apache.catalina.tribes.util.IQueue#add(java.lang.String,
java.lang.Object)
+- * FIXME extract some method
+- */
+- public boolean add(ChannelMessage msg, Member[] destination, InterceptorPayload
payload) {
+- boolean ok = true;
+- long time = 0;
+-
+- if (!enabled) {
+- if (log.isInfoEnabled())
+- log.info("FastQueue.add: queue disabled, add aborted");
+- return false;
+- }
+-
+- if (timeWait) {
+- time = System.currentTimeMillis();
+- }
+- lock.lockAdd();
+- try {
+- if (log.isTraceEnabled()) {
+- log.trace("FastQueue.add: starting with size " + size);
+- }
+- if (checkLock) {
+- if (inAdd)
+- log.warn("FastQueue.add: Detected other add");
+- inAdd = true;
+- if (inMutex)
+- log.warn("FastQueue.add: Detected other mutex in add");
+- inMutex = true;
+- }
+-
+- if ((maxQueueLength > 0) && (size >= maxQueueLength)) {
+- ok = false;
+- if (log.isTraceEnabled()) {
+- log.trace("FastQueue.add: Could not add, since queue is full
(" + size + ">=" + maxQueueLength + ")");
+- }
+- } else {
+- LinkObject element = new LinkObject(msg,destination, payload);
+- if (size == 0) {
+- first = last = element;
+- size = 1;
+- } else {
+- if (last == null) {
+- ok = false;
+- log.error("FastQueue.add: Could not add, since last is null
although size is "+ size + " (>0)");
+- } else {
+- last.append(element);
+- last = element;
+- size++;
+- }
+- }
+- }
+-
+- if (first == null) {
+- log.error("FastQueue.add: first is null, size is " + size +
" at end of add");
+- }
+- if (last == null) {
+- log.error("FastQueue.add: last is null, size is " + size+
" at end of add");
+- }
+-
+- if (checkLock) {
+- if (!inMutex) log.warn("FastQueue.add: Cancelled by other mutex in
add");
+- inMutex = false;
+- if (!inAdd) log.warn("FastQueue.add: Cancelled by other
add");
+- inAdd = false;
+- }
+- if (log.isTraceEnabled()) log.trace("FastQueue.add: add ending with
size " + size);
+-
+- } finally {
+- lock.unlockAdd(true);
+- }
+- return ok;
+- }
+-
+- /**
+- * remove the complete queued object list
+- * @see org.apache.catalina.tribes.util.IQueue#remove()
+- * FIXME extract some method
+- */
+- public LinkObject remove() {
+- LinkObject element;
+- boolean gotLock;
+- long time = 0;
+-
+- if (!enabled) {
+- if (log.isInfoEnabled())
+- log.info("FastQueue.remove: queue disabled, remove aborted");
+- return null;
+- }
+-
+- if (timeWait) {
+- time = System.currentTimeMillis();
+- }
+- gotLock = lock.lockRemove();
+- try {
+-
+- if (!gotLock) {
+- if (enabled) {
+- if (log.isInfoEnabled())
+- log.info("FastQueue.remove: Remove aborted although queue
enabled");
+- } else {
+- if (log.isInfoEnabled())
+- log.info("FastQueue.remove: queue disabled, remove
aborted");
+- }
+- return null;
+- }
+-
+- if (log.isTraceEnabled()) {
+- log.trace("FastQueue.remove: remove starting with size " +
size);
+- }
+- if (checkLock) {
+- if (inRemove)
+- log.warn("FastQueue.remove: Detected other remove");
+- inRemove = true;
+- if (inMutex)
+- log.warn("FastQueue.remove: Detected other mutex in
remove");
+- inMutex = true;
+- }
+-
+- element = first;
+-
+- first = last = null;
+- size = 0;
+-
+- if (checkLock) {
+- if (!inMutex)
+- log.warn("FastQueue.remove: Cancelled by other mutex in
remove");
+- inMutex = false;
+- if (!inRemove)
+- log.warn("FastQueue.remove: Cancelled by other remove");
+- inRemove = false;
+- }
+- if (log.isTraceEnabled()) {
+- log.trace("FastQueue.remove: remove ending with size " +
size);
+- }
+-
+- if (timeWait) {
+- time = System.currentTimeMillis();
+- }
+- } finally {
+- lock.unlockRemove();
+- }
+- return element;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/bio/util/LinkObject.java (working copy)
+@@ -1,108 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.transport.bio.util;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.ErrorHandler;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-
+-/**
+- * The class <b>LinkObject</b> implements an element
+- * for a linked list, consisting of a general
+- * data object and a pointer to the next element.
+- *
+- * @author Rainer Jung
+- * @author Peter Rossbach
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+-
+- */
+-
+-public class LinkObject {
+-
+- private ChannelMessage msg;
+- private LinkObject next;
+- private byte[] key ;
+- private Member[] destination;
+- private InterceptorPayload payload;
+-
+- /**
+- * Construct a new element from the data object.
+- * Sets the pointer to null.
+- *
+- * @param key The key
+- * @param payload The data object.
+- */
+- public LinkObject(ChannelMessage msg, Member[] destination, InterceptorPayload
payload) {
+- this.msg = msg;
+- this.next = null;
+- this.key = msg.getUniqueId();
+- this.payload = payload;
+- this.destination = destination;
+- }
+-
+- /**
+- * Set the next element.
+- * @param next The next element.
+- */
+- public void append(LinkObject next) {
+- this.next = next;
+- }
+-
+- /**
+- * Get the next element.
+- * @return The next element.
+- */
+- public LinkObject next() {
+- return next;
+- }
+-
+- public void setNext(LinkObject next) {
+- this.next = next;
+- }
+-
+- /**
+- * Get the data object from the element.
+- * @return The data object from the element.
+- */
+- public ChannelMessage data() {
+- return msg;
+- }
+-
+- /**
+- * Get the unique message id
+- * @return the unique message id
+- */
+- public byte[] getKey() {
+- return key;
+- }
+-
+- public ErrorHandler getHandler() {
+- return payload!=null?payload.getErrorHandler():null;
+- }
+-
+- public InterceptorPayload getPayload() {
+- return payload;
+- }
+-
+- public Member[] getDestination() {
+- return destination;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/bio/BioReceiver.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/BioReceiver.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/bio/BioReceiver.java (working copy)
+@@ -1,157 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport.bio;
+-
+-import java.io.IOException;
+-import java.net.ServerSocket;
+-import java.net.Socket;
+-
+-import org.apache.catalina.tribes.ChannelReceiver;
+-import org.apache.catalina.tribes.io.ListenCallback;
+-import org.apache.catalina.tribes.io.ObjectReader;
+-import org.apache.catalina.tribes.transport.ReceiverBase;
+-import org.apache.catalina.tribes.transport.RxTaskPool;
+-import org.apache.catalina.tribes.transport.AbstractRxTask;
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version $Revision:$ $Date:$
+- */
+-public class BioReceiver extends ReceiverBase implements Runnable, ChannelReceiver,
ListenCallback {
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(BioReceiver.class);
+-
+- protected ServerSocket serverSocket;
+-
+- public BioReceiver() {
+- }
+-
+- /**
+- *
+- * @throws IOException
+- * @todo Implement this org.apache.catalina.tribes.ChannelReceiver method
+- */
+- public void start() throws IOException {
+- super.start();
+- try {
+- setPool(new RxTaskPool(getMaxThreads(),getMinThreads(),this));
+- } catch (Exception x) {
+- log.fatal("ThreadPool can initilzed. Listener not started", x);
+- if ( x instanceof IOException ) throw (IOException)x;
+- else throw new IOException(x.getMessage());
+- }
+- try {
+- getBind();
+- bind();
+- Thread t = new Thread(this, "BioReceiver");
+- t.setDaemon(true);
+- t.start();
+- } catch (Exception x) {
+- log.fatal("Unable to start cluster receiver", x);
+- if ( x instanceof IOException ) throw (IOException)x;
+- else throw new IOException(x.getMessage());
+- }
+- }
+-
+- public AbstractRxTask createRxTask() {
+- return getReplicationThread();
+- }
+-
+- protected BioReplicationTask getReplicationThread() {
+- BioReplicationTask result = new BioReplicationTask(this);
+- result.setOptions(getWorkerThreadOptions());
+- result.setUseBufferPool(this.getUseBufferPool());
+- return result;
+- }
+-
+- /**
+- *
+- * @todo Implement this org.apache.catalina.tribes.ChannelReceiver method
+- */
+- public void stop() {
+- setListen(false);
+- try {
+- this.serverSocket.close();
+- }catch ( Exception x ) {}
+- super.stop();
+- }
+-
+-
+-
+-
+- protected void bind() throws IOException {
+- // allocate an unbound server socket channel
+- serverSocket = new ServerSocket();
+- // set the port the server channel will listen to
+- //serverSocket.bind(new InetSocketAddress(getBind(), getTcpListenPort()));
+- bind(serverSocket,getPort(),getAutoBind());
+- }
+-
+-
+-
+- public void run() {
+- try {
+- listen();
+- } catch (Exception x) {
+- log.error("Unable to run replication listener.", x);
+- }
+- }
+-
+- public void listen() throws Exception {
+- if (doListen()) {
+- log.warn("ServerSocket already started");
+- return;
+- }
+- setListen(true);
+-
+- while ( doListen() ) {
+- Socket socket = null;
+- if ( getTaskPool().available() < 1 ) {
+- if ( log.isWarnEnabled() )
+- log.warn("All BIO server replication threads are busy, unable
to handle more requests until a thread is freed up.");
+- }
+- BioReplicationTask task = (BioReplicationTask)getTaskPool().getRxTask();
+- if ( task == null ) continue; //should never happen
+- try {
+- socket = serverSocket.accept();
+- }catch ( Exception x ) {
+- if ( doListen() ) throw x;
+- }
+- if ( !doListen() ) {
+- task.setDoRun(false);
+- task.serviceSocket(null,null);
+- getExecutor().execute(task);
+- break; //regular shutdown
+- }
+- if ( socket == null ) continue;
+- socket.setReceiveBufferSize(getRxBufSize());
+- socket.setSendBufferSize(getTxBufSize());
+- socket.setTcpNoDelay(getTcpNoDelay());
+- socket.setKeepAlive(getSoKeepAlive());
+- socket.setOOBInline(getOoBInline());
+- socket.setReuseAddress(getSoReuseAddress());
+- socket.setSoLinger(getSoLingerOn(),getSoLingerTime());
+- socket.setTrafficClass(getSoTrafficClass());
+- socket.setSoTimeout(getTimeout());
+- ObjectReader reader = new ObjectReader(socket);
+- task.serviceSocket(socket,reader);
+- }//while
+- }
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java (revision
590752)
++++ java/org/apache/catalina/tribes/transport/bio/PooledMultiSender.java (working copy)
+@@ -1,72 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport.bio;
+-
+-import org.apache.catalina.tribes.transport.DataSender;
+-import org.apache.catalina.tribes.transport.PooledSender;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.transport.MultiPointSender;
+-import org.apache.catalina.tribes.ChannelMessage;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class PooledMultiSender extends PooledSender {
+-
+-
+- public PooledMultiSender() {
+- }
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg) throws
ChannelException {
+- MultiPointSender sender = null;
+- try {
+- sender = (MultiPointSender)getSender();
+- if (sender == null) {
+- ChannelException cx = new ChannelException("Unable to retrieve a
data sender, time out error.");
+- for (int i = 0; i < destination.length; i++)
cx.addFaultyMember(destination[i], new NullPointerException("Unable to retrieve a
sender from the sender pool"));
+- throw cx;
+- } else {
+- sender.sendMessage(destination, msg);
+- }
+- sender.keepalive();
+- }finally {
+- if ( sender != null ) returnSender(sender);
+- }
+- }
+-
+- /**
+- * getNewDataSender
+- *
+- * @return DataSender
+- * @todo Implement this org.apache.catalina.tribes.transport.PooledSender
+- * method
+- */
+- public DataSender getNewDataSender() {
+- MultipointBioSender sender = new MultipointBioSender();
+- sender.transferProperties(this,sender);
+- return sender;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/transport/DataSender.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/DataSender.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/DataSender.java (working copy)
+@@ -1,45 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.transport;
+-
+-import java.io.IOException;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public interface DataSender {
+- public void connect() throws IOException;
+- public void disconnect();
+- public boolean isConnected();
+- public void setRxBufSize(int size);
+- public void setTxBufSize(int size);
+- public boolean keepalive();
+- public void setTimeout(long timeout);
+- public void setKeepAliveCount(int maxRequests);
+- public void setKeepAliveTime(long keepAliveTimeInMs);
+- public int getRequestCount();
+- public long getConnectTime();
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/transport/Constants.java
+===================================================================
+--- java/org/apache/catalina/tribes/transport/Constants.java (revision 590752)
++++ java/org/apache/catalina/tribes/transport/Constants.java (working copy)
+@@ -1,43 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.tribes.transport;
+-
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-
+-/**
+- * Manifest constants for the
<code>org.apache.catalina.tribes.transport</code>
+- * package.
+- * @author Filip Hanik
+- * @author Peter Rossbach
+- * @version $Revision$ $Date$
+- */
+-
+-public class Constants {
+-
+- public static final String Package =
"org.apache.catalina.tribes.transport";
+-
+- /*
+- * Do not change any of these values!
+- */
+- public static final byte[] ACK_DATA = new byte[] {6, 2, 3};
+- public static final byte[] FAIL_ACK_DATA = new byte[] {11, 0, 5};
+- public static final byte[] ACK_COMMAND = XByteBuffer.createDataPackage(ACK_DATA);
+- public static final byte[] FAIL_ACK_COMMAND =
XByteBuffer.createDataPackage(FAIL_ACK_DATA);
+-
+-}
+Index:
java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java (working
copy)
+@@ -1,117 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.Member;
+-import java.util.ArrayList;
+-import org.apache.catalina.tribes.group.AbsoluteOrder;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.Channel;
+-
+-public class StaticMembershipInterceptor
+- extends ChannelInterceptorBase {
+- protected ArrayList members = new ArrayList();
+- protected Member localMember = null;
+-
+- public StaticMembershipInterceptor() {
+- super();
+- }
+-
+- public void addStaticMember(Member member) {
+- synchronized (members) {
+- if (!members.contains(member)) members.add(member);
+- }
+- }
+-
+- public void removeStaticMember(Member member) {
+- synchronized (members) {
+- if (members.contains(member)) members.remove(member);
+- }
+- }
+-
+- public void setLocalMember(Member member) {
+- this.localMember = member;
+- }
+-
+- /**
+- * has members
+- */
+- public boolean hasMembers() {
+- return super.hasMembers() || (members.size()>0);
+- }
+-
+- /**
+- * Get all current cluster members
+- * @return all members or empty array
+- */
+- public Member[] getMembers() {
+- if ( members.size() == 0 ) return super.getMembers();
+- else {
+- synchronized (members) {
+- Member[] others = super.getMembers();
+- Member[] result = new Member[members.size() + others.length];
+- for (int i = 0; i < others.length; i++) result[i] = others[i];
+- for (int i = 0; i < members.size(); i++) result[i + others.length] =
(Member) members.get(i);
+- AbsoluteOrder.absoluteOrder(result);
+- return result;
+- }//sync
+- }//end if
+- }
+-
+- /**
+- *
+- * @param mbr Member
+- * @return Member
+- */
+- public Member getMember(Member mbr) {
+- if ( members.contains(mbr) ) return (Member)members.get(members.indexOf(mbr));
+- else return super.getMember(mbr);
+- }
+-
+- /**
+- * Return the member that represents this node.
+- *
+- * @return Member
+- */
+- public Member getLocalMember(boolean incAlive) {
+- if (this.localMember != null ) return localMember;
+- else return super.getLocalMember(incAlive);
+- }
+-
+- /**
+- * Send notifications upwards
+- * @param svc int
+- * @throws ChannelException
+- */
+- public void start(int svc) throws ChannelException {
+- if ( (Channel.SND_RX_SEQ&svc)==Channel.SND_RX_SEQ )
super.start(Channel.SND_RX_SEQ);
+- if ( (Channel.SND_TX_SEQ&svc)==Channel.SND_TX_SEQ )
super.start(Channel.SND_TX_SEQ);
+- final Member[] mbrs = (Member[])members.toArray(new Member[members.size()]);
+- final ChannelInterceptorBase base = this;
+- Thread t = new Thread() {
+- public void run() {
+- for (int i=0; i<mbrs.length; i++ ) {
+- base.memberAdded(mbrs[i]);
+- }
+- }
+- };
+- t.start();
+- super.start(svc & (~Channel.SND_RX_SEQ) & (~Channel.SND_TX_SEQ));
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java (working
copy)
+@@ -1,148 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.util.HashMap;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.util.UUIDGenerator;
+-import org.apache.catalina.tribes.util.Arrays;
+-import org.apache.catalina.tribes.UniqueId;
+-import java.util.Map;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class TwoPhaseCommitInterceptor extends ChannelInterceptorBase {
+-
+- public static final byte[] START_DATA = new byte[] {113, 1, -58, 2, -34, -60, 75,
-78, -101, -12, 32, -29, 32, 111, -40, 4};
+- public static final byte[] END_DATA = new byte[] {54, -13, 90, 110, 47, -31, 75,
-24, -81, -29, 36, 52, -58, 77, -110, 56};
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(TwoPhaseCommitInterceptor.class);
+-
+- protected HashMap messages = new HashMap();
+- protected long expire = 1000 * 60; //one minute expiration
+- protected boolean deepclone = true;
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws
+- ChannelException {
+- //todo, optimize, if destination.length==1, then we can do
+- //msg.setOptions(msg.getOptions() & (~getOptionFlag())
+- //and just send one message
+- if (okToProcess(msg.getOptions()) ) {
+- super.sendMessage(destination, msg, null);
+- ChannelMessage confirmation = null;
+- if ( deepclone ) confirmation = (ChannelMessage)msg.deepclone();
+- else confirmation = (ChannelMessage)msg.clone();
+- confirmation.getMessage().reset();
+- UUIDGenerator.randomUUID(false,confirmation.getUniqueId(),0);
+- confirmation.getMessage().append(START_DATA,0,START_DATA.length);
+-
confirmation.getMessage().append(msg.getUniqueId(),0,msg.getUniqueId().length);
+- confirmation.getMessage().append(END_DATA,0,END_DATA.length);
+- super.sendMessage(destination,confirmation,payload);
+- } else {
+- //turn off two phase commit
+- //this wont work if the interceptor has 0 as a flag
+- //since there is no flag to turn off
+- //msg.setOptions(msg.getOptions() & (~getOptionFlag()));
+- super.sendMessage(destination, msg, payload);
+- }
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- if (okToProcess(msg.getOptions())) {
+- if ( msg.getMessage().getLength() ==
(START_DATA.length+msg.getUniqueId().length+END_DATA.length) &&
+-
Arrays.contains(msg.getMessage().getBytesDirect(),0,START_DATA,0,START_DATA.length)
&&
+-
Arrays.contains(msg.getMessage().getBytesDirect(),START_DATA.length+msg.getUniqueId().length,END_DATA,0,END_DATA.length)
) {
+- UniqueId id = new
UniqueId(msg.getMessage().getBytesDirect(),START_DATA.length,msg.getUniqueId().length);
+- MapEntry original = (MapEntry)messages.get(id);
+- if ( original != null ) {
+- super.messageReceived(original.msg);
+- messages.remove(id);
+- } else log.warn("Received a confirmation, but original message is
missing. Id:"+Arrays.toString(id.getBytes()));
+- } else {
+- UniqueId id = new UniqueId(msg.getUniqueId());
+- MapEntry entry = new
MapEntry((ChannelMessage)msg.deepclone(),id,System.currentTimeMillis());
+- messages.put(id,entry);
+- }
+- } else {
+- super.messageReceived(msg);
+- }
+- }
+-
+- public boolean getDeepclone() {
+- return deepclone;
+- }
+-
+- public long getExpire() {
+- return expire;
+- }
+-
+- public void setDeepclone(boolean deepclone) {
+- this.deepclone = deepclone;
+- }
+-
+- public void setExpire(long expire) {
+- this.expire = expire;
+- }
+-
+- public void heartbeat() {
+- try {
+- long now = System.currentTimeMillis();
+- Map.Entry[] entries = (Map.Entry[])messages.entrySet().toArray(new
Map.Entry[messages.size()]);
+- for (int i=0; i<entries.length; i++ ) {
+- MapEntry entry = (MapEntry)entries[i].getValue();
+- if ( entry.expired(now,expire) ) {
+- if(log.isInfoEnabled())
+- log.info("Message ["+entry.id+"] has expired.
Removing.");
+- messages.remove(entry.id);
+- }//end if
+- }
+- } catch ( Exception x ) {
+- log.warn("Unable to perform heartbeat on the TwoPhaseCommit
interceptor.",x);
+- } finally {
+- super.heartbeat();
+- }
+- }
+-
+- public static class MapEntry {
+- public ChannelMessage msg;
+- public UniqueId id;
+- public long timestamp;
+-
+- public MapEntry(ChannelMessage msg, UniqueId id, long timestamp) {
+- this.msg = msg;
+- this.id = id;
+- this.timestamp = timestamp;
+- }
+- public boolean expired(long now, long expiration) {
+- return (now - timestamp ) > expiration;
+- }
+-
+- }
+-
+-}
+\ No newline at end of file
+Index:
java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java (working
copy)
+@@ -1,202 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.transport.bio.util.FastQueue;
+-import org.apache.catalina.tribes.transport.bio.util.LinkObject;
+-import org.apache.catalina.tribes.UniqueId;
+-
+-/**
+- *
+- * The message dispatcher is a way to enable asynchronous communication
+- * through a channel. The dispatcher will look for the
<code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code>
+- * flag to be set, if it is, it will queue the message for delivery and immediately
return to the sender.
+- *
+- *
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class MessageDispatchInterceptor extends ChannelInterceptorBase implements
Runnable {
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(MessageDispatchInterceptor.class);
+-
+- protected long maxQueueSize = 1024*1024*64; //64MB
+- protected FastQueue queue = new FastQueue();
+- protected boolean run = false;
+- protected Thread msgDispatchThread = null;
+- protected long currentSize = 0;
+- protected boolean useDeepClone = true;
+- protected boolean alwaysSend = true;
+-
+- public MessageDispatchInterceptor() {
+- setOptionFlag(Channel.SEND_OPTIONS_ASYNCHRONOUS);
+- }
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- boolean async = (msg.getOptions() & Channel.SEND_OPTIONS_ASYNCHRONOUS) ==
Channel.SEND_OPTIONS_ASYNCHRONOUS;
+- if ( async && run ) {
+- if ( (getCurrentSize()+msg.getMessage().getLength()) > maxQueueSize ) {
+- if ( alwaysSend ) {
+- super.sendMessage(destination,msg,payload);
+- return;
+- } else {
+- throw new ChannelException("Asynchronous queue is full, reached
its limit of " + maxQueueSize +" bytes, current:" + getCurrentSize() +
" bytes.");
+- }//end if
+- }//end if
+- //add to queue
+- if ( useDeepClone ) msg = (ChannelMessage)msg.deepclone();
+- if (!addToQueue(msg, destination, payload) ) {
+- throw new ChannelException("Unable to add the message to the async
queue, queue bug?");
+- }
+- addAndGetCurrentSize(msg.getMessage().getLength());
+- } else {
+- super.sendMessage(destination, msg, payload);
+- }
+- }
+-
+- public boolean addToQueue(ChannelMessage msg, Member[] destination,
InterceptorPayload payload) {
+- return queue.add(msg,destination,payload);
+- }
+-
+- public LinkObject removeFromQueue() {
+- return queue.remove();
+- }
+-
+- public void startQueue() {
+- msgDispatchThread = new Thread(this);
+-
msgDispatchThread.setName("MessageDispatchInterceptor.MessageDispatchThread");
+- msgDispatchThread.setDaemon(true);
+- msgDispatchThread.setPriority(Thread.MAX_PRIORITY);
+- queue.setEnabled(true);
+- run = true;
+- msgDispatchThread.start();
+- }
+-
+- public void stopQueue() {
+- run = false;
+- msgDispatchThread.interrupt();
+- queue.setEnabled(false);
+- setAndGetCurrentSize(0);
+- }
+-
+-
+- public void setOptionFlag(int flag) {
+- if ( flag != Channel.SEND_OPTIONS_ASYNCHRONOUS ) log.warn("Warning, you are
overriding the asynchronous option flag, this will disable the
Channel.SEND_OPTIONS_ASYNCHRONOUS that other apps might use.");
+- super.setOptionFlag(flag);
+- }
+-
+- public void setMaxQueueSize(long maxQueueSize) {
+- this.maxQueueSize = maxQueueSize;
+- }
+-
+- public void setUseDeepClone(boolean useDeepClone) {
+- this.useDeepClone = useDeepClone;
+- }
+-
+- public long getMaxQueueSize() {
+- return maxQueueSize;
+- }
+-
+- public boolean getUseDeepClone() {
+- return useDeepClone;
+- }
+-
+- public long getCurrentSize() {
+- return currentSize;
+- }
+-
+- public synchronized long addAndGetCurrentSize(long inc) {
+- currentSize += inc;
+- return currentSize;
+- }
+-
+- public synchronized long setAndGetCurrentSize(long value) {
+- currentSize = value;
+- return value;
+- }
+-
+- public void start(int svc) throws ChannelException {
+- //start the thread
+- if (!run ) {
+- synchronized (this) {
+- if ( !run && ((svc &
Channel.SND_TX_SEQ)==Channel.SND_TX_SEQ) ) {//only start with the sender
+- startQueue();
+- }//end if
+- }//sync
+- }//end if
+- super.start(svc);
+- }
+-
+-
+- public void stop(int svc) throws ChannelException {
+- //stop the thread
+- if ( run ) {
+- synchronized (this) {
+- if ( run && ((svc &
Channel.SND_TX_SEQ)==Channel.SND_TX_SEQ)) {
+- stopQueue();
+- }//end if
+- }//sync
+- }//end if
+-
+- super.stop(svc);
+- }
+-
+- public void run() {
+- while ( run ) {
+- LinkObject link = removeFromQueue();
+- if ( link == null ) continue; //should not happen unless we exceed wait
time
+- while ( link != null && run ) {
+- link = sendAsyncData(link);
+- }//while
+- }//while
+- }//run
+-
+- protected LinkObject sendAsyncData(LinkObject link) {
+- ChannelMessage msg = link.data();
+- Member[] destination = link.getDestination();
+- try {
+- super.sendMessage(destination,msg,null);
+- try {
+- if ( link.getHandler() != null ) link.getHandler().handleCompletion(new
UniqueId(msg.getUniqueId()));
+- } catch ( Exception ex ) {
+- log.error("Unable to report back completed message.",ex);
+- }
+- } catch ( Exception x ) {
+- ChannelException cx = null;
+- if ( x instanceof ChannelException ) cx = (ChannelException)x;
+- else cx = new ChannelException(x);
+- if ( log.isDebugEnabled() ) log.debug("Error while processing async
message.",x);
+- try {
+- if (link.getHandler() != null) link.getHandler().handleError(cx, new
UniqueId(msg.getUniqueId()));
+- } catch ( Exception ex ) {
+- log.error("Unable to report back error message.",ex);
+- }
+- } finally {
+- addAndGetCurrentSize(-msg.getMessage().getLength());
+- link = link.next();
+- }//try
+- return link;
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java (revision
590752)
++++ java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java (working
copy)
+@@ -1,179 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.lang.ref.WeakReference;
+-import java.util.Arrays;
+-import java.util.concurrent.atomic.AtomicInteger;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelInterceptor;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.io.ChannelData;
+-
+-/**
+- *
+- * Sends a ping to all members.
+- * Configure this interceptor with the TcpFailureDetector below it,
+- * and the TcpFailureDetector will act as the membership guide.
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public class TcpPingInterceptor extends ChannelInterceptorBase {
+-
+- protected static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog(TcpPingInterceptor.class);
+-
+- protected static byte[] TCP_PING_DATA = new byte[] {
+- 79, -89, 115, 72, 121, -33, 67, -55, -97, 111, -119, -128, -95, 91, 7, 20,
+- 125, -39, 82, 91, -21, -33, 67, -102, -73, 126, -66, -113, -127, 103, 30, -74,
+- 55, 21, -66, -121, 69, 33, 76, -88, -65, 10, 77, 19, 83, 56, 21, 50,
+- 85, -10, -108, -73, 58, -33, 33, 120, -111, 4, 125, -41, 114, -124, -64, -43};
+-
+- protected long interval = 1000; //1 second
+-
+- protected boolean useThread = false;
+- protected boolean staticOnly = false;
+- protected boolean running = true;
+- protected PingThread thread = null;
+- protected static AtomicInteger cnt = new AtomicInteger(0);
+-
+- WeakReference<TcpFailureDetector> failureDetector = null;
+- WeakReference<StaticMembershipInterceptor> staticMembers = null;
+-
+- public synchronized void start(int svc) throws ChannelException {
+- super.start(svc);
+- running = true;
+- if ( thread == null ) {
+- thread = new PingThread();
+- thread.setDaemon(true);
+-
thread.setName("TcpPingInterceptor.PingThread-"+cnt.addAndGet(1));
+- thread.start();
+- }
+-
+- //acquire the interceptors to invoke on send ping events
+- ChannelInterceptor next = getNext();
+- while ( next != null ) {
+- if ( next instanceof TcpFailureDetector )
+- failureDetector = new
WeakReference<TcpFailureDetector>((TcpFailureDetector)next);
+- if ( next instanceof StaticMembershipInterceptor )
+- staticMembers = new
WeakReference<StaticMembershipInterceptor>((StaticMembershipInterceptor)next);
+- next = next.getNext();
+- }
+-
+- }
+-
+- public void stop(int svc) throws ChannelException {
+- running = false;
+- if ( thread != null ) thread.interrupt();
+- thread = null;
+- super.stop(svc);
+- }
+-
+- public void heartbeat() {
+- super.heartbeat();
+- if (!getUseThread()) sendPing();
+- }
+-
+- public long getInterval() {
+- return interval;
+- }
+-
+- public void setInterval(long interval) {
+- this.interval = interval;
+- }
+-
+- public void setUseThread(boolean useThread) {
+- this.useThread = useThread;
+- }
+-
+- public void setStaticOnly(boolean staticOnly) {
+- this.staticOnly = staticOnly;
+- }
+-
+- public boolean getUseThread() {
+- return useThread;
+- }
+-
+- public boolean getStaticOnly() {
+- return staticOnly;
+- }
+-
+- protected void sendPing() {
+- if (failureDetector.get()!=null) {
+- //we have a reference to the failure detector
+- //piggy back on that dude
+- failureDetector.get().checkMembers(true);
+- }else {
+- if (staticOnly && staticMembers.get()!=null) {
+- sendPingMessage(staticMembers.get().getMembers());
+- } else {
+- sendPingMessage(getMembers());
+- }
+- }
+- }
+-
+- protected void sendPingMessage(Member[] members) {
+- if ( members == null || members.length == 0 ) return;
+- ChannelData data = new ChannelData(true);//generates a unique Id
+- data.setAddress(getLocalMember(false));
+- data.setTimestamp(System.currentTimeMillis());
+- data.setOptions(getOptionFlag());
+- try {
+- super.sendMessage(members, data, null);
+- }catch (ChannelException x) {
+- log.warn("Unable to send TCP ping.",x);
+- }
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- //catch incoming
+- boolean process = true;
+- if ( okToProcess(msg.getOptions()) ) {
+- //check to see if it is a ping message, if so, process = false
+- process = ( (msg.getMessage().getLength() != TCP_PING_DATA.length) ||
+- (!Arrays.equals(TCP_PING_DATA,msg.getMessage().getBytes()) ) );
+- }//end if
+-
+- //ignore the message, it doesnt have the flag set
+- if ( process ) super.messageReceived(msg);
+- else if ( log.isDebugEnabled() ) log.debug("Received a TCP ping
packet:"+msg);
+- }//messageReceived
+-
+- protected class PingThread extends Thread {
+- public void run() {
+- while (running) {
+- try {
+- sleep(interval);
+- sendPing();
+- }catch ( InterruptedException ix ) {
+- interrupted();
+- }catch ( Exception x ) {
+- log.warn("Unable to send ping from TCP ping thread.",x);
+- }
+- }
+- }
+- }
+-
+-
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java (working
copy)
+@@ -1,839 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.util.concurrent.atomic.AtomicBoolean;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelInterceptor;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.UniqueId;
+-import org.apache.catalina.tribes.group.AbsoluteOrder;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import org.apache.catalina.tribes.membership.Membership;
+-import org.apache.catalina.tribes.util.Arrays;
+-import org.apache.catalina.tribes.util.UUIDGenerator;
+-
+-/**
+- * <p>Title: Auto merging leader election algorithm</p>
+- *
+- * <p>Description: Implementation of a simple coordinator algorithm that not only
selects a coordinator,
+- * it also merges groups automatically when members are discovered that werent part
of the
+- * </p>
+- * <p>This algorithm is non blocking meaning it allows for transactions while the
coordination phase is going on
+- * </p>
+- * <p>This implementation is based on a home brewed algorithm that uses the
AbsoluteOrder of a membership
+- * to pass a token ring of the current membership.<br>
+- * This is not the same as just using AbsoluteOrder! Consider the following
scenario:<br>
+- * Nodes, A,B,C,D,E on a network, in that priority. AbsoluteOrder will only work if all
+- * nodes are receiving pings from all the other nodes.
+- * meaning, that node{i} receives pings from node{all}-node{i}<br>
+- * but the following could happen if a multicast problem occurs.
+- * A has members {B,C,D}<br>
+- * B has members {A,C}<br>
+- * C has members {D,E}<br>
+- * D has members {A,B,C,E}<br>
+- * E has members {A,C,D}<br>
+- * Because the default Tribes membership implementation, relies on the multicast packets
to
+- * arrive at all nodes correctly, there is nothing guaranteeing that it will.<br>
+- * <br>
+- * To best explain how this algorithm works, lets take the above example:
+- * For simplicity we assume that a send operation is O(1) for all nodes, although this
algorithm will work
+- * where messages overlap, as they all depend on absolute order<br>
+- * Scenario 1: A,B,C,D,E all come online at the same time
+- * Eval phase, A thinks of itself as leader, B thinks of A as leader,
+- * C thinks of itself as leader, D,E think of A as leader<br>
+- * Token phase:<br>
+- * (1) A sends out a message X{A-ldr, A-src, mbrs-A,B,C,D} to B where X is the id for
the message(and the view)<br>
+- * (1) C sends out a message Y{C-ldr, C-src, mbrs-C,D,E} to D where Y is the id for the
message(and the view)<br>
+- * (2) B receives X{A-ldr, A-src, mbrs-A,B,C,D}, sends X{A-ldr, A-src, mbrs-A,B,C,D} to
C <br>
+- * (2) D receives Y{C-ldr, C-src, mbrs-C,D,E} D is aware of A,B, sends Y{A-ldr, C-src,
mbrs-A,B,C,D,E} to E<br>
+- * (3) C receives X{A-ldr, A-src, mbrs-A,B,C,D}, sends X{A-ldr, A-src, mbrs-A,B,C,D,E}
to D<br>
+- * (3) E receives Y{A-ldr, C-src, mbrs-A,B,C,D,E} sends Y{A-ldr, C-src, mbrs-A,B,C,D,E}
to A<br>
+- * (4) D receives X{A-ldr, A-src, mbrs-A,B,C,D,E} sends sends X{A-ldr, A-src,
mbrs-A,B,C,D,E} to A<br>
+- * (4) A receives Y{A-ldr, C-src, mbrs-A,B,C,D,E}, holds the message, add E to its list
of members<br>
+- * (5) A receives X{A-ldr, A-src, mbrs-A,B,C,D,E} <br>
+- * At this point, the state looks like<br>
+- * A - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>
+- * B - {A-ldr, mbrs-A,B,C,D, id=X}<br>
+- * C - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>
+- * D - {A-ldr, mbrs-A,B,C,D,E, id=X}<br>
+- * E - {A-ldr, mbrs-A,B,C,D,E, id=Y}<br>
+- * <br>
+- * A message doesn't stop until it reaches its original sender, unless its dropped
by a higher leader.
+- * As you can see, E still thinks the viewId=Y, which is not correct. But at this point
we have
+- * arrived at the same membership and all nodes are informed of each other.<br>
+- * To synchronize the rest we simply perform the following check at A when A receives
X:<br>
+- * Original X{A-ldr, A-src, mbrs-A,B,C,D} == Arrived X{A-ldr, A-src,
mbrs-A,B,C,D,E}<br>
+- * Since the condition is false, A, will resend the token, and A sends X{A-ldr, A-src,
mbrs-A,B,C,D,E} to B
+- * When A receives X again, the token is complete. <br>
+- * Optionally, A can send a message X{A-ldr, A-src, mbrs-A,B,C,D,E confirmed} to
A,B,C,D,E who then
+- * install and accept the view.
+- * </p>
+- * <p>
+- * Lets assume that C1 arrives, C1 has lower priority than C, but higher priority than
D.<br>
+- * Lets also assume that C1 sees the following view {B,D,E}<br>
+- * C1 waits for a token to arrive. When the token arrives, the same scenario as above
will happen.<br>
+- * In the scenario where C1 sees {D,E} and A,B,C can not see C1, no token will ever
arrive.<br>
+- * In this case, C1 sends a Z{C1-ldr, C1-src, mbrs-C1,D,E} to D<br>
+- * D receives Z{C1-ldr, C1-src, mbrs-C1,D,E} and sends Z{A-ldr, C1-src,
mbrs-A,B,C,C1,D,E} to E<br>
+- * E receives Z{A-ldr, C1-src, mbrs-A,B,C,C1,D,E} and sends it to A<br>
+- * A sends Z{A-ldr, A-src, mbrs-A,B,C,C1,D,E} to B and the chain continues until A
receives the token again.
+- * At that time A optionally sends out Z{A-ldr, A-src, mbrs-A,B,C,C1,D,E, confirmed} to
A,B,C,C1,D,E
+- * </p>
+- * <p>To ensure that the view gets implemented at all nodes at the same time,
+- * A will send out a VIEW_CONF message, this is the 'confirmed' message that
is optional above.
+- * <p>Ideally, the interceptor below this one would be the TcpFailureDetector to
ensure correct memberships</p>
+- *
+- * <p>The example above, of course can be simplified with a finite
statemachine:<br>
+- * But I suck at writing state machines, my head gets all confused. One day I will
document this algorithm though.<br>
+- * Maybe I'll do a state diagram :)
+- * </p>
+- * <h2>State Diagrams</h2>
+- * <a
href="http://people.apache.org/~fhanik/tribes/docs/leader-election-i...
an election</a><br><br>
+- * <a
href="http://people.apache.org/~fhanik/tribes/docs/leader-election-m...
an election message</a><br><br>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- *
+- *
+- *
+- */
+-public class NonBlockingCoordinator extends ChannelInterceptorBase {
+-
+- /**
+- * header for a coordination message
+- */
+- protected static final byte[] COORD_HEADER = new byte[] {-86, 38, -34, -29, -98, 90,
65, 63, -81, -122, -6, -110, 99, -54, 13, 63};
+- /**
+- * Coordination request
+- */
+- protected static final byte[] COORD_REQUEST = new byte[] {104, -95, -92, -42, 114,
-36, 71, -19, -79, 20, 122, 101, -1, -48, -49, 30};
+- /**
+- * Coordination confirmation, for blocking installations
+- */
+- protected static final byte[] COORD_CONF = new byte[] {67, 88, 107, -86, 69, 23, 76,
-70, -91, -23, -87, -25, -125, 86, 75, 20};
+-
+- /**
+- * Alive message
+- */
+- protected static final byte[] COORD_ALIVE = new byte[] {79, -121, -25, -15, -59, 5,
64, 94, -77, 113, -119, -88, 52, 114, -56, -46,
+- -18, 102, 10, 34, -127, -9,
71, 115, -70, 72, -101, 88, 72, -124, 127, 111,
+- 74, 76, -116, 50, 111, 103,
65, 3, -77, 51, -35, 0, 119, 117, 9, -26,
+- 119, 50, -75, -105, -102,
36, 79, 37, -68, -84, -123, 15, -22, -109, 106, -55};
+- /**
+- * Time to wait for coordination timeout
+- */
+- protected long waitForCoordMsgTimeout = 15000;
+- /**
+- * Our current view
+- */
+- protected Membership view = null;
+- /**
+- * Out current viewId
+- */
+- protected UniqueId viewId;
+-
+- /**
+- * Our nonblocking membership
+- */
+- protected Membership membership = null;
+-
+- /**
+- * indicates that we are running an election
+- * and this is the one we are running
+- */
+- protected UniqueId suggestedviewId;
+- protected Membership suggestedView;
+-
+- protected boolean started = false;
+- protected final int startsvc = 0xFFFF;
+-
+- protected Object electionMutex = new Object();
+-
+- protected AtomicBoolean coordMsgReceived = new AtomicBoolean(false);
+-
+- public NonBlockingCoordinator() {
+- super();
+- }
+-
+-//============================================================================================================
+-// COORDINATION HANDLING
+-//============================================================================================================
+-
+- public void startElection(boolean force) throws ChannelException {
+- synchronized (electionMutex) {
+- MemberImpl local = (MemberImpl)getLocalMember(false);
+- MemberImpl[] others = (MemberImpl[])membership.getMembers();
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_START_ELECT,this,"Election initated"));
+- if ( others.length == 0 ) {
+- this.viewId = new UniqueId(UUIDGenerator.randomUUID(false));
+- this.view = new Membership(local,AbsoluteOrder.comp, true);
+-
this.handleViewConf(this.createElectionMsg(local,others,local),local,view);
+- return; //the only member, no need for an election
+- }
+- if ( suggestedviewId != null ) {
+-
+- if ( view != null &&
Arrays.diff(view,suggestedView,local).length == 0 &&
Arrays.diff(suggestedView,view,local).length == 0) {
+- suggestedviewId = null;
+- suggestedView = null;
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned,
running election matches view"));
+- } else {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned,
election running"));
+- }
+- return; //election already running, I'm not allowed to have two of
them
+- }
+- if ( view != null && Arrays.diff(view,membership,local).length == 0
&& Arrays.diff(membership,view,local).length == 0) {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned,
view matches membership"));
+- return; //already have this view installed
+- }
+- int prio = AbsoluteOrder.comp.compare(local,others[0]);
+- MemberImpl leader = ( prio < 0 )?local:others[0];//am I the leader in my
view?
+- if ( local.equals(leader) || force ) {
+- CoordinationMessage msg = createElectionMsg(local, others, leader);
+- suggestedviewId = msg.getId();
+- suggestedView = new Membership(local,AbsoluteOrder.comp,true);
+- Arrays.fill(suggestedView,msg.getMembers());
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_PROCESS_ELECT,this,"Election, sending
request"));
+- sendElectionMsg(local,others[0],msg);
+- } else {
+- try {
+- coordMsgReceived.set(false);
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG,this,"Election, waiting for
request"));
+- electionMutex.wait(waitForCoordMsgTimeout);
+- }catch ( InterruptedException x ) {
+- Thread.currentThread().interrupted();
+- }
+- if ( suggestedviewId == null && (!coordMsgReceived.get())) {
+- //no message arrived, send the coord msg
+-// fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG,this,"Election, waiting timed
out."));
+-// startElection(true);
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned,
waiting timed out."));
+- } else {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_ELECT_ABANDONED,this,"Election abandoned,
received a message"));
+- }
+- }//end if
+-
+- }
+- }
+-
+- private CoordinationMessage createElectionMsg(MemberImpl local, MemberImpl[] others,
MemberImpl leader) {
+- Membership m = new Membership(local,AbsoluteOrder.comp,true);
+- Arrays.fill(m,others);
+- MemberImpl[] mbrs = m.getMembers();
+- m.reset();
+- CoordinationMessage msg = new CoordinationMessage(leader, local, mbrs,new
UniqueId(UUIDGenerator.randomUUID(true)), this.COORD_REQUEST);
+- return msg;
+- }
+-
+- protected void sendElectionMsg(MemberImpl local, MemberImpl next,
CoordinationMessage msg) throws ChannelException {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_SEND_MSG,this,"Sending election message
to("+next.getName()+")"));
+- super.sendMessage(new Member[] {next}, createData(msg, local), null);
+- }
+-
+- protected void sendElectionMsgToNextInline(MemberImpl local, CoordinationMessage
msg) throws ChannelException {
+- int next = Arrays.nextIndex(local,msg.getMembers());
+- int current = next;
+- msg.leader = msg.getMembers()[0];
+- boolean sent = false;
+- while ( !sent && current >= 0 ) {
+- try {
+- sendElectionMsg(local, (MemberImpl) msg.getMembers()[current], msg);
+- sent = true;
+- }catch ( ChannelException x ) {
+- log.warn("Unable to send election message
to:"+msg.getMembers()[current]);
+- current = Arrays.nextIndex(msg.getMembers()[current],msg.getMembers());
+- if ( current == next ) throw x;
+- }
+- }
+- }
+-
+- public Member getNextInLine(MemberImpl local, MemberImpl[] others) {
+- MemberImpl result = null;
+- for ( int i=0; i<others.length; i++ ) {
+-
+- }
+- return result;
+- }
+-
+- public ChannelData createData(CoordinationMessage msg, MemberImpl local) {
+- msg.write();
+- ChannelData data = new ChannelData(true);
+- data.setAddress(local);
+- data.setMessage(msg.getBuffer());
+- data.setOptions(Channel.SEND_OPTIONS_USE_ACK);
+- data.setTimestamp(System.currentTimeMillis());
+- return data;
+- }
+-
+- protected void viewChange(UniqueId viewId, Member[] view) {
+- //invoke any listeners
+- }
+-
+- protected boolean alive(Member mbr) {
+- return TcpFailureDetector.memberAlive(mbr,
+- COORD_ALIVE,
+- false,
+- false,
+- waitForCoordMsgTimeout,
+- waitForCoordMsgTimeout,
+- getOptionFlag());
+- }
+-
+- protected Membership mergeOnArrive(CoordinationMessage msg, Member sender) {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_PRE_MERGE,this,"Pre merge"));
+- MemberImpl local = (MemberImpl)getLocalMember(false);
+- Membership merged = new Membership(local,AbsoluteOrder.comp,true);
+- Arrays.fill(merged,msg.getMembers());
+- Arrays.fill(merged,getMembers());
+- Member[] diff = Arrays.diff(merged,membership,local);
+- for ( int i=0; i<diff.length; i++ ) {
+- if (!alive(diff[i])) merged.removeMember((MemberImpl)diff[i]);
+- else memberAdded(diff[i],false);
+- }
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_POST_MERGE,this,"Post merge"));
+- return merged;
+- }
+-
+- protected void processCoordMessage(CoordinationMessage msg, Member sender) throws
ChannelException {
+- if ( !coordMsgReceived.get() ) {
+- coordMsgReceived.set(true);
+- synchronized (electionMutex) { electionMutex.notifyAll();}
+- }
+- msg.timestamp = System.currentTimeMillis();
+- Membership merged = mergeOnArrive(msg, sender);
+- if (isViewConf(msg)) handleViewConf(msg, sender, merged);
+- else handleToken(msg, sender, merged);
+- ClassLoader loader;
+-
+- }
+-
+- protected void handleToken(CoordinationMessage msg, Member sender,Membership merged)
throws ChannelException {
+- MemberImpl local = (MemberImpl)getLocalMember(false);
+- if ( local.equals(msg.getSource()) ) {
+- //my message msg.src=local
+- handleMyToken(local, msg, sender,merged);
+- } else {
+- handleOtherToken(local, msg, sender,merged);
+- }
+- }
+-
+- protected void handleMyToken(MemberImpl local, CoordinationMessage msg, Member
sender,Membership merged) throws ChannelException {
+- if ( local.equals(msg.getLeader()) ) {
+- //no leadership change
+- if ( Arrays.sameMembers(msg.getMembers(),merged.getMembers()) ) {
+- msg.type = COORD_CONF;
+-
super.sendMessage(Arrays.remove(msg.getMembers(),local),createData(msg,local),null);
+- handleViewConf(msg,local,merged);
+- } else {
+- //membership change
+- suggestedView = new Membership(local,AbsoluteOrder.comp,true);
+- suggestedviewId = msg.getId();
+- Arrays.fill(suggestedView,merged.getMembers());
+- msg.view = (MemberImpl[])merged.getMembers();
+- sendElectionMsgToNextInline(local,msg);
+- }
+- } else {
+- //leadership change
+- suggestedView = null;
+- suggestedviewId = null;
+- msg.view = (MemberImpl[])merged.getMembers();
+- sendElectionMsgToNextInline(local,msg);
+- }
+- }
+-
+- protected void handleOtherToken(MemberImpl local, CoordinationMessage msg, Member
sender,Membership merged) throws ChannelException {
+- if ( local.equals(msg.getLeader()) ) {
+- //I am the new leader
+- //startElection(false);
+- } else {
+- msg.view = (MemberImpl[])merged.getMembers();
+- sendElectionMsgToNextInline(local,msg);
+- }
+- }
+-
+- protected void handleViewConf(CoordinationMessage msg, Member sender,Membership
merged) throws ChannelException {
+- if ( viewId != null && msg.getId().equals(viewId) ) return;//we already
have this view
+- view = new
Membership((MemberImpl)getLocalMember(false),AbsoluteOrder.comp,true);
+- Arrays.fill(view,msg.getMembers());
+- viewId = msg.getId();
+-
+- if ( viewId.equals(suggestedviewId) ) {
+- suggestedView = null;
+- suggestedviewId = null;
+- }
+-
+- if (suggestedView != null &&
AbsoluteOrder.comp.compare(suggestedView.getMembers()[0],merged.getMembers()[0])<0 ) {
+- suggestedView = null;
+- suggestedviewId = null;
+- }
+-
+- viewChange(viewId,view.getMembers());
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_CONF_RX,this,"Accepted View"));
+-
+- if ( suggestedviewId == null &&
hasHigherPriority(merged.getMembers(),membership.getMembers()) ) {
+- startElection(false);
+- }
+- }
+-
+- protected boolean isViewConf(CoordinationMessage msg) {
+- return Arrays.contains(msg.getType(),0,COORD_CONF,0,COORD_CONF.length);
+- }
+-
+- protected boolean hasHigherPriority(Member[] complete, Member[] local) {
+- if ( local == null || local.length == 0 ) return false;
+- if ( complete == null || complete.length == 0 ) return true;
+- AbsoluteOrder.absoluteOrder(complete);
+- AbsoluteOrder.absoluteOrder(local);
+- return (AbsoluteOrder.comp.compare(complete[0],local[0]) > 0);
+-
+- }
+-
+-
+- /**
+- * Returns coordinator if one is available
+- * @return Member
+- */
+- public Member getCoordinator() {
+- return (view != null && view.hasMembers()) ? view.getMembers()[0] :
null;
+- }
+-
+- public Member[] getView() {
+- return (view != null && view.hasMembers()) ? view.getMembers() : new
Member[0];
+- }
+-
+- public UniqueId getViewId() {
+- return viewId;
+- }
+-
+- /**
+- * Block in/out messages while a election is going on
+- */
+- protected void halt() {
+-
+- }
+-
+- /**
+- * Release lock for in/out messages election is completed
+- */
+- protected void release() {
+-
+- }
+-
+- /**
+- * Wait for an election to end
+- */
+- protected void waitForRelease() {
+-
+- }
+-
+-
+-//============================================================================================================
+-// OVERRIDDEN METHODS FROM CHANNEL INTERCEPTOR BASE
+-//============================================================================================================
+- public void start(int svc) throws ChannelException {
+- if (membership == null) setupMembership();
+- if (started)return;
+- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START,
this, "Before start"));
+- super.start(startsvc);
+- started = true;
+- if (view == null) view = new Membership(
(MemberImpl)super.getLocalMember(true), AbsoluteOrder.comp, true);
+- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_START,
this, "After start"));
+- startElection(false);
+- }
+-
+- public void stop(int svc) throws ChannelException {
+- try {
+- halt();
+- synchronized (electionMutex) {
+- if (!started)return;
+- started = false;
+- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_STOP,
this, "Before stop"));
+- super.stop(startsvc);
+- this.view = null;
+- this.viewId = null;
+- this.suggestedView = null;
+- this.suggestedviewId = null;
+- this.membership.reset();
+- fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_STOP,
this, "After stop"));
+- }
+- }finally {
+- release();
+- }
+- }
+-
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- waitForRelease();
+- super.sendMessage(destination, msg, payload);
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- if (
Arrays.contains(msg.getMessage().getBytesDirect(),0,COORD_ALIVE,0,COORD_ALIVE.length) ) {
+- //ignore message, its an alive message
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE,this,"Alive Message"));
+-
+- } else if (
Arrays.contains(msg.getMessage().getBytesDirect(),0,COORD_HEADER,0,COORD_HEADER.length) )
{
+- try {
+- CoordinationMessage cmsg = new CoordinationMessage(msg.getMessage());
+- Member[] cmbr = cmsg.getMembers();
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE,this,"Coord Msg
Arrived("+Arrays.toNameString(cmbr)+")"));
+- processCoordMessage(cmsg, msg.getAddress());
+- }catch ( ChannelException x ) {
+- log.error("Error processing coordination message. Could be
fatal.",x);
+- }
+- } else {
+- super.messageReceived(msg);
+- }
+- }
+-
+- public boolean accept(ChannelMessage msg) {
+- return super.accept(msg);
+- }
+-
+- public void memberAdded(Member member) {
+- memberAdded(member,true);
+- }
+-
+- public void memberAdded(Member member,boolean elect) {
+- try {
+- if ( membership == null ) setupMembership();
+- if ( membership.memberAlive((MemberImpl)member) )
super.memberAdded(member);
+- try {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_MBR_ADD,this,"Member
add("+member.getName()+")"));
+- if (started && elect) startElection(false);
+- }catch ( ChannelException x ) {
+- log.error("Unable to start election when member was
added.",x);
+- }
+- }finally {
+- }
+-
+- }
+-
+- public void memberDisappeared(Member member) {
+- try {
+-
+- membership.removeMember((MemberImpl)member);
+- super.memberDisappeared(member);
+- try {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_MBR_DEL,this,"Member
remove("+member.getName()+")"));
+- if ( started && (isCoordinator() || isHighest()) )
+- startElection(true); //to do, if a member disappears, only the
coordinator can start
+- }catch ( ChannelException x ) {
+- log.error("Unable to start election when member was
removed.",x);
+- }
+- }finally {
+- }
+- }
+-
+- public boolean isHighest() {
+- Member local = getLocalMember(false);
+- if ( membership.getMembers().length == 0 ) return true;
+- else return AbsoluteOrder.comp.compare(local,membership.getMembers()[0])<=0;
+- }
+-
+- public boolean isCoordinator() {
+- Member coord = getCoordinator();
+- return coord != null && getLocalMember(false).equals(coord);
+- }
+-
+- public void heartbeat() {
+- try {
+- MemberImpl local = (MemberImpl)getLocalMember(false);
+- if ( view != null && (Arrays.diff(view,membership,local).length != 0
|| Arrays.diff(membership,view,local).length != 0) ) {
+- if ( isHighest() ) {
+- fireInterceptorEvent(new
CoordinationEvent(CoordinationEvent.EVT_START_ELECT, this,
+- "Heartbeat found
inconsistency, restart election"));
+- startElection(true);
+- }
+- }
+- } catch ( Exception x ){
+- log.error("Unable to perform heartbeat.",x);
+- } finally {
+- super.heartbeat();
+- }
+- }
+-
+- /**
+- * has members
+- */
+- public boolean hasMembers() {
+-
+- return membership.hasMembers();
+- }
+-
+- /**
+- * Get all current cluster members
+- * @return all members or empty array
+- */
+- public Member[] getMembers() {
+-
+- return membership.getMembers();
+- }
+-
+- /**
+- *
+- * @param mbr Member
+- * @return Member
+- */
+- public Member getMember(Member mbr) {
+-
+- return membership.getMember(mbr);
+- }
+-
+- /**
+- * Return the member that represents this node.
+- *
+- * @return Member
+- */
+- public Member getLocalMember(boolean incAlive) {
+- Member local = super.getLocalMember(incAlive);
+- if ( view == null && (local != null)) setupMembership();
+- return local;
+- }
+-
+- protected synchronized void setupMembership() {
+- if ( membership == null ) {
+- membership = new
Membership((MemberImpl)super.getLocalMember(true),AbsoluteOrder.comp,false);
+- }
+- }
+-
+-
+-//============================================================================================================
+-// HELPER CLASSES FOR COORDINATION
+-//============================================================================================================
+-
+-
+-
+-
+- public static class CoordinationMessage {
+- //X{A-ldr, A-src, mbrs-A,B,C,D}
+- protected XByteBuffer buf;
+- protected MemberImpl leader;
+- protected MemberImpl source;
+- protected MemberImpl[] view;
+- protected UniqueId id;
+- protected byte[] type;
+- protected long timestamp = System.currentTimeMillis();
+-
+- public CoordinationMessage(XByteBuffer buf) {
+- this.buf = buf;
+- parse();
+- }
+-
+- public CoordinationMessage(MemberImpl leader,
+- MemberImpl source,
+- MemberImpl[] view,
+- UniqueId id,
+- byte[] type) {
+- this.buf = new XByteBuffer(4096,false);
+- this.leader = leader;
+- this.source = source;
+- this.view = view;
+- this.id = id;
+- this.type = type;
+- this.write();
+- }
+-
+-
+- public byte[] getHeader() {
+- return NonBlockingCoordinator.COORD_HEADER;
+- }
+-
+- public MemberImpl getLeader() {
+- if ( leader == null ) parse();
+- return leader;
+- }
+-
+- public MemberImpl getSource() {
+- if ( source == null ) parse();
+- return source;
+- }
+-
+- public UniqueId getId() {
+- if ( id == null ) parse();
+- return id;
+- }
+-
+- public MemberImpl[] getMembers() {
+- if ( view == null ) parse();
+- return view;
+- }
+-
+- public byte[] getType() {
+- if (type == null ) parse();
+- return type;
+- }
+-
+- public XByteBuffer getBuffer() {
+- return this.buf;
+- }
+-
+- public void parse() {
+- //header
+- int offset = 16;
+- //leader
+- int ldrLen = buf.toInt(buf.getBytesDirect(),offset);
+- offset += 4;
+- byte[] ldr = new byte[ldrLen];
+- System.arraycopy(buf.getBytesDirect(),offset,ldr,0,ldrLen);
+- leader = MemberImpl.getMember(ldr);
+- offset += ldrLen;
+- //source
+- int srcLen = buf.toInt(buf.getBytesDirect(),offset);
+- offset += 4;
+- byte[] src = new byte[srcLen];
+- System.arraycopy(buf.getBytesDirect(),offset,src,0,srcLen);
+- source = MemberImpl.getMember(src);
+- offset += srcLen;
+- //view
+- int mbrCount = buf.toInt(buf.getBytesDirect(),offset);
+- offset += 4;
+- view = new MemberImpl[mbrCount];
+- for (int i=0; i<view.length; i++ ) {
+- int mbrLen = buf.toInt(buf.getBytesDirect(),offset);
+- offset += 4;
+- byte[] mbr = new byte[mbrLen];
+- System.arraycopy(buf.getBytesDirect(), offset, mbr, 0, mbrLen);
+- view[i] = MemberImpl.getMember(mbr);
+- offset += mbrLen;
+- }
+- //id
+- this.id = new UniqueId(buf.getBytesDirect(),offset,16);
+- offset += 16;
+- type = new byte[16];
+- System.arraycopy(buf.getBytesDirect(), offset, type, 0, type.length);
+- offset += 16;
+-
+- }
+-
+- public void write() {
+- buf.reset();
+- //header
+- buf.append(COORD_HEADER,0,COORD_HEADER.length);
+- //leader
+- byte[] ldr = leader.getData(false,false);
+- buf.append(ldr.length);
+- buf.append(ldr,0,ldr.length);
+- ldr = null;
+- //source
+- byte[] src = source.getData(false,false);
+- buf.append(src.length);
+- buf.append(src,0,src.length);
+- src = null;
+- //view
+- buf.append(view.length);
+- for (int i=0; i<view.length; i++ ) {
+- byte[] mbr = view[i].getData(false,false);
+- buf.append(mbr.length);
+- buf.append(mbr,0,mbr.length);
+- }
+- //id
+- buf.append(id.getBytes(),0,id.getBytes().length);
+- buf.append(type,0,type.length);
+- }
+- }
+-
+- public void fireInterceptorEvent(InterceptorEvent event) {
+- if (event instanceof CoordinationEvent &&
+- ((CoordinationEvent)event).type == CoordinationEvent.EVT_CONF_RX)
+- log.info(event);
+- }
+-
+- public static class CoordinationEvent implements InterceptorEvent {
+- public static final int EVT_START = 1;
+- public static final int EVT_MBR_ADD = 2;
+- public static final int EVT_MBR_DEL = 3;
+- public static final int EVT_START_ELECT = 4;
+- public static final int EVT_PROCESS_ELECT = 5;
+- public static final int EVT_MSG_ARRIVE = 6;
+- public static final int EVT_PRE_MERGE = 7;
+- public static final int EVT_POST_MERGE = 8;
+- public static final int EVT_WAIT_FOR_MSG = 9;
+- public static final int EVT_SEND_MSG = 10;
+- public static final int EVT_STOP = 11;
+- public static final int EVT_CONF_RX = 12;
+- public static final int EVT_ELECT_ABANDONED = 13;
+-
+- int type;
+- ChannelInterceptor interceptor;
+- Member coord;
+- Member[] mbrs;
+- String info;
+- Membership view;
+- Membership suggestedView;
+- public CoordinationEvent(int type,ChannelInterceptor interceptor, String info)
{
+- this.type = type;
+- this.interceptor = interceptor;
+- this.coord = ((NonBlockingCoordinator)interceptor).getCoordinator();
+- this.mbrs = ((NonBlockingCoordinator)interceptor).membership.getMembers();
+- this.info = info;
+- this.view = ((NonBlockingCoordinator)interceptor).view;
+- this.suggestedView = ((NonBlockingCoordinator)interceptor).suggestedView;
+- }
+-
+- public int getEventType() {
+- return type;
+- }
+-
+- public String getEventTypeDesc() {
+- switch (type) {
+- case EVT_START: return "EVT_START:"+info;
+- case EVT_MBR_ADD: return "EVT_MBR_ADD:"+info;
+- case EVT_MBR_DEL: return "EVT_MBR_DEL:"+info;
+- case EVT_START_ELECT: return "EVT_START_ELECT:"+info;
+- case EVT_PROCESS_ELECT: return "EVT_PROCESS_ELECT:"+info;
+- case EVT_MSG_ARRIVE: return "EVT_MSG_ARRIVE:"+info;
+- case EVT_PRE_MERGE: return "EVT_PRE_MERGE:"+info;
+- case EVT_POST_MERGE: return "EVT_POST_MERGE:"+info;
+- case EVT_WAIT_FOR_MSG: return "EVT_WAIT_FOR_MSG:"+info;
+- case EVT_SEND_MSG: return "EVT_SEND_MSG:"+info;
+- case EVT_STOP: return "EVT_STOP:"+info;
+- case EVT_CONF_RX: return "EVT_CONF_RX:"+info;
+- case EVT_ELECT_ABANDONED: return "EVT_ELECT_ABANDONED:"+info;
+- default: return "Unknown";
+- }
+- }
+-
+- public ChannelInterceptor getInterceptor() {
+- return interceptor;
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("CoordinationEvent[type=");
+- buf.append(type).append("\n\tLocal:");
+- Member local = interceptor.getLocalMember(false);
+-
buf.append(local!=null?local.getName():"").append("\n\tCoord:");
+-
buf.append(coord!=null?coord.getName():"").append("\n\tView:");
+-
buf.append(Arrays.toNameString(view!=null?view.getMembers():null)).append("\n\tSuggested
View:");
+-
buf.append(Arrays.toNameString(suggestedView!=null?suggestedView.getMembers():null)).append("\n\tMembers:");
+- buf.append(Arrays.toNameString(mbrs)).append("\n\tInfo:");
+- buf.append(info).append("]");
+- return buf.toString();
+- }
+- }
+-
+-
+-
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java (revision
590752)
++++ java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java (working
copy)
+@@ -1,100 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.io.ByteArrayInputStream;
+-import java.io.ByteArrayOutputStream;
+-import java.io.IOException;
+-import java.util.Arrays;
+-import java.util.zip.GZIPInputStream;
+-import java.util.zip.GZIPOutputStream;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-
+-
+-
+-/**
+- *
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class GzipInterceptor extends ChannelInterceptorBase {
+- public static final int DEFAULT_BUFFER_SIZE = 2048;
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- try {
+- byte[] data = compress(msg.getMessage().getBytes());
+- msg.getMessage().trim(msg.getMessage().getLength());
+- msg.getMessage().append(data,0,data.length);
+- getNext().sendMessage(destination, msg, payload);
+- } catch ( IOException x ) {
+- log.error("Unable to compress byte contents");
+- throw new ChannelException(x);
+- }
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- try {
+- byte[] data = decompress(msg.getMessage().getBytes());
+- msg.getMessage().trim(msg.getMessage().getLength());
+- msg.getMessage().append(data,0,data.length);
+- getPrevious().messageReceived(msg);
+- } catch ( IOException x ) {
+- log.error("Unable to decompress byte contents",x);
+- }
+- }
+-
+- public static byte[] compress(byte[] data) throws IOException {
+- ByteArrayOutputStream bout = new ByteArrayOutputStream();
+- GZIPOutputStream gout = new GZIPOutputStream(bout);
+- gout.write(data);
+- gout.flush();
+- gout.close();
+- return bout.toByteArray();
+- }
+-
+- /**
+- * @todo Fix to create an automatically growing buffer.
+- * @param data byte[]
+- * @return byte[]
+- * @throws IOException
+- */
+- public static byte[] decompress(byte[] data) throws IOException {
+- ByteArrayInputStream bin = new ByteArrayInputStream(data);
+- GZIPInputStream gin = new GZIPInputStream(bin);
+- byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];
+- int length = gin.read(tmp);
+- byte[] result = new byte[length];
+- System.arraycopy(tmp,0,result,0,length);
+- return result;
+- }
+-
+- public static void main(String[] arg) throws Exception {
+- byte[] data = new byte[1024];
+- Arrays.fill(data,(byte)1);
+- byte[] compress = compress(data);
+- byte[] decompress = decompress(compress);
+- System.out.println("Debug test");
+-
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java (revision
590752)
++++ java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java (working
copy)
+@@ -1,323 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.net.InetAddress;
+-import java.net.InetSocketAddress;
+-import java.net.Socket;
+-import java.net.SocketTimeoutException;
+-import java.util.Arrays;
+-import java.util.HashMap;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelException.FaultyMember;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.RemoteProcessException;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import org.apache.catalina.tribes.membership.Membership;
+-import java.net.ConnectException;
+-
+-/**
+- * <p>Title: A perfect failure detector </p>
+- *
+- * <p>Description: The TcpFailureDetector is a useful interceptor
+- * that adds reliability to the membership layer.</p>
+- * <p>
+- * If the network is busy, or the system is busy so that the membership receiver thread
+- * is not getting enough time to update its table, members can be "timed
out"
+- * This failure detector will intercept the memberDisappeared message(unless its a true
shutdown message)
+- * and connect to the member using TCP.
+- * </p>
+- * <p>
+- * The TcpFailureDetector works in two ways. <br>
+- * 1. It intercepts memberDisappeared events
+- * 2. It catches send errors
+- * </p>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class TcpFailureDetector extends ChannelInterceptorBase {
+-
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( TcpFailureDetector.class );
+-
+- protected static byte[] TCP_FAIL_DETECT = new byte[] {
+- 79, -89, 115, 72, 121, -126, 67, -55, -97, 111, -119, -128, -95, 91, 7, 20,
+- 125, -39, 82, 91, -21, -15, 67, -102, -73, 126, -66, -113, -127, 103, 30, -74,
+- 55, 21, -66, -121, 69, 126, 76, -88, -65, 10, 77, 19, 83, 56, 21, 50,
+- 85, -10, -108, -73, 58, -6, 64, 120, -111, 4, 125, -41, 114, -124, -64, -43};
+-
+- protected boolean performConnectTest = true;
+-
+- protected long connectTimeout = 1000;//1 second default
+-
+- protected boolean performSendTest = true;
+-
+- protected boolean performReadTest = false;
+-
+- protected long readTestTimeout = 5000;//5 seconds
+-
+- protected Membership membership = null;
+-
+- protected HashMap removeSuspects = new HashMap();
+-
+- protected HashMap addSuspects = new HashMap();
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- try {
+- super.sendMessage(destination, msg, payload);
+- }catch ( ChannelException cx ) {
+- FaultyMember[] mbrs = cx.getFaultyMembers();
+- for ( int i=0; i<mbrs.length; i++ ) {
+- if ( mbrs[i].getCause()!=null &&
+- (!(mbrs[i].getCause() instanceof RemoteProcessException)) )
{//RemoteProcessException's are ok
+- this.memberDisappeared(mbrs[i].getMember());
+- }//end if
+- }//for
+- throw cx;
+- }
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- //catch incoming
+- boolean process = true;
+- if ( okToProcess(msg.getOptions()) ) {
+- //check to see if it is a testMessage, if so, process = false
+- process = ( (msg.getMessage().getLength() != TCP_FAIL_DETECT.length) ||
+- (!Arrays.equals(TCP_FAIL_DETECT,msg.getMessage().getBytes()) )
);
+- }//end if
+-
+- //ignore the message, it doesnt have the flag set
+- if ( process ) super.messageReceived(msg);
+- else if ( log.isDebugEnabled() ) log.debug("Received a failure detector
packet:"+msg);
+- }//messageReceived
+-
+-
+- public void memberAdded(Member member) {
+- if ( membership == null ) setupMembership();
+- boolean notify = false;
+- synchronized (membership) {
+- if (removeSuspects.containsKey(member)) {
+- //previously marked suspect, system below picked up the member again
+- removeSuspects.remove(member);
+- } else if (membership.getMember( (MemberImpl) member) == null){
+- //if we add it here, then add it upwards too
+- //check to see if it is alive
+- if (memberAlive(member)) {
+- membership.memberAlive( (MemberImpl) member);
+- notify = true;
+- } else {
+- addSuspects.put(member, new Long(System.currentTimeMillis()));
+- }
+- }
+- }
+- if ( notify ) super.memberAdded(member);
+- }
+-
+- public void memberDisappeared(Member member) {
+- if ( membership == null ) setupMembership();
+- boolean notify = false;
+- boolean shutdown = Arrays.equals(member.getCommand(),Member.SHUTDOWN_PAYLOAD);
+- if ( !shutdown )
+- if(log.isInfoEnabled())
+- log.info("Received memberDisappeared["+member+"] message.
Will verify.");
+- synchronized (membership) {
+- //check to see if the member really is gone
+- //if the payload is not a shutdown message
+- if (shutdown || !memberAlive(member)) {
+- //not correct, we need to maintain the map
+- membership.removeMember( (MemberImpl) member);
+- removeSuspects.remove(member);
+- notify = true;
+- } else {
+- //add the member as suspect
+- removeSuspects.put(member, new Long(System.currentTimeMillis()));
+- }
+- }
+- if ( notify ) {
+- if(log.isInfoEnabled())
+- log.info("Verification complete. Member
disappeared["+member+"]");
+- super.memberDisappeared(member);
+- } else {
+- if(log.isInfoEnabled())
+- log.info("Verification complete. Member still
alive["+member+"]");
+-
+- }
+- }
+-
+- public boolean hasMembers() {
+- if ( membership == null ) setupMembership();
+- return membership.hasMembers();
+- }
+-
+- public Member[] getMembers() {
+- if ( membership == null ) setupMembership();
+- return membership.getMembers();
+- }
+-
+- public Member getMember(Member mbr) {
+- if ( membership == null ) setupMembership();
+- return membership.getMember(mbr);
+- }
+-
+- public Member getLocalMember(boolean incAlive) {
+- return super.getLocalMember(incAlive);
+- }
+-
+- public void heartbeat() {
+- super.heartbeat();
+- checkMembers(false);
+- }
+- public void checkMembers(boolean checkAll) {
+-
+- try {
+- if (membership == null) setupMembership();
+- synchronized (membership) {
+- if ( !checkAll ) performBasicCheck();
+- else performForcedCheck();
+- }
+- }catch ( Exception x ) {
+- log.warn("Unable to perform heartbeat on the
TcpFailureDetector.",x);
+- } finally {
+-
+- }
+- }
+-
+- protected void performForcedCheck() {
+- //update all alive times
+- Member[] members = super.getMembers();
+- for (int i = 0; members != null && i < members.length; i++) {
+- if (memberAlive(members[i])) {
+- if (membership.memberAlive((MemberImpl)members[i]))
super.memberAdded(members[i]);
+- addSuspects.remove(members[i]);
+- } else {
+- if (membership.getMember(members[i])!=null) {
+- membership.removeMember((MemberImpl)members[i]);
+- removeSuspects.remove(members[i]);
+- super.memberDisappeared((MemberImpl)members[i]);
+- }
+- } //end if
+- } //for
+-
+- }
+-
+- protected void performBasicCheck() {
+- //update all alive times
+- Member[] members = super.getMembers();
+- for (int i = 0; members != null && i < members.length; i++) {
+- if (membership.memberAlive( (MemberImpl) members[i])) {
+- //we don't have this one in our membership, check to see if he/she
is alive
+- if (memberAlive(members[i])) {
+- log.warn("Member added, even though we werent notified:" +
members[i]);
+- super.memberAdded(members[i]);
+- } else {
+- membership.removeMember( (MemberImpl) members[i]);
+- } //end if
+- } //end if
+- } //for
+-
+- //check suspect members if they are still alive,
+- //if not, simply issue the memberDisappeared message
+- MemberImpl[] keys = (MemberImpl[]) removeSuspects.keySet().toArray(new
MemberImpl[removeSuspects.size()]);
+- for (int i = 0; i < keys.length; i++) {
+- MemberImpl m = (MemberImpl) keys[i];
+- if (membership.getMember(m) != null && (!memberAlive(m))) {
+- membership.removeMember(m);
+- super.memberDisappeared(m);
+- removeSuspects.remove(m);
+- if(log.isInfoEnabled())
+- log.info("Suspect member, confirmed
dead.["+m+"]");
+- } //end if
+- }
+-
+- //check add suspects members if they are alive now,
+- //if they are, simply issue the memberAdded message
+- keys = (MemberImpl[]) addSuspects.keySet().toArray(new
MemberImpl[addSuspects.size()]);
+- for (int i = 0; i < keys.length; i++) {
+- MemberImpl m = (MemberImpl) keys[i];
+- if ( membership.getMember(m) == null && (memberAlive(m))) {
+- membership.memberAlive(m);
+- super.memberAdded(m);
+- addSuspects.remove(m);
+- if(log.isInfoEnabled())
+- log.info("Suspect member, confirmed
alive.["+m+"]");
+- } //end if
+- }
+- }
+-
+- protected synchronized void setupMembership() {
+- if ( membership == null ) {
+- membership = new Membership((MemberImpl)super.getLocalMember(true));
+- }
+-
+- }
+-
+- protected boolean memberAlive(Member mbr) {
+- return
memberAlive(mbr,TCP_FAIL_DETECT,performSendTest,performReadTest,readTestTimeout,connectTimeout,getOptionFlag());
+- }
+-
+- protected static boolean memberAlive(Member mbr, byte[] msgData,
+- boolean sendTest, boolean readTest,
+- long readTimeout, long conTimeout,
+- int optionFlag) {
+- //could be a shutdown notification
+- if ( Arrays.equals(mbr.getCommand(),Member.SHUTDOWN_PAYLOAD) ) return false;
+-
+- Socket socket = new Socket();
+- try {
+- InetAddress ia = InetAddress.getByAddress(mbr.getHost());
+- InetSocketAddress addr = new InetSocketAddress(ia, mbr.getPort());
+- socket.setSoTimeout((int)readTimeout);
+- socket.connect(addr, (int) conTimeout);
+- if ( sendTest ) {
+- ChannelData data = new ChannelData(true);
+- data.setAddress(mbr);
+- data.setMessage(new XByteBuffer(msgData,false));
+- data.setTimestamp(System.currentTimeMillis());
+- int options = optionFlag | Channel.SEND_OPTIONS_BYTE_MESSAGE;
+- if ( readTest ) options = (options | Channel.SEND_OPTIONS_USE_ACK);
+- else options = (options & (~Channel.SEND_OPTIONS_USE_ACK));
+- data.setOptions(options);
+- byte[] message = XByteBuffer.createDataPackage(data);
+- socket.getOutputStream().write(message);
+- if ( readTest ) {
+- int length = socket.getInputStream().read(message);
+- return length > 0;
+- }
+- }//end if
+- return true;
+- } catch ( SocketTimeoutException sx) {
+- //do nothing, we couldn't connect
+- } catch ( ConnectException cx) {
+- //do nothing, we couldn't connect
+- }catch (Exception x ) {
+- log.error("Unable to perform failure detection check, assuming member
down.",x);
+- } finally {
+- try {socket.close(); } catch ( Exception ignore ){}
+- }
+- return false;
+- }
+-
+-
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/ThroughputInterceptor.java (working
copy)
+@@ -1,120 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import java.text.DecimalFormat;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import java.util.concurrent.atomic.AtomicInteger;
+-import java.util.concurrent.atomic.AtomicLong;
+-
+-
+-
+-/**
+- *
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class ThroughputInterceptor extends ChannelInterceptorBase {
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ThroughputInterceptor.class);
+-
+- double mbTx = 0;
+- double mbAppTx = 0;
+- double mbRx = 0;
+- double timeTx = 0;
+- double lastCnt = 0;
+- AtomicLong msgTxCnt = new AtomicLong(1);
+- AtomicLong msgRxCnt = new AtomicLong(0);
+- AtomicLong msgTxErr = new AtomicLong(0);
+- int interval = 10000;
+- AtomicInteger access = new AtomicInteger(0);
+- long txStart = 0;
+- long rxStart = 0;
+- DecimalFormat df = new DecimalFormat("#0.00");
+-
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- if ( access.addAndGet(1) == 1 ) txStart = System.currentTimeMillis();
+- long bytes =
XByteBuffer.getDataPackageLength(((ChannelData)msg).getDataPackageLength());
+- try {
+- super.sendMessage(destination, msg, payload);
+- }catch ( ChannelException x ) {
+- msgTxErr.addAndGet(1);
+- access.addAndGet(-1);
+- throw x;
+- }
+- mbTx += ((double)(bytes*destination.length))/(1024d*1024d);
+- mbAppTx += ((double)(bytes))/(1024d*1024d);
+- if ( access.addAndGet(-1) == 0 ) {
+- long stop = System.currentTimeMillis();
+- timeTx += ( (double) (stop - txStart)) / 1000d;
+- if ((msgTxCnt.get() / interval) >= lastCnt) {
+- lastCnt++;
+- report(timeTx);
+- }
+- }
+- msgTxCnt.addAndGet(1);
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- if ( rxStart == 0 ) rxStart = System.currentTimeMillis();
+- long bytes =
XByteBuffer.getDataPackageLength(((ChannelData)msg).getDataPackageLength());
+- mbRx += ((double)bytes)/(1024d*1024d);
+- msgRxCnt.addAndGet(1);
+- if ( msgRxCnt.get() % interval == 0 ) report(timeTx);
+- super.messageReceived(msg);
+-
+- }
+-
+- public void report(double timeTx) {
+- StringBuffer buf = new StringBuffer("ThroughputInterceptor Report[\n\tTx
Msg:");
+- buf.append(msgTxCnt).append(" messages\n\tSent:");
+- buf.append(df.format(mbTx));
+- buf.append(" MB (total)\n\tSent:");
+- buf.append(df.format(mbAppTx));
+- buf.append(" MB (application)\n\tTime:");
+- buf.append(df.format(timeTx));
+- buf.append(" seconds\n\tTx Speed:");
+- buf.append(df.format(mbTx/timeTx));
+- buf.append(" MB/sec (total)\n\tTxSpeed:");
+- buf.append(df.format(mbAppTx/timeTx));
+- buf.append(" MB/sec (application)\n\tError Msg:");
+- buf.append(msgTxErr).append("\n\tRx Msg:");
+- buf.append(msgRxCnt);
+- buf.append(" messages\n\tRx Speed:");
+-
buf.append(df.format(mbRx/((double)((System.currentTimeMillis()-rxStart)/1000))));
+- buf.append(" MB/sec (since 1st msg)\n\tReceived:");
+- buf.append(df.format(mbRx)).append(" MB]\n");
+- if ( log.isInfoEnabled() ) log.info(buf);
+- }
+-
+- public void setInterval(int interval) {
+- this.interval = interval;
+- }
+-
+- public int getInterval() {
+- return interval;
+- }
+-
+-}
+Index:
java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/MessageDispatch15Interceptor.java (working
copy)
+@@ -1,112 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.util.concurrent.LinkedBlockingQueue;
+-import java.util.concurrent.ThreadPoolExecutor;
+-import java.util.concurrent.atomic.AtomicLong;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.transport.bio.util.LinkObject;
+-import java.util.concurrent.TimeUnit;
+-
+-/**
+- *
+- * Same implementation as the MessageDispatchInterceptor
+- * except is ues an atomic long for the currentSize calculation
+- * and uses a thread pool for message sending.
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public class MessageDispatch15Interceptor extends MessageDispatchInterceptor {
+-
+- protected AtomicLong currentSize = new AtomicLong(0);
+- protected ThreadPoolExecutor executor = null;
+- protected int maxThreads = 10;
+- protected int maxSpareThreads = 2;
+- protected long keepAliveTime = 5000;
+- protected LinkedBlockingQueue<Runnable> runnablequeue = new
LinkedBlockingQueue<Runnable>();
+-
+- public long getCurrentSize() {
+- return currentSize.get();
+- }
+-
+- public long addAndGetCurrentSize(long inc) {
+- return currentSize.addAndGet(inc);
+- }
+-
+- public long setAndGetCurrentSize(long value) {
+- currentSize.set(value);
+- return value;
+- }
+-
+- public boolean addToQueue(ChannelMessage msg, Member[] destination,
InterceptorPayload payload) {
+- final LinkObject obj = new LinkObject(msg,destination,payload);
+- Runnable r = new Runnable() {
+- public void run() {
+- sendAsyncData(obj);
+- }
+- };
+- executor.execute(r);
+- return true;
+- }
+-
+- public LinkObject removeFromQueue() {
+- return null; //not used, thread pool contains its own queue.
+- }
+-
+- public void startQueue() {
+- if ( run ) return;
+- executor = new
ThreadPoolExecutor(maxSpareThreads,maxThreads,keepAliveTime,TimeUnit.MILLISECONDS,runnablequeue);
+- run = true;
+- }
+-
+- public void stopQueue() {
+- run = false;
+- executor.shutdownNow();
+- setAndGetCurrentSize(0);
+- runnablequeue.clear();
+- }
+-
+- public long getKeepAliveTime() {
+- return keepAliveTime;
+- }
+-
+- public int getMaxSpareThreads() {
+- return maxSpareThreads;
+- }
+-
+- public int getMaxThreads() {
+- return maxThreads;
+- }
+-
+- public void setKeepAliveTime(long keepAliveTime) {
+- this.keepAliveTime = keepAliveTime;
+- }
+-
+- public void setMaxSpareThreads(int maxSpareThreads) {
+- this.maxSpareThreads = maxSpareThreads;
+- }
+-
+- public void setMaxThreads(int maxThreads) {
+- this.maxThreads = maxThreads;
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java (revision
590752)
++++ java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java (working
copy)
+@@ -1,329 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.util.HashMap;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import java.util.concurrent.atomic.AtomicInteger;
+-import java.util.concurrent.locks.ReentrantReadWriteLock;
+-
+-
+-
+-/**
+- *
+- * The order interceptor guarantees that messages are received in the same order they
were
+- * sent.
+- * This interceptor works best with the ack=true setting. <br>
+- * There is no point in
+- * using this with the replicationMode="fastasynchqueue" as this mode
guarantees ordering.<BR>
+- * If you are using the mode ack=false replicationMode=pooled, and have a lot of
concurrent threads,
+- * this interceptor can really slow you down, as many messages will be completely out of
order
+- * and the queue might become rather large. If this is the case, then you might want to
set
+- * the value OrderInterceptor.maxQueue = 25 (meaning that we will never keep more than
25 messages in our queue)
+- * <br><b>Configuration Options</b><br>
+- * OrderInteceptor.expire=<milliseconds> - if a message arrives out of order, how
long before we act on it <b>default=3000ms</b><br>
+- * OrderInteceptor.maxQueue=<max queue size> - how much can the queue grow to
ensure ordering.
+- * This setting is useful to avoid
OutOfMemoryErrors<b>default=Integer.MAX_VALUE</b><br>
+- * OrderInterceptor.forwardExpired=<boolean> - this flag tells the interceptor
what to
+- * do when a message has expired or the queue has grown larger than the maxQueue value.
+- * true means that the message is sent up the stack to the receiver that will receive
and out of order message
+- * false means, forget the message and reset the message counter.
<b>default=true</b>
+- *
+- *
+- * @author Filip Hanik
+- * @version 1.1
+- */
+-public class OrderInterceptor extends ChannelInterceptorBase {
+- private HashMap outcounter = new HashMap();
+- private HashMap incounter = new HashMap();
+- private HashMap incoming = new HashMap();
+- private long expire = 3000;
+- private boolean forwardExpired = true;
+- private int maxQueue = Integer.MAX_VALUE;
+-
+- ReentrantReadWriteLock inLock = new ReentrantReadWriteLock(true);
+- ReentrantReadWriteLock outLock= new ReentrantReadWriteLock(true);
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- if ( !okToProcess(msg.getOptions()) ) {
+- super.sendMessage(destination, msg, payload);
+- return;
+- }
+- ChannelException cx = null;
+- for (int i=0; i<destination.length; i++ ) {
+- try {
+- int nr = 0;
+- try {
+- outLock.writeLock().lock();
+- nr = incCounter(destination[i]);
+- } finally {
+- outLock.writeLock().unlock();
+- }
+- //reduce byte copy
+- msg.getMessage().append(nr);
+- try {
+- getNext().sendMessage(new Member[] {destination[i]}, msg, payload);
+- } finally {
+- msg.getMessage().trim(4);
+- }
+- }catch ( ChannelException x ) {
+- if ( cx == null ) cx = x;
+- cx.addFaultyMember(x.getFaultyMembers());
+- }
+- }//for
+- if ( cx != null ) throw cx;
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- if ( !okToProcess(msg.getOptions()) ) {
+- super.messageReceived(msg);
+- return;
+- }
+- int msgnr =
XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);
+- msg.getMessage().trim(4);
+- MessageOrder order = new MessageOrder(msgnr,(ChannelMessage)msg.deepclone());
+- try {
+- inLock.writeLock().lock();
+- if ( processIncoming(order) ) processLeftOvers(msg.getAddress(),false);
+- }finally {
+- inLock.writeLock().unlock();
+- }
+- }
+- protected void processLeftOvers(Member member, boolean force) {
+- MessageOrder tmp = (MessageOrder)incoming.get(member);
+- if ( force ) {
+- Counter cnt = getInCounter(member);
+- cnt.setCounter(Integer.MAX_VALUE);
+- }
+- if ( tmp!= null ) processIncoming(tmp);
+- }
+- /**
+- *
+- * @param order MessageOrder
+- * @return boolean - true if a message expired and was processed
+- */
+- protected boolean processIncoming(MessageOrder order) {
+- boolean result = false;
+- Member member = order.getMessage().getAddress();
+- Counter cnt = getInCounter(member);
+-
+- MessageOrder tmp = (MessageOrder)incoming.get(member);
+- if ( tmp != null ) {
+- order = MessageOrder.add(tmp,order);
+- }
+-
+-
+- while ( (order!=null) && (order.getMsgNr() <= cnt.getCounter()) ) {
+- //we are right on target. process orders
+- if ( order.getMsgNr() == cnt.getCounter() ) cnt.inc();
+- else if ( order.getMsgNr() > cnt.getCounter() )
cnt.setCounter(order.getMsgNr());
+- super.messageReceived(order.getMessage());
+- order.setMessage(null);
+- order = order.next;
+- }
+- MessageOrder head = order;
+- MessageOrder prev = null;
+- tmp = order;
+- //flag to empty out the queue when it larger than maxQueue
+- boolean empty = order!=null?order.getCount()>=maxQueue:false;
+- while ( tmp != null ) {
+- //process expired messages or empty out the queue
+- if ( tmp.isExpired(expire) || empty ) {
+- //reset the head
+- if ( tmp == head ) head = tmp.next;
+- cnt.setCounter(tmp.getMsgNr()+1);
+- if ( getForwardExpired() )
+- super.messageReceived(tmp.getMessage());
+- tmp.setMessage(null);
+- tmp = tmp.next;
+- if ( prev != null ) prev.next = tmp;
+- result = true;
+- } else {
+- prev = tmp;
+- tmp = tmp.next;
+- }
+- }
+- if ( head == null ) incoming.remove(member);
+- else incoming.put(member, head);
+- return result;
+- }
+-
+- public void memberAdded(Member member) {
+- //notify upwards
+- super.memberAdded(member);
+- }
+-
+- public void memberDisappeared(Member member) {
+- //reset counters - lock free
+- incounter.remove(member);
+- outcounter.remove(member);
+- //clear the remaining queue
+- processLeftOvers(member,true);
+- //notify upwards
+- super.memberDisappeared(member);
+- }
+-
+- protected int incCounter(Member mbr) {
+- Counter cnt = getOutCounter(mbr);
+- return cnt.inc();
+- }
+-
+- protected Counter getInCounter(Member mbr) {
+- Counter cnt = (Counter)incounter.get(mbr);
+- if ( cnt == null ) {
+- cnt = new Counter();
+- cnt.inc(); //always start at 1 for incoming
+- incounter.put(mbr,cnt);
+- }
+- return cnt;
+- }
+-
+- protected Counter getOutCounter(Member mbr) {
+- Counter cnt = (Counter)outcounter.get(mbr);
+- if ( cnt == null ) {
+- cnt = new Counter();
+- outcounter.put(mbr,cnt);
+- }
+- return cnt;
+- }
+-
+- protected static class Counter {
+- private AtomicInteger value = new AtomicInteger(0);
+-
+- public int getCounter() {
+- return value.get();
+- }
+-
+- public void setCounter(int counter) {
+- this.value.set(counter);
+- }
+-
+- public int inc() {
+- return value.addAndGet(1);
+- }
+- }
+-
+- protected static class MessageOrder {
+- private long received = System.currentTimeMillis();
+- private MessageOrder next;
+- private int msgNr;
+- private ChannelMessage msg = null;
+- public MessageOrder(int msgNr,ChannelMessage msg) {
+- this.msgNr = msgNr;
+- this.msg = msg;
+- }
+-
+- public boolean isExpired(long expireTime) {
+- return (System.currentTimeMillis()-received) > expireTime;
+- }
+-
+- public ChannelMessage getMessage() {
+- return msg;
+- }
+-
+- public void setMessage(ChannelMessage msg) {
+- this.msg = msg;
+- }
+-
+- public void setNext(MessageOrder order) {
+- this.next = order;
+- }
+- public MessageOrder getNext() {
+- return next;
+- }
+-
+- public int getCount() {
+- int counter = 1;
+- MessageOrder tmp = next;
+- while ( tmp != null ) {
+- counter++;
+- tmp = tmp.next;
+- }
+- return counter;
+- }
+-
+- public static MessageOrder add(MessageOrder head, MessageOrder add) {
+- if ( head == null ) return add;
+- if ( add == null ) return head;
+- if ( head == add ) return add;
+-
+- if ( head.getMsgNr() > add.getMsgNr() ) {
+- add.next = head;
+- return add;
+- }
+-
+- MessageOrder iter = head;
+- MessageOrder prev = null;
+- while ( iter.getMsgNr() < add.getMsgNr() && (iter.next !=null ) )
{
+- prev = iter;
+- iter = iter.next;
+- }
+- if ( iter.getMsgNr() < add.getMsgNr() ) {
+- //add after
+- add.next = iter.next;
+- iter.next = add;
+- } else if (iter.getMsgNr() > add.getMsgNr()) {
+- //add before
+- prev.next = add;
+- add.next = iter;
+-
+- } else {
+- throw new ArithmeticException("Message added has the same counter,
synchronization bug. Disable the order interceptor");
+- }
+-
+- return head;
+- }
+-
+- public int getMsgNr() {
+- return msgNr;
+- }
+-
+-
+-
+- }
+-
+- public void setExpire(long expire) {
+- this.expire = expire;
+- }
+-
+- public void setForwardExpired(boolean forwardExpired) {
+- this.forwardExpired = forwardExpired;
+- }
+-
+- public void setMaxQueue(int maxQueue) {
+- this.maxQueue = maxQueue;
+- }
+-
+- public long getExpire() {
+- return expire;
+- }
+-
+- public boolean getForwardExpired() {
+- return forwardExpired;
+- }
+-
+- public int getMaxQueue() {
+- return maxQueue;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java (working
copy)
+@@ -1,102 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import org.apache.catalina.tribes.membership.Membership;
+-import java.util.Arrays;
+-
+-/**
+- * <p>Title: Member domain filter interceptor </p>
+- *
+- * <p>Description: Filters membership based on domain.
+- * </p>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class DomainFilterInterceptor extends ChannelInterceptorBase {
+-
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( DomainFilterInterceptor.class );
+-
+- protected Membership membership = null;
+-
+- protected byte[] domain = new byte[0];
+-
+- public void messageReceived(ChannelMessage msg) {
+- //should we filter incoming based on domain?
+- super.messageReceived(msg);
+- }//messageReceived
+-
+-
+- public void memberAdded(Member member) {
+- if ( membership == null ) setupMembership();
+- boolean notify = false;
+- synchronized (membership) {
+- notify = Arrays.equals(domain,member.getDomain());
+- if ( notify ) notify = membership.memberAlive((MemberImpl)member);
+- }
+- if ( notify ) super.memberAdded(member);
+- }
+-
+- public void memberDisappeared(Member member) {
+- if ( membership == null ) setupMembership();
+- boolean notify = false;
+- synchronized (membership) {
+- notify = Arrays.equals(domain,member.getDomain());
+- membership.removeMember((MemberImpl)member);
+- }
+- if ( notify ) super.memberDisappeared(member);
+- }
+-
+- public boolean hasMembers() {
+- if ( membership == null ) setupMembership();
+- return membership.hasMembers();
+- }
+-
+- public Member[] getMembers() {
+- if ( membership == null ) setupMembership();
+- return membership.getMembers();
+- }
+-
+- public Member getMember(Member mbr) {
+- if ( membership == null ) setupMembership();
+- return membership.getMember(mbr);
+- }
+-
+- public Member getLocalMember(boolean incAlive) {
+- return super.getLocalMember(incAlive);
+- }
+-
+-
+- protected synchronized void setupMembership() {
+- if ( membership == null ) {
+- membership = new Membership((MemberImpl)super.getLocalMember(true));
+- }
+-
+- }
+-
+- public byte[] getDomain() {
+- return domain;
+- }
+-
+- public void setDomain(byte[] domain) {
+- this.domain = domain;
+- }
+-}
+Index: java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
+===================================================================
+---
java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java (revision
590752)
++++
java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java (working
copy)
+@@ -1,242 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- */
+-
+-package org.apache.catalina.tribes.group.interceptors;
+-
+-import java.util.Arrays;
+-import java.util.HashMap;
+-import java.util.Set;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.group.ChannelInterceptorBase;
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-
+-/**
+- *
+- * The fragmentation interceptor splits up large messages into smaller messages and
assembles them on the other end.
+- * This is very useful when you don't want large messages hogging the sending
sockets
+- * and smaller messages can make it through.
+- *
+- * <br><b>Configuration Options</b><br>
+- * OrderInteceptor.expire=<milliseconds> - how long do we keep the fragments in
memory and wait for the rest to arrive<b>default=60,000ms -> 60seconds</b>
+- * This setting is useful to avoid OutOfMemoryErrors<br>
+- * OrderInteceptor.maxSize=<max message size> - message size in bytes
<b>default=1024*100 (around a tenth of a MB)</b><br>
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class FragmentationInterceptor extends ChannelInterceptorBase {
+- private static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( FragmentationInterceptor.class );
+-
+- protected HashMap fragpieces = new HashMap();
+- private int maxSize = 1024*100;
+- private long expire = 1000 * 60; //one minute expiration
+- protected boolean deepclone = true;
+-
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- int size = msg.getMessage().getLength();
+- boolean frag = (size>maxSize) && okToProcess(msg.getOptions());
+- if ( frag ) {
+- frag(destination, msg, payload);
+- } else {
+- msg.getMessage().append(frag);
+- super.sendMessage(destination, msg, payload);
+- }
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- boolean isFrag =
XByteBuffer.toBoolean(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-1);
+- msg.getMessage().trim(1);
+- if ( isFrag ) {
+- defrag(msg);
+- } else {
+- super.messageReceived(msg);
+- }
+- }
+-
+-
+- public FragCollection getFragCollection(FragKey key, ChannelMessage msg) {
+- FragCollection coll = (FragCollection)fragpieces.get(key);
+- if ( coll == null ) {
+- synchronized (fragpieces) {
+- coll = (FragCollection)fragpieces.get(key);
+- if ( coll == null ) {
+- coll = new FragCollection(msg);
+- fragpieces.put(key, coll);
+- }
+- }
+- }
+- return coll;
+- }
+-
+- public void removeFragCollection(FragKey key) {
+- fragpieces.remove(key);
+- }
+-
+- public void defrag(ChannelMessage msg ) {
+- FragKey key = new FragKey(msg.getUniqueId());
+- FragCollection coll = getFragCollection(key,msg);
+- coll.addMessage((ChannelMessage)msg.deepclone());
+-
+- if ( coll.complete() ) {
+- removeFragCollection(key);
+- ChannelMessage complete = coll.assemble();
+- super.messageReceived(complete);
+-
+- }
+- }
+-
+- public void frag(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- int size = msg.getMessage().getLength();
+-
+- int count = ((size / maxSize )+(size%maxSize==0?0:1));
+- ChannelMessage[] messages = new ChannelMessage[count];
+- int remaining = size;
+- for ( int i=0; i<count; i++ ) {
+- ChannelMessage tmp = (ChannelMessage)msg.clone();
+- int offset = (i*maxSize);
+- int length = Math.min(remaining,maxSize);
+- tmp.getMessage().clear();
+- tmp.getMessage().append(msg.getMessage().getBytesDirect(),offset,length);
+- //add the msg nr
+- //tmp.getMessage().append(XByteBuffer.toBytes(i),0,4);
+- tmp.getMessage().append(i);
+- //add the total nr of messages
+- //tmp.getMessage().append(XByteBuffer.toBytes(count),0,4);
+- tmp.getMessage().append(count);
+- //add true as the frag flag
+- //byte[] flag = XByteBuffer.toBytes(true);
+- //tmp.getMessage().append(flag,0,flag.length);
+- tmp.getMessage().append(true);
+- messages[i] = tmp;
+- remaining -= length;
+-
+- }
+- for ( int i=0; i<messages.length; i++ ) {
+- super.sendMessage(destination,messages[i],payload);
+- }
+- }
+-
+- public void heartbeat() {
+- try {
+- Set set = fragpieces.keySet();
+- Object[] keys = set.toArray();
+- for ( int i=0; i<keys.length; i++ ) {
+- FragKey key = (FragKey)keys[i];
+- if ( key != null && key.expired(getExpire()) )
+- removeFragCollection(key);
+- }
+- }catch ( Exception x ) {
+- if ( log.isErrorEnabled() ) {
+- log.error("Unable to perform heartbeat clean up in the frag
interceptor",x);
+- }
+- }
+- super.heartbeat();
+- }
+-
+-
+-
+- public int getMaxSize() {
+- return maxSize;
+- }
+-
+- public long getExpire() {
+- return expire;
+- }
+-
+- public void setMaxSize(int maxSize) {
+- this.maxSize = maxSize;
+- }
+-
+- public void setExpire(long expire) {
+- this.expire = expire;
+- }
+-
+- public static class FragCollection {
+- private long received = System.currentTimeMillis();
+- private ChannelMessage msg;
+- private XByteBuffer[] frags;
+- public FragCollection(ChannelMessage msg) {
+- //get the total messages
+- int count =
XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);
+- frags = new XByteBuffer[count];
+- this.msg = msg;
+- }
+-
+- public void addMessage(ChannelMessage msg) {
+- //remove the total messages
+- msg.getMessage().trim(4);
+- //get the msg nr
+- int nr =
XByteBuffer.toInt(msg.getMessage().getBytesDirect(),msg.getMessage().getLength()-4);
+- //remove the msg nr
+- msg.getMessage().trim(4);
+- frags[nr] = msg.getMessage();
+-
+- }
+-
+- public boolean complete() {
+- boolean result = true;
+- for ( int i=0; (i<frags.length) && (result); i++ ) result =
(frags[i] != null);
+- return result;
+- }
+-
+- public ChannelMessage assemble() {
+- if ( !complete() ) throw new IllegalStateException("Fragments are
missing.");
+- int buffersize = 0;
+- for (int i=0; i<frags.length; i++ ) buffersize += frags[i].getLength();
+- XByteBuffer buf = new XByteBuffer(buffersize,false);
+- msg.setMessage(buf);
+- for ( int i=0; i<frags.length; i++ ) {
+-
msg.getMessage().append(frags[i].getBytesDirect(),0,frags[i].getLength());
+- }
+- return msg;
+- }
+-
+- public boolean expired(long expire) {
+- return (System.currentTimeMillis()-received)>expire;
+- }
+-
+-
+-
+- }
+-
+- public static class FragKey {
+- private byte[] uniqueId;
+- private long received = System.currentTimeMillis();
+- public FragKey(byte[] id ) {
+- this.uniqueId = id;
+- }
+- public int hashCode() {
+- return XByteBuffer.toInt(uniqueId,0);
+- }
+-
+- public boolean equals(Object o ) {
+- if ( o instanceof FragKey ) {
+- return Arrays.equals(uniqueId,((FragKey)o).uniqueId);
+- } else return false;
+-
+- }
+-
+- public boolean expired(long expire) {
+- return (System.currentTimeMillis()-received)>expire;
+- }
+-
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/RpcCallback.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/RpcCallback.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/RpcCallback.java (working copy)
+@@ -1,46 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * The RpcCallback interface is an interface for the Tribes channel to request a
+- * response object to a request that came in.
+- * @author not attributable
+- * @version 1.0
+- */
+-public interface RpcCallback {
+-
+- /**
+- *
+- * @param msg Serializable
+- * @return Serializable - null if no reply should be sent
+- */
+- public Serializable replyRequest(Serializable msg, Member sender);
+-
+- /**
+- * If the reply has already been sent to the requesting thread,
+- * the rpc callback can handle any data that comes in after the fact.
+- * @param msg Serializable
+- * @param sender Member
+- */
+- public void leftOver(Serializable msg, Member sender);
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/ChannelInterceptorBase.java (working copy)
+@@ -1,172 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelInterceptor;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * Abstract class for the interceptor base class.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public abstract class ChannelInterceptorBase implements ChannelInterceptor {
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(
+- ChannelInterceptorBase.class);
+-
+- private ChannelInterceptor next;
+- private ChannelInterceptor previous;
+- //default value, always process
+- protected int optionFlag = 0;
+-
+- public ChannelInterceptorBase() {
+-
+- }
+-
+- public boolean okToProcess(int messageFlags) {
+- if (this.optionFlag == 0 ) return true;
+- return ((optionFlag&messageFlags) == optionFlag);
+- }
+-
+- public final void setNext(ChannelInterceptor next) {
+- this.next = next;
+- }
+-
+- public final ChannelInterceptor getNext() {
+- return next;
+- }
+-
+- public final void setPrevious(ChannelInterceptor previous) {
+- this.previous = previous;
+- }
+-
+- public void setOptionFlag(int optionFlag) {
+- this.optionFlag = optionFlag;
+- }
+-
+- public final ChannelInterceptor getPrevious() {
+- return previous;
+- }
+-
+- public int getOptionFlag() {
+- return optionFlag;
+- }
+-
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws
+- ChannelException {
+- if (getNext() != null) getNext().sendMessage(destination, msg, payload);
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- if (getPrevious() != null) getPrevious().messageReceived(msg);
+- }
+-
+- public boolean accept(ChannelMessage msg) {
+- return true;
+- }
+-
+- public void memberAdded(Member member) {
+- //notify upwards
+- if (getPrevious() != null) getPrevious().memberAdded(member);
+- }
+-
+- public void memberDisappeared(Member member) {
+- //notify upwards
+- if (getPrevious() != null) getPrevious().memberDisappeared(member);
+- }
+-
+- public void heartbeat() {
+- if (getNext() != null) getNext().heartbeat();
+- }
+-
+- /**
+- * has members
+- */
+- public boolean hasMembers() {
+- if ( getNext()!=null )return getNext().hasMembers();
+- else return false;
+- }
+-
+- /**
+- * Get all current cluster members
+- * @return all members or empty array
+- */
+- public Member[] getMembers() {
+- if ( getNext()!=null ) return getNext().getMembers();
+- else return null;
+- }
+-
+- /**
+- *
+- * @param mbr Member
+- * @return Member
+- */
+- public Member getMember(Member mbr) {
+- if ( getNext()!=null) return getNext().getMember(mbr);
+- else return null;
+- }
+-
+- /**
+- * Return the member that represents this node.
+- *
+- * @return Member
+- */
+- public Member getLocalMember(boolean incAlive) {
+- if ( getNext()!=null ) return getNext().getLocalMember(incAlive);
+- else return null;
+- }
+-
+- /**
+- * Starts up the channel. This can be called multiple times for individual services
to start
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will start all services <BR>
+- * MBR_RX_SEQ - starts the membership receiver <BR>
+- * MBR_TX_SEQ - starts the membership broadcaster <BR>
+- * SND_TX_SEQ - starts the replication transmitter<BR>
+- * SND_RX_SEQ - starts the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- */
+- public void start(int svc) throws ChannelException {
+- if ( getNext()!=null ) getNext().start(svc);
+- }
+-
+- /**
+- * Shuts down the channel. This can be called multiple times for individual services
to shutdown
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will shutdown all services <BR>
+- * MBR_RX_SEQ - stops the membership receiver <BR>
+- * MBR_TX_SEQ - stops the membership broadcaster <BR>
+- * SND_TX_SEQ - stops the replication transmitter<BR>
+- * SND_RX_SEQ - stops the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- */
+- public void stop(int svc) throws ChannelException {
+- if (getNext() != null) getNext().stop(svc);
+- }
+-
+- public void fireInterceptorEvent(InterceptorEvent event) {
+- //empty operation
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/group/GroupChannel.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/GroupChannel.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/GroupChannel.java (working copy)
+@@ -1,673 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-
+-import java.io.Serializable;
+-import java.util.ArrayList;
+-import java.util.Iterator;
+-
+-import org.apache.catalina.tribes.ByteMessage;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelInterceptor;
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.ChannelReceiver;
+-import org.apache.catalina.tribes.ChannelSender;
+-import org.apache.catalina.tribes.ErrorHandler;
+-import org.apache.catalina.tribes.ManagedChannel;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipListener;
+-import org.apache.catalina.tribes.MembershipService;
+-import org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor;
+-import org.apache.catalina.tribes.io.ChannelData;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-import org.apache.catalina.tribes.UniqueId;
+-import org.apache.catalina.tribes.Heartbeat;
+-import org.apache.catalina.tribes.io.BufferPool;
+-import org.apache.catalina.tribes.RemoteProcessException;
+-import org.apache.catalina.tribes.util.Logs;
+-import org.apache.catalina.tribes.util.Arrays;
+-
+-/**
+- * The default implementation of a Channel.<br>
+- * The GroupChannel manages the replication channel. It coordinates
+- * message being sent and received with membership announcements.
+- * The channel has an chain of interceptors that can modify the message or perform other
logic.<br>
+- * It manages a complete group, both membership and replication.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public class GroupChannel extends ChannelInterceptorBase implements ManagedChannel {
+- /**
+- * Flag to determine if the channel manages its own heartbeat
+- * If set to true, the channel will start a local thread for the heart beat.
+- */
+- protected boolean heartbeat = true;
+- /**
+- * If <code>heartbeat == true</code> then how often do we want this
+- * heartbeat to run. default is one minute
+- */
+- protected long heartbeatSleeptime = 5*1000;//every 5 seconds
+-
+- /**
+- * Internal heartbeat thread
+- */
+- protected HeartbeatThread hbthread = null;
+-
+- /**
+- * The <code>ChannelCoordinator</code> coordinates the bottom layer
components:<br>
+- * - MembershipService<br>
+- * - ChannelSender <br>
+- * - ChannelReceiver<br>
+- */
+- protected ChannelCoordinator coordinator = new ChannelCoordinator();
+-
+- /**
+- * The first interceptor in the inteceptor stack.
+- * The interceptors are chained in a linked list, so we only need a reference to
the
+- * first one
+- */
+- protected ChannelInterceptor interceptors = null;
+-
+- /**
+- * A list of membership listeners that subscribe to membership announcements
+- */
+- protected ArrayList membershipListeners = new ArrayList();
+-
+- /**
+- * A list of channel listeners that subscribe to incoming messages
+- */
+- protected ArrayList channelListeners = new ArrayList();
+-
+- /**
+- * If set to true, the GroupChannel will check to make sure that
+- */
+- protected boolean optionCheck = false;
+-
+- /**
+- * Creates a GroupChannel. This constructor will also
+- * add the first interceptor in the GroupChannel.<br>
+- * The first interceptor is always the channel itself.
+- */
+- public GroupChannel() {
+- addInterceptor(this);
+- }
+-
+-
+- /**
+- * Adds an interceptor to the stack for message processing<br>
+- * Interceptors are ordered in the way they are added.<br>
+- * <code>channel.addInterceptor(A);</code><br>
+- * <code>channel.addInterceptor(C);</code><br>
+- * <code>channel.addInterceptor(B);</code><br>
+- * Will result in a interceptor stack like this:<br>
+- * <code>A -> C -> B</code><br>
+- * The complete stack will look like this:<br>
+- * <code>Channel -> A -> C -> B ->
ChannelCoordinator</code><br>
+- * @param interceptor ChannelInterceptorBase
+- */
+- public void addInterceptor(ChannelInterceptor interceptor) {
+- if ( interceptors == null ) {
+- interceptors = interceptor;
+- interceptors.setNext(coordinator);
+- interceptors.setPrevious(null);
+- coordinator.setPrevious(interceptors);
+- } else {
+- ChannelInterceptor last = interceptors;
+- while ( last.getNext() != coordinator ) {
+- last = last.getNext();
+- }
+- last.setNext(interceptor);
+- interceptor.setNext(coordinator);
+- interceptor.setPrevious(last);
+- coordinator.setPrevious(interceptor);
+- }
+- }
+-
+- /**
+- * Sends a heartbeat through the interceptor stack.<br>
+- * Invoke this method from the application on a periodic basis if
+- * you have turned off internal heartbeats
<code>channel.setHeartbeat(false)</code>
+- */
+- public void heartbeat() {
+- super.heartbeat();
+- Iterator i = membershipListeners.iterator();
+- while ( i.hasNext() ) {
+- Object o = i.next();
+- if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();
+- }
+- i = channelListeners.iterator();
+- while ( i.hasNext() ) {
+- Object o = i.next();
+- if ( o instanceof Heartbeat ) ((Heartbeat)o).heartbeat();
+- }
+-
+- }
+-
+-
+- /**
+- * Send a message to the destinations specified
+- * @param destination Member[] - destination.length > 1
+- * @param msg Serializable - the message to send
+- * @param options int - sender options, options can trigger guarantee levels and
different interceptors to
+- * react to the message see class documentation for the
<code>Channel</code> object.<br>
+- * @return UniqueId - the unique Id that was assigned to this message
+- * @throws ChannelException - if an error occurs processing the message
+- * @see org.apache.catalina.tribes.Channel
+- */
+- public UniqueId send(Member[] destination, Serializable msg, int options) throws
ChannelException {
+- return send(destination,msg,options,null);
+- }
+-
+- /**
+- *
+- * @param destination Member[] - destination.length > 1
+- * @param msg Serializable - the message to send
+- * @param options int - sender options, options can trigger guarantee levels and
different interceptors to
+- * react to the message see class documentation for the
<code>Channel</code> object.<br>
+- * @param handler - callback object for error handling and completion notification,
used when a message is
+- * sent asynchronously using the
<code>Channel.SEND_OPTIONS_ASYNCHRONOUS</code> flag enabled.
+- * @return UniqueId - the unique Id that was assigned to this message
+- * @throws ChannelException - if an error occurs processing the message
+- * @see org.apache.catalina.tribes.Channel
+- */
+- public UniqueId send(Member[] destination, Serializable msg, int options,
ErrorHandler handler) throws ChannelException {
+- if ( msg == null ) throw new ChannelException("Cant send a NULL
message");
+- XByteBuffer buffer = null;
+- try {
+- if ( destination == null || destination.length == 0) throw new
ChannelException("No destination given");
+- ChannelData data = new ChannelData(true);//generates a unique Id
+- data.setAddress(getLocalMember(false));
+- data.setTimestamp(System.currentTimeMillis());
+- byte[] b = null;
+- if ( msg instanceof ByteMessage ){
+- b = ((ByteMessage)msg).getMessage();
+- options = options | SEND_OPTIONS_BYTE_MESSAGE;
+- } else {
+- b = XByteBuffer.serialize(msg);
+- options = options & (~SEND_OPTIONS_BYTE_MESSAGE);
+- }
+- data.setOptions(options);
+- //XByteBuffer buffer = new XByteBuffer(b.length+128,false);
+- buffer = BufferPool.getBufferPool().getBuffer(b.length+128, false);
+- buffer.append(b,0,b.length);
+- data.setMessage(buffer);
+- InterceptorPayload payload = null;
+- if ( handler != null ) {
+- payload = new InterceptorPayload();
+- payload.setErrorHandler(handler);
+- }
+- getFirstInterceptor().sendMessage(destination, data, payload);
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("GroupChannel - Sent msg:" + new
UniqueId(data.getUniqueId()) + " at " +new
java.sql.Timestamp(System.currentTimeMillis())+ " to
"+Arrays.toNameString(destination));
+- Logs.MESSAGES.trace("GroupChannel - Send Message:" + new
UniqueId(data.getUniqueId()) + " is " +msg);
+- }
+-
+- return new UniqueId(data.getUniqueId());
+- }catch ( Exception x ) {
+- if ( x instanceof ChannelException ) throw (ChannelException)x;
+- throw new ChannelException(x);
+- } finally {
+- if ( buffer != null ) BufferPool.getBufferPool().returnBuffer(buffer);
+- }
+- }
+-
+-
+- /**
+- * Callback from the interceptor stack. <br>
+- * When a message is received from a remote node, this method will be invoked by
+- * the previous interceptor.<br>
+- * This method can also be used to send a message to other components within the
same application,
+- * but its an extreme case, and you're probably better off doing that logic
between the applications itself.
+- * @param msg ChannelMessage
+- */
+- public void messageReceived(ChannelMessage msg) {
+- if ( msg == null ) return;
+- try {
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("GroupChannel - Received msg:" + new
UniqueId(msg.getUniqueId()) + " at " +new
java.sql.Timestamp(System.currentTimeMillis())+ " from
"+msg.getAddress().getName());
+- }
+-
+- Serializable fwd = null;
+- if ( (msg.getOptions() & SEND_OPTIONS_BYTE_MESSAGE) ==
SEND_OPTIONS_BYTE_MESSAGE ) {
+- fwd = new ByteMessage(msg.getMessage().getBytes());
+- } else {
+- try {
+- fwd = XByteBuffer.deserialize(msg.getMessage().getBytesDirect(), 0,
msg.getMessage().getLength());
+- }catch (Exception sx) {
+- log.error("Unable to deserialize message:"+msg,sx);
+- return;
+- }
+- }
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("GroupChannel - Receive Message:" + new
UniqueId(msg.getUniqueId()) + " is " +fwd);
+- }
+-
+- //get the actual member with the correct alive time
+- Member source = msg.getAddress();
+- boolean rx = false;
+- boolean delivered = false;
+- for ( int i=0; i<channelListeners.size(); i++ ) {
+- ChannelListener channelListener =
(ChannelListener)channelListeners.get(i);
+- if (channelListener != null && channelListener.accept(fwd,
source)) {
+- channelListener.messageReceived(fwd, source);
+- delivered = true;
+- //if the message was accepted by an RPC channel, that channel
+- //is responsible for returning the reply, otherwise we send an
absence reply
+- if ( channelListener instanceof RpcChannel ) rx = true;
+- }
+- }//for
+- if ((!rx) && (fwd instanceof RpcMessage)) {
+- //if we have a message that requires a response,
+- //but none was given, send back an immediate one
+- sendNoRpcChannelReply((RpcMessage)fwd,source);
+- }
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("GroupChannel
delivered["+delivered+"] id:"+new UniqueId(msg.getUniqueId()));
+- }
+-
+- } catch ( Exception x ) {
+- //this could be the channel listener throwing an exception, we should log it
+- //as a warning.
+- if ( log.isWarnEnabled() ) log.warn("Error receiving
message:",x);
+- throw new RemoteProcessException("Exception:"+x.getMessage(),x);
+- }
+- }
+-
+- /**
+- * Sends a <code>NoRpcChannelReply</code> message to a member<br>
+- * This method gets invoked by the channel if a RPC message comes in
+- * and no channel listener accepts the message. This avoids timeout
+- * @param msg RpcMessage
+- * @param destination Member - the destination for the reply
+- */
+- protected void sendNoRpcChannelReply(RpcMessage msg, Member destination) {
+- try {
+- //avoid circular loop
+- if ( msg instanceof RpcMessage.NoRpcChannelReply) return;
+- RpcMessage.NoRpcChannelReply reply = new
RpcMessage.NoRpcChannelReply(msg.rpcId,msg.uuid);
+- send(new Member[]{destination},reply,Channel.SEND_OPTIONS_ASYNCHRONOUS);
+- } catch ( Exception x ) {
+- log.error("Unable to find rpc channel, failed to send
NoRpcChannelReply.",x);
+- }
+- }
+-
+- /**
+- * memberAdded gets invoked by the interceptor below the channel
+- * and the channel will broadcast it to the membership listeners
+- * @param member Member - the new member
+- */
+- public void memberAdded(Member member) {
+- //notify upwards
+- for (int i=0; i<membershipListeners.size(); i++ ) {
+- MembershipListener membershipListener =
(MembershipListener)membershipListeners.get(i);
+- if (membershipListener != null) membershipListener.memberAdded(member);
+- }
+- }
+-
+- /**
+- * memberDisappeared gets invoked by the interceptor below the channel
+- * and the channel will broadcast it to the membership listeners
+- * @param member Member - the member that left or crashed
+- */
+- public void memberDisappeared(Member member) {
+- //notify upwards
+- for (int i=0; i<membershipListeners.size(); i++ ) {
+- MembershipListener membershipListener =
(MembershipListener)membershipListeners.get(i);
+- if (membershipListener != null)
membershipListener.memberDisappeared(member);
+- }
+- }
+-
+- /**
+- * Sets up the default implementation interceptor stack
+- * if no interceptors have been added
+- * @throws ChannelException
+- */
+- protected synchronized void setupDefaultStack() throws ChannelException {
+-
+- if ( getFirstInterceptor() != null &&
+- ((getFirstInterceptor().getNext() instanceof ChannelCoordinator))) {
+- ChannelInterceptor interceptor = null;
+- Class clazz = null;
+- try {
+- clazz =
Class.forName("org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor",
+- true,GroupChannel.class.getClassLoader());
+- clazz.newInstance();
+- } catch ( Throwable x ) {
+- clazz = MessageDispatchInterceptor.class;
+- }//catch
+- try {
+- interceptor = (ChannelInterceptor) clazz.newInstance();
+- } catch (Exception x) {
+- throw new ChannelException("Unable to add
MessageDispatchInterceptor to interceptor chain.",x);
+- }
+- this.addInterceptor(interceptor);
+- }
+- }
+-
+- /**
+- * Validates the option flags that each interceptor is using and reports
+- * an error if two interceptor share the same flag.
+- * @throws ChannelException
+- */
+- protected void checkOptionFlags() throws ChannelException {
+- StringBuffer conflicts = new StringBuffer();
+- ChannelInterceptor first = interceptors;
+- while ( first != null ) {
+- int flag = first.getOptionFlag();
+- if ( flag != 0 ) {
+- ChannelInterceptor next = first.getNext();
+- while ( next != null ) {
+- int nflag = next.getOptionFlag();
+- if (nflag!=0 && (((flag & nflag) == flag ) || ((flag
& nflag) == nflag)) ) {
+- conflicts.append("[");
+- conflicts.append(first.getClass().getName());
+- conflicts.append(":");
+- conflicts.append(flag);
+- conflicts.append(" == ");
+- conflicts.append(next.getClass().getName());
+- conflicts.append(":");
+- conflicts.append(nflag);
+- conflicts.append("] ");
+- }//end if
+- next = next.getNext();
+- }//while
+- }//end if
+- first = first.getNext();
+- }//while
+- if ( conflicts.length() > 0 ) throw new ChannelException("Interceptor
option flag conflict: "+conflicts.toString());
+-
+- }
+-
+- /**
+- * Starts the channel
+- * @param svc int - what service to start
+- * @throws ChannelException
+- * @see org.apache.catalina.tribes.Channel#start(int)
+- */
+- public synchronized void start(int svc) throws ChannelException {
+- setupDefaultStack();
+- if (optionCheck) checkOptionFlags();
+- super.start(svc);
+- if ( hbthread == null && heartbeat ) {
+- hbthread = new HeartbeatThread(this,heartbeatSleeptime);
+- hbthread.start();
+- }
+- }
+-
+- /**
+- * Stops the channel
+- * @param svc int
+- * @throws ChannelException
+- * @see org.apache.catalina.tribes.Channel#stop(int)
+- */
+- public synchronized void stop(int svc) throws ChannelException {
+- if (hbthread != null) {
+- hbthread.stopHeartbeat();
+- hbthread = null;
+- }
+- super.stop(svc);
+- }
+-
+- /**
+- * Returns the first interceptor of the stack. Useful for traversal.
+- * @return ChannelInterceptor
+- */
+- public ChannelInterceptor getFirstInterceptor() {
+- if (interceptors != null) return interceptors;
+- else return coordinator;
+- }
+-
+- /**
+- * Returns the channel receiver component
+- * @return ChannelReceiver
+- */
+- public ChannelReceiver getChannelReceiver() {
+- return coordinator.getClusterReceiver();
+- }
+-
+- /**
+- * Returns the channel sender component
+- * @return ChannelSender
+- */
+- public ChannelSender getChannelSender() {
+- return coordinator.getClusterSender();
+- }
+-
+- /**
+- * Returns the membership service component
+- * @return MembershipService
+- */
+- public MembershipService getMembershipService() {
+- return coordinator.getMembershipService();
+- }
+-
+- /**
+- * Sets the channel receiver component
+- * @param clusterReceiver ChannelReceiver
+- */
+- public void setChannelReceiver(ChannelReceiver clusterReceiver) {
+- coordinator.setClusterReceiver(clusterReceiver);
+- }
+-
+- /**
+- * Sets the channel sender component
+- * @param clusterSender ChannelSender
+- */
+- public void setChannelSender(ChannelSender clusterSender) {
+- coordinator.setClusterSender(clusterSender);
+- }
+-
+- /**
+- * Sets the membership component
+- * @param membershipService MembershipService
+- */
+- public void setMembershipService(MembershipService membershipService) {
+- coordinator.setMembershipService(membershipService);
+- }
+-
+- /**
+- * Adds a membership listener to the channel.<br>
+- * Membership listeners are uniquely identified using the equals(Object) method
+- * @param membershipListener MembershipListener
+- */
+- public void addMembershipListener(MembershipListener membershipListener) {
+- if (!this.membershipListeners.contains(membershipListener) )
+- this.membershipListeners.add(membershipListener);
+- }
+-
+- /**
+- * Removes a membership listener from the channel.<br>
+- * Membership listeners are uniquely identified using the equals(Object) method
+- * @param membershipListener MembershipListener
+- */
+-
+- public void removeMembershipListener(MembershipListener membershipListener) {
+- membershipListeners.remove(membershipListener);
+- }
+-
+- /**
+- * Adds a channel listener to the channel.<br>
+- * Channel listeners are uniquely identified using the equals(Object) method
+- * @param channelListener ChannelListener
+- */
+- public void addChannelListener(ChannelListener channelListener) {
+- if (!this.channelListeners.contains(channelListener) ) {
+- this.channelListeners.add(channelListener);
+- } else {
+- throw new IllegalArgumentException("Listener already
exists:"+channelListener+"["+channelListener.getClass().getName()+"]");
+- }
+- }
+-
+- /**
+- *
+- * Removes a channel listener from the channel.<br>
+- * Channel listeners are uniquely identified using the equals(Object) method
+- * @param channelListener ChannelListener
+- */
+- public void removeChannelListener(ChannelListener channelListener) {
+- channelListeners.remove(channelListener);
+- }
+-
+- /**
+- * Returns an iterator of all the interceptors in this stack
+- * @return Iterator
+- */
+- public Iterator getInterceptors() {
+- return new InterceptorIterator(this.getNext(),this.coordinator);
+- }
+-
+- /**
+- * Enables/disables the option check<br>
+- * Setting this to true, will make the GroupChannel perform a conflict check
+- * on the interceptors. If two interceptors are using the same option flag
+- * and throw an error upon start.
+- * @param optionCheck boolean
+- */
+- public void setOptionCheck(boolean optionCheck) {
+- this.optionCheck = optionCheck;
+- }
+-
+- /**
+- * Configure local heartbeat sleep time<br>
+- * Only used when <code>getHeartbeat()==true</code>
+- * @param heartbeatSleeptime long - time in milliseconds to sleep between
heartbeats
+- */
+- public void setHeartbeatSleeptime(long heartbeatSleeptime) {
+- this.heartbeatSleeptime = heartbeatSleeptime;
+- }
+-
+- /**
+- * Enables or disables local heartbeat.
+- * if <code>setHeartbeat(true)</code> is invoked then the channel will
start an internal
+- * thread to invoke <code>Channel.heartbeat()</code> every
<code>getHeartbeatSleeptime</code> milliseconds
+- * @param heartbeat boolean
+- */
+- public void setHeartbeat(boolean heartbeat) {
+- this.heartbeat = heartbeat;
+- }
+-
+- /**
+- * @see #setOptionCheck(boolean)
+- * @return boolean
+- */
+- public boolean getOptionCheck() {
+- return optionCheck;
+- }
+-
+- /**
+- * @see #setHeartbeat(boolean)
+- * @return boolean
+- */
+- public boolean getHeartbeat() {
+- return heartbeat;
+- }
+-
+- /**
+- * Returns the sleep time in milliseconds that the internal heartbeat will
+- * sleep in between invokations of <code>Channel.heartbeat()</code>
+- * @return long
+- */
+- public long getHeartbeatSleeptime() {
+- return heartbeatSleeptime;
+- }
+-
+- /**
+- *
+- * <p>Title: Interceptor Iterator</p>
+- *
+- * <p>Description: An iterator to loop through the interceptors in a
channel</p>
+- *
+- * @version 1.0
+- */
+- public static class InterceptorIterator implements Iterator {
+- private ChannelInterceptor end;
+- private ChannelInterceptor start;
+- public InterceptorIterator(ChannelInterceptor start, ChannelInterceptor end) {
+- this.end = end;
+- this.start = start;
+- }
+-
+- public boolean hasNext() {
+- return start!=null && start != end;
+- }
+-
+- public Object next() {
+- Object result = null;
+- if ( hasNext() ) {
+- result = start;
+- start = start.getNext();
+- }
+- return result;
+- }
+-
+- public void remove() {
+- //empty operation
+- }
+- }
+-
+- /**
+- *
+- * <p>Title: Internal heartbeat thread</p>
+- *
+- * <p>Description: if <code>Channel.getHeartbeat()==true</code>
then a thread of this class
+- * is created</p>
+- *
+- * @version 1.0
+- */
+- public static class HeartbeatThread extends Thread {
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(HeartbeatThread.class);
+- protected static int counter = 1;
+- protected static synchronized int inc() {
+- return counter++;
+- }
+-
+- protected boolean doRun = true;
+- protected GroupChannel channel;
+- protected long sleepTime;
+- public HeartbeatThread(GroupChannel channel, long sleepTime) {
+- super();
+- this.setPriority(MIN_PRIORITY);
+- setName("GroupChannel-Heartbeat-"+inc());
+- setDaemon(true);
+- this.channel = channel;
+- this.sleepTime = sleepTime;
+- }
+- public void stopHeartbeat() {
+- doRun = false;
+- interrupt();
+- }
+-
+- public void run() {
+- while (doRun) {
+- try {
+- Thread.sleep(sleepTime);
+- channel.heartbeat();
+- } catch ( InterruptedException x ) {
+- interrupted();
+- } catch ( Exception x ) {
+- log.error("Unable to send heartbeat through Tribes interceptor
stack. Will try to sleep again.",x);
+- }//catch
+- }//while
+- }//run
+- }//HeartbeatThread
+-
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/group/InterceptorPayload.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/InterceptorPayload.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/InterceptorPayload.java (working copy)
+@@ -1,35 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import org.apache.catalina.tribes.ErrorHandler;
+-
+-/**
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class InterceptorPayload {
+- private ErrorHandler errorHandler;
+-
+- public ErrorHandler getErrorHandler() {
+- return errorHandler;
+- }
+-
+- public void setErrorHandler(ErrorHandler errorHandler) {
+- this.errorHandler = errorHandler;
+- }
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/RpcMessage.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/RpcMessage.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/RpcMessage.java (working copy)
+@@ -1,115 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.group;
+-
+-import java.io.ObjectInput;
+-import java.io.Serializable;
+-import java.io.Externalizable;
+-import java.io.IOException;
+-import java.io.ObjectOutput;
+-import org.apache.catalina.tribes.util.Arrays;
+-
+-/**
+- * <p>Title: </p>
+- *
+- * <p>Description: </p>
+- *
+- * <p>Company: </p>
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class RpcMessage implements Externalizable {
+-
+- protected Serializable message;
+- protected byte[] uuid;
+- protected byte[] rpcId;
+- protected boolean reply = false;
+-
+- public RpcMessage() {
+- //for serialization
+- }
+-
+- public RpcMessage(byte[] rpcId, byte[] uuid, Serializable message) {
+- this.rpcId = rpcId;
+- this.uuid = uuid;
+- this.message = message;
+- }
+-
+- public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException
{
+- reply = in.readBoolean();
+- int length = in.readInt();
+- uuid = new byte[length];
+- in.read(uuid, 0, length);
+- length = in.readInt();
+- rpcId = new byte[length];
+- in.read(rpcId, 0, length);
+- message = (Serializable)in.readObject();
+- }
+-
+- public void writeExternal(ObjectOutput out) throws IOException {
+- out.writeBoolean(reply);
+- out.writeInt(uuid.length);
+- out.write(uuid, 0, uuid.length);
+- out.writeInt(rpcId.length);
+- out.write(rpcId, 0, rpcId.length);
+- out.writeObject(message);
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("RpcMessage[");
+- buf.append(super.toString());
+- buf.append("] rpcId=");
+- buf.append(Arrays.toString(rpcId));
+- buf.append("; uuid=");
+- buf.append(Arrays.toString(uuid));
+- buf.append("; msg=");
+- buf.append(message);
+- return buf.toString();
+- }
+-
+- public static class NoRpcChannelReply extends RpcMessage {
+- public NoRpcChannelReply() {
+-
+- }
+-
+- public NoRpcChannelReply(byte[] rpcid, byte[] uuid) {
+- super(rpcid,uuid,null);
+- reply = true;
+- }
+-
+- public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
+- reply = true;
+- int length = in.readInt();
+- uuid = new byte[length];
+- in.read(uuid, 0, length);
+- length = in.readInt();
+- rpcId = new byte[length];
+- in.read(rpcId, 0, length);
+- }
+-
+- public void writeExternal(ObjectOutput out) throws IOException {
+- out.writeInt(uuid.length);
+- out.write(uuid, 0, uuid.length);
+- out.writeInt(rpcId.length);
+- out.write(rpcId, 0, rpcId.length);
+- }
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/group/AbsoluteOrder.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/AbsoluteOrder.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/AbsoluteOrder.java (working copy)
+@@ -1,116 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import org.apache.catalina.tribes.Member;
+-import java.util.Comparator;
+-import java.util.Arrays;
+-
+-/**
+- * <p>Title: Membership - Absolute Order</p>
+- *
+- * <p>Description: A simple, yet agreeable and efficient way of ordering
members</p>
+- * <p>
+- * Ordering members can serve as a basis for electing a leader or coordinating
efforts.<br>
+- * This is stinky simple, it works on the basis of the
<code>Member</code> interface
+- * and orders members in the following format:
+- *
+- * <ol>
+- * <li>IP comparison - byte by byte, lower byte higher rank</li>
+- * <li>IPv4 addresses rank higher than IPv6, ie the lesser number of bytes,
the higher rank</li>
+- * <li>Port comparison - lower port, higher rank</li>
+- * <li>UniqueId comparison- byte by byte, lower byte higher rank</li>
+- * </ol>
+- *
+- * </p>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- * @see org.apache.catalina.tribes.Member
+- */
+-public class AbsoluteOrder {
+- public static final AbsoluteComparator comp = new AbsoluteComparator();
+-
+- protected AbsoluteOrder() {
+- super();
+- }
+-
+-
+-
+- public static void absoluteOrder(Member[] members) {
+- if ( members == null || members.length == 0 ) return;
+- Arrays.sort(members,comp);
+- }
+-
+-
+- public static class AbsoluteComparator implements Comparator {
+- public int compare(Object o1, Object o2) {
+- if ( !((o1 instanceof Member) && (o2 instanceof Member)) ) return
0;
+- return compareMembers((Member)o1,(Member)o2);
+- }
+-
+- public int compareMembers(Member m1, Member m2) {
+- int result = compareIps(m1,m2);
+- if ( result == 0 ) result = comparePorts(m1,m2);
+- if ( result == 0 ) result = compareIds(m1,m2);
+- return result;
+- }
+-
+- public int compareIps(Member m1, Member m2) {
+- return compareBytes(m1.getHost(),m2.getHost());
+- }
+-
+- public int comparePorts(Member m1, Member m2) {
+- return compareInts(m1.getPort(),m2.getPort());
+- }
+-
+- public int compareIds(Member m1, Member m2) {
+- return compareBytes(m1.getUniqueId(),m2.getUniqueId());
+- }
+-
+- protected int compareBytes(byte[] d1, byte[] d2) {
+- int result = 0;
+- if ( d1.length == d2.length ) {
+- for (int i=0; (result==0) && (i<d1.length); i++) {
+- result = compareBytes(d1[i],d2[i]);
+- }
+- } else if ( d1.length < d2.length) {
+- result = -1;
+- } else {
+- result = 1;
+- }
+- return result;
+- }
+-
+- protected int compareBytes(byte b1, byte b2) {
+- return compareInts((int)b1,(int)b2);
+- }
+-
+- protected int compareInts(int b1, int b2) {
+- int result = 0;
+- if ( b1 == b2 ) {
+-
+- } else if ( b1 < b2) {
+- result = -1;
+- } else {
+- result = 1;
+- }
+- return result;
+- }
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/group/ChannelCoordinator.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/ChannelCoordinator.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/ChannelCoordinator.java (working copy)
+@@ -1,316 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.ChannelReceiver;
+-import org.apache.catalina.tribes.ChannelSender;
+-
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.MembershipService;
+-import org.apache.catalina.tribes.MessageListener;
+-import org.apache.catalina.tribes.transport.SenderState;
+-import org.apache.catalina.tribes.transport.ReplicationTransmitter;
+-import org.apache.catalina.tribes.membership.McastService;
+-import org.apache.catalina.tribes.transport.nio.NioReceiver;
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.util.Logs;
+-import org.apache.catalina.tribes.UniqueId;
+-import org.apache.catalina.tribes.util.Arrays;
+-
+-
+-/**
+- * The channel coordinator object coordinates the membership service,
+- * the sender and the receiver.
+- * This is the last interceptor in the chain.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public class ChannelCoordinator extends ChannelInterceptorBase implements
MessageListener {
+- private ChannelReceiver clusterReceiver = new NioReceiver();
+- private ChannelSender clusterSender = new ReplicationTransmitter();
+- private MembershipService membershipService = new McastService();
+-
+- //override optionflag
+- protected int optionFlag =
Channel.SEND_OPTIONS_BYTE_MESSAGE|Channel.SEND_OPTIONS_USE_ACK|Channel.SEND_OPTIONS_SYNCHRONIZED_ACK;
+- public int getOptionFlag() {return optionFlag;}
+- public void setOptionFlag(int flag) {optionFlag=flag;}
+-
+- private int startLevel = 0;
+-
+- public ChannelCoordinator() {
+-
+- }
+-
+- public ChannelCoordinator(ChannelReceiver receiver,
+- ChannelSender sender,
+- MembershipService service) {
+- this();
+- this.setClusterReceiver(receiver);
+- this.setClusterSender(sender);
+- this.setMembershipService(service);
+- }
+-
+- /**
+- * Send a message to one or more members in the cluster
+- * @param destination Member[] - the destinations, null or zero length means all
+- * @param msg ClusterMessage - the message to send
+- * @param options int - sender options, see class documentation
+- * @return ClusterMessage[] - the replies from the members, if any.
+- */
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException {
+- if ( destination == null ) destination = membershipService.getMembers();
+- clusterSender.sendMessage(msg,destination);
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("ChannelCoordinator - Sent msg:" + new
UniqueId(msg.getUniqueId()) + " at " +new
java.sql.Timestamp(System.currentTimeMillis())+ " to
"+Arrays.toNameString(destination));
+- }
+- }
+-
+-
+- /**
+- * Starts up the channel. This can be called multiple times for individual services
to start
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will start all services <BR>
+- * MBR_RX_SEQ - starts the membership receiver <BR>
+- * MBR_TX_SEQ - starts the membership broadcaster <BR>
+- * SND_TX_SEQ - starts the replication transmitter<BR>
+- * SND_RX_SEQ - starts the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- */
+- public void start(int svc) throws ChannelException {
+- this.internalStart(svc);
+- }
+-
+- /**
+- * Shuts down the channel. This can be called multiple times for individual services
to shutdown
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will shutdown all services <BR>
+- * MBR_RX_SEQ - stops the membership receiver <BR>
+- * MBR_TX_SEQ - stops the membership broadcaster <BR>
+- * SND_TX_SEQ - stops the replication transmitter<BR>
+- * SND_RX_SEQ - stops the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- */
+- public void stop(int svc) throws ChannelException {
+- this.internalStop(svc);
+- }
+-
+-
+- /**
+- * Starts up the channel. This can be called multiple times for individual services
to start
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will start all services <BR>
+- * MBR_RX_SEQ - starts the membership receiver <BR>
+- * MBR_TX_SEQ - starts the membership broadcaster <BR>
+- * SND_TX_SEQ - starts the replication transmitter<BR>
+- * SND_RX_SEQ - starts the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- */
+- protected synchronized void internalStart(int svc) throws ChannelException {
+- try {
+- boolean valid = false;
+- //make sure we don't pass down any flags that are unrelated to the
bottom layer
+- svc = svc & Channel.DEFAULT;
+-
+- if (startLevel == Channel.DEFAULT) return; //we have already started up all
components
+- if (svc == 0 ) return;//nothing to start
+-
+- if (svc == (svc & startLevel)) throw new ChannelException("Channel
already started for level:"+svc);
+-
+- //must start the receiver first so that we can coordinate the port it
+- //listens to with the local membership settings
+- if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {
+- clusterReceiver.setMessageListener(this);
+- clusterReceiver.start();
+- //synchronize, big time FIXME
+-
membershipService.setLocalMemberProperties(getClusterReceiver().getHost(),
getClusterReceiver().getPort());
+- valid = true;
+- }
+- if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {
+- clusterSender.start();
+- valid = true;
+- }
+-
+- if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {
+- membershipService.setMembershipListener(this);
+- membershipService.start(MembershipService.MBR_RX);
+- valid = true;
+- }
+- if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {
+- membershipService.start(MembershipService.MBR_TX);
+- valid = true;
+- }
+-
+- if ( !valid) {
+- throw new IllegalArgumentException("Invalid start level, valid
levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ");
+- }
+- startLevel = (startLevel | svc);
+- }catch ( ChannelException cx ) {
+- throw cx;
+- }catch ( Exception x ) {
+- throw new ChannelException(x);
+- }
+- }
+-
+- /**
+- * Shuts down the channel. This can be called multiple times for individual services
to shutdown
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * DEFAULT - will shutdown all services <BR>
+- * MBR_RX_SEQ - starts the membership receiver <BR>
+- * MBR_TX_SEQ - starts the membership broadcaster <BR>
+- * SND_TX_SEQ - starts the replication transmitter<BR>
+- * SND_RX_SEQ - starts the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- */
+- protected synchronized void internalStop(int svc) throws ChannelException {
+- try {
+- //make sure we don't pass down any flags that are unrelated to the
bottom layer
+- svc = svc & Channel.DEFAULT;
+-
+- if (startLevel == 0) return; //we have already stopped up all components
+- if (svc == 0 ) return;//nothing to stop
+-
+- boolean valid = false;
+- if ( Channel.SND_RX_SEQ==(svc & Channel.SND_RX_SEQ) ) {
+- clusterReceiver.stop();
+- clusterReceiver.setMessageListener(null);
+- valid = true;
+- }
+- if ( Channel.SND_TX_SEQ==(svc & Channel.SND_TX_SEQ) ) {
+- clusterSender.stop();
+- valid = true;
+- }
+-
+- if ( Channel.MBR_RX_SEQ==(svc & Channel.MBR_RX_SEQ) ) {
+- membershipService.stop(MembershipService.MBR_RX);
+- membershipService.setMembershipListener(null);
+- valid = true;
+-
+- }
+- if ( Channel.MBR_TX_SEQ==(svc & Channel.MBR_TX_SEQ) ) {
+- valid = true;
+- membershipService.stop(MembershipService.MBR_TX);
+- }
+- if ( !valid) {
+- throw new IllegalArgumentException("Invalid start level, valid
levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ");
+- }
+-
+- startLevel = (startLevel & (~svc));
+-
+- }catch ( Exception x ) {
+- throw new ChannelException(x);
+- } finally {
+-
+- }
+-
+- }
+-
+- public void memberAdded(Member member){
+- SenderState.getSenderState(member);
+- super.memberAdded(member);
+- }
+-
+- public void memberDisappeared(Member member){
+- SenderState.removeSenderState(member);
+- super.memberDisappeared(member);
+- }
+-
+- public void messageReceived(ChannelMessage msg) {
+- if ( Logs.MESSAGES.isTraceEnabled() ) {
+- Logs.MESSAGES.trace("ChannelCoordinator - Received msg:" + new
UniqueId(msg.getUniqueId()) + " at " +new
java.sql.Timestamp(System.currentTimeMillis())+ " from
"+msg.getAddress().getName());
+- }
+- super.messageReceived(msg);
+- }
+-
+-
+- public ChannelReceiver getClusterReceiver() {
+- return clusterReceiver;
+- }
+-
+- public ChannelSender getClusterSender() {
+- return clusterSender;
+- }
+-
+- public MembershipService getMembershipService() {
+- return membershipService;
+- }
+-
+- public void setClusterReceiver(ChannelReceiver clusterReceiver) {
+- if ( clusterReceiver != null ) {
+- this.clusterReceiver = clusterReceiver;
+- this.clusterReceiver.setMessageListener(this);
+- } else {
+- if (this.clusterReceiver!=null )
this.clusterReceiver.setMessageListener(null);
+- this.clusterReceiver = null;
+- }
+- }
+-
+- public void setClusterSender(ChannelSender clusterSender) {
+- this.clusterSender = clusterSender;
+- }
+-
+- public void setMembershipService(MembershipService membershipService) {
+- this.membershipService = membershipService;
+- this.membershipService.setMembershipListener(this);
+- }
+-
+- public void heartbeat() {
+- if ( clusterSender!=null ) clusterSender.heartbeat();
+- super.heartbeat();
+- }
+-
+- /**
+- * has members
+- */
+- public boolean hasMembers() {
+- return this.getMembershipService().hasMembers();
+- }
+-
+- /**
+- * Get all current cluster members
+- * @return all members or empty array
+- */
+- public Member[] getMembers() {
+- return this.getMembershipService().getMembers();
+- }
+-
+- /**
+- *
+- * @param mbr Member
+- * @return Member
+- */
+- public Member getMember(Member mbr){
+- return this.getMembershipService().getMember(mbr);
+- }
+-
+-
+- /**
+- * Return the member that represents this node.
+- *
+- * @return Member
+- */
+- public Member getLocalMember(boolean incAlive) {
+- return this.getMembershipService().getLocalMember(incAlive);
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/group/RpcChannel.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/RpcChannel.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/RpcChannel.java (working copy)
+@@ -1,262 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import java.io.Serializable;
+-import java.util.ArrayList;
+-import java.util.Arrays;
+-import java.util.HashMap;
+-
+-import org.apache.catalina.tribes.Channel;
+-import org.apache.catalina.tribes.ChannelException;
+-import org.apache.catalina.tribes.ChannelListener;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.util.UUIDGenerator;
+-
+-/**
+- * A channel to handle RPC messaging
+- * @author Filip Hanik
+- */
+-public class RpcChannel implements ChannelListener{
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(RpcChannel.class);
+-
+- public static final int FIRST_REPLY = 1;
+- public static final int MAJORITY_REPLY = 2;
+- public static final int ALL_REPLY = 3;
+- public static final int NO_REPLY = 4;
+-
+- private Channel channel;
+- private RpcCallback callback;
+- private byte[] rpcId;
+-
+- private HashMap responseMap = new HashMap();
+-
+- /**
+- * Create an RPC channel. You can have several RPC channels attached to a group
+- * all separated out by the uniqueness
+- * @param rpcId - the unique Id for this RPC group
+- * @param channel Channel
+- * @param callback RpcCallback
+- */
+- public RpcChannel(byte[] rpcId, Channel channel, RpcCallback callback) {
+- this.channel = channel;
+- this.callback = callback;
+- this.rpcId = rpcId;
+- channel.addChannelListener(this);
+- }
+-
+-
+- /**
+- * Send a message and wait for the response.
+- * @param destination Member[] - the destination for the message, and the members
you request a reply from
+- * @param message Serializable - the message you are sending out
+- * @param options int - FIRST_REPLY, MAJORITY_REPLY or ALL_REPLY
+- * @param timeout long - timeout in milliseconds, if no reply is received within
this time null is returned
+- * @return Response[] - an array of response objects.
+- * @throws ChannelException
+- */
+- public Response[] send(Member[] destination,
+- Serializable message,
+- int rpcOptions,
+- int channelOptions,
+- long timeout) throws ChannelException {
+-
+- if ( destination==null || destination.length == 0 ) return new Response[0];
+-
+- //avoid dead lock
+- channelOptions = channelOptions & ~Channel.SEND_OPTIONS_SYNCHRONIZED_ACK;
+-
+- RpcCollectorKey key = new RpcCollectorKey(UUIDGenerator.randomUUID(false));
+- RpcCollector collector = new
RpcCollector(key,rpcOptions,destination.length,timeout);
+- try {
+- synchronized (collector) {
+- if ( rpcOptions != NO_REPLY ) responseMap.put(key, collector);
+- RpcMessage rmsg = new RpcMessage(rpcId, key.id, message);
+- channel.send(destination, rmsg, channelOptions);
+- if ( rpcOptions != NO_REPLY ) collector.wait(timeout);
+- }
+- } catch ( InterruptedException ix ) {
+- Thread.currentThread().interrupted();
+- //throw new ChannelException(ix);
+- }finally {
+- responseMap.remove(key);
+- }
+- return collector.getResponses();
+- }
+-
+- public void messageReceived(Serializable msg, Member sender) {
+- RpcMessage rmsg = (RpcMessage)msg;
+- RpcCollectorKey key = new RpcCollectorKey(rmsg.uuid);
+- if ( rmsg.reply ) {
+- RpcCollector collector = (RpcCollector)responseMap.get(key);
+- if (collector == null) {
+- callback.leftOver(rmsg.message, sender);
+- } else {
+- synchronized (collector) {
+- //make sure it hasn't been removed
+- if ( responseMap.containsKey(key) ) {
+- if ( (rmsg instanceof RpcMessage.NoRpcChannelReply) )
+- collector.destcnt--;
+- else
+- collector.addResponse(rmsg.message, sender);
+- if (collector.isComplete()) collector.notifyAll();
+- } else {
+- if (! (rmsg instanceof RpcMessage.NoRpcChannelReply) )
+- callback.leftOver(rmsg.message, sender);
+- }
+- }//synchronized
+- }//end if
+- } else{
+- Serializable reply = callback.replyRequest(rmsg.message,sender);
+- rmsg.reply = true;
+- rmsg.message = reply;
+- try {
+- channel.send(new Member[] {sender}, rmsg,0);
+- }catch ( Exception x ) {
+- log.error("Unable to send back reply in RpcChannel.",x);
+- }
+- }//end if
+- }
+-
+- public void breakdown() {
+- channel.removeChannelListener(this);
+- }
+-
+- public void finalize() {
+- breakdown();
+- }
+-
+- public boolean accept(Serializable msg, Member sender) {
+- if ( msg instanceof RpcMessage ) {
+- RpcMessage rmsg = (RpcMessage)msg;
+- return Arrays.equals(rmsg.rpcId,rpcId);
+- }else return false;
+- }
+-
+- public Channel getChannel() {
+- return channel;
+- }
+-
+- public RpcCallback getCallback() {
+- return callback;
+- }
+-
+- public byte[] getRpcId() {
+- return rpcId;
+- }
+-
+- public void setChannel(Channel channel) {
+- this.channel = channel;
+- }
+-
+- public void setCallback(RpcCallback callback) {
+- this.callback = callback;
+- }
+-
+- public void setRpcId(byte[] rpcId) {
+- this.rpcId = rpcId;
+- }
+-
+-
+-
+- /**
+- *
+- * Class that holds all response.
+- * @author not attributable
+- * @version 1.0
+- */
+- public static class RpcCollector {
+- public ArrayList responses = new ArrayList();
+- public RpcCollectorKey key;
+- public int options;
+- public int destcnt;
+- public long timeout;
+-
+- public RpcCollector(RpcCollectorKey key, int options, int destcnt, long timeout)
{
+- this.key = key;
+- this.options = options;
+- this.destcnt = destcnt;
+- this.timeout = timeout;
+- }
+-
+- public void addResponse(Serializable message, Member sender){
+- Response resp = new Response(sender,message);
+- responses.add(resp);
+- }
+-
+- public boolean isComplete() {
+- if ( destcnt <= 0 ) return true;
+- switch (options) {
+- case ALL_REPLY:
+- return destcnt == responses.size();
+- case MAJORITY_REPLY:
+- {
+- float perc = ((float)responses.size()) / ((float)destcnt);
+- return perc >= 0.50f;
+- }
+- case FIRST_REPLY:
+- return responses.size()>0;
+- default:
+- return false;
+- }
+- }
+-
+- public int hashCode() {
+- return key.hashCode();
+- }
+-
+- public boolean equals(Object o) {
+- if ( o instanceof RpcCollector ) {
+- RpcCollector r = (RpcCollector)o;
+- return r.key.equals(this.key);
+- } else return false;
+- }
+-
+- public Response[] getResponses() {
+- return (Response[])responses.toArray(new Response[responses.size()]);
+- }
+- }
+-
+- public static class RpcCollectorKey {
+- byte[] id;
+- public RpcCollectorKey(byte[] id) {
+- this.id = id;
+- }
+-
+- public int hashCode() {
+- return id[0]+id[1]+id[2]+id[3];
+- }
+-
+- public boolean equals(Object o) {
+- if ( o instanceof RpcCollectorKey ) {
+- RpcCollectorKey r = (RpcCollectorKey)o;
+- return Arrays.equals(id,r.id);
+- } else return false;
+- }
+-
+- }
+-
+- protected static String bToS(byte[] data) {
+- StringBuffer buf = new StringBuffer(4*16);
+- buf.append("{");
+- for (int i=0; data!=null && i<data.length; i++ )
buf.append(String.valueOf(data[i])).append(" ");
+- buf.append("}");
+- return buf.toString();
+- }
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/group/Response.java
+===================================================================
+--- java/org/apache/catalina/tribes/group/Response.java (revision 590752)
++++ java/org/apache/catalina/tribes/group/Response.java (working copy)
+@@ -1,54 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.group;
+-
+-import java.io.Serializable;
+-
+-import org.apache.catalina.tribes.Member;
+-
+-/**
+- * A response object holds a message from a responding partner.
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class Response {
+- private Member source;
+- private Serializable message;
+- public Response() {
+- }
+-
+- public Response(Member source, Serializable message) {
+- this.source = source;
+- this.message = message;
+- }
+-
+- public void setSource(Member source) {
+- this.source = source;
+- }
+-
+- public void setMessage(Serializable message) {
+- this.message = message;
+- }
+-
+- public Member getSource() {
+- return source;
+- }
+-
+- public Serializable getMessage() {
+- return message;
+- }
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/ChannelMessage.java
+===================================================================
+--- java/org/apache/catalina/tribes/ChannelMessage.java (revision 590752)
++++ java/org/apache/catalina/tribes/ChannelMessage.java (working copy)
+@@ -1,109 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import java.io.Serializable;
+-import org.apache.catalina.tribes.io.XByteBuffer;
+-
+-/**
+- * Message that is passed through the interceptor stack after the
+- * data serialized in the Channel object and then passed down to the
+- * interceptor and eventually down to the ChannelSender component
+- * @author Filip Hanik
+- *
+- */
+-public interface ChannelMessage extends Serializable {
+-
+-
+-
+-
+- /**
+- * Get the address that this message originated from.
+- * Almost always <code>Channel.getLocalMember(boolean)</code><br>
+- * This would be set to a different address
+- * if the message was being relayed from a host other than the one
+- * that originally sent it.
+- * @return the source or reply-to address of this message
+- */
+- public Member getAddress();
+-
+- /**
+- * Sets the source or reply-to address of this message
+- * @param member Member
+- */
+- public void setAddress(Member member);
+-
+- /**
+- * Timestamp of when the message was created.
+- * @return long timestamp in milliseconds
+- */
+- public long getTimestamp();
+-
+- /**
+- *
+- * Sets the timestamp of this message
+- * @param timestamp The timestamp
+- */
+- public void setTimestamp(long timestamp);
+-
+- /**
+- * Each message must have a globally unique Id.
+- * interceptors heavily depend on this id for message processing
+- * @return byte
+- */
+- public byte[] getUniqueId();
+-
+- /**
+- * The byte buffer that contains the actual message payload
+- * @param buf XByteBuffer
+- */
+- public void setMessage(XByteBuffer buf);
+-
+- /**
+- * returns the byte buffer that contains the actual message payload
+- * @return XByteBuffer
+- */
+- public XByteBuffer getMessage();
+-
+- /**
+- * The message options is a 32 bit flag set
+- * that triggers interceptors and message behavior.
+- * @see Channel#send(Member[], Serializable, int)
+- * @see ChannelInterceptor#getOptionFlag
+- * @return int - the option bits set for this message
+- */
+- public int getOptions();
+-
+- /**
+- * sets the option bits for this message
+- * @param options int
+- * @see #getOptions()
+- */
+- public void setOptions(int options);
+-
+- /**
+- * Shallow clone, what gets cloned depends on the implementation
+- * @return ChannelMessage
+- */
+- public Object clone();
+-
+- /**
+- * Deep clone, all fields MUST get cloned
+- * @return ChannelMessage
+- */
+- public Object deepclone();
+-}
+Index: java/org/apache/catalina/tribes/ChannelListener.java
+===================================================================
+--- java/org/apache/catalina/tribes/ChannelListener.java (revision 590752)
++++ java/org/apache/catalina/tribes/ChannelListener.java (working copy)
+@@ -1,68 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import java.io.Serializable;
+-/**
+- *
+- * <p>Title: ChannelListener</p>
+- *
+- * <p>Description: An interface to listens to incoming messages from a channel
</p>
+- * When a message is received, the Channel will invoke the channel listener in a
conditional sequence.
+- * <code>if ( listener.accept(msg,sender) )
listener.messageReceived(msg,sender);</code><br>
+- * A ChannelListener implementation MUST NOT return true on
<code>accept(Serializable, Member)</code>
+- * if it doesn't intend to process the message. The channel can this way track
whether a message
+- * was processed by an above application or if it was just received and forgot about, a
featuer required
+- * to support message-response(RPC) calls<br>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public interface ChannelListener {
+-
+- /**
+- * Receive a message from the channel
+- * @param msg Serializable
+- * @param sender - the source of the message
+- */
+- public void messageReceived(Serializable msg, Member sender);
+-
+- /**
+- * Invoked by the channel to determine if the listener will process this message or
not.
+- * @param msg Serializable
+- * @param sender Member
+- * @return boolean
+- */
+- public boolean accept(Serializable msg, Member sender);
+-
+- /**
+- *
+- * @param listener Object
+- * @return boolean
+- * @see Object#equals(Object)
+- */
+- public boolean equals(Object listener);
+-
+- /**
+- *
+- * @return int
+- * @see Object#hashCode(int)
+- */
+- public int hashCode();
+-
+-}
+Index: java/org/apache/catalina/tribes/package.html
+===================================================================
+--- java/org/apache/catalina/tribes/package.html (revision 590752)
++++ java/org/apache/catalina/tribes/package.html (working copy)
+@@ -1,86 +0,0 @@
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<body>
+-<head><title>Apache Tribes - The Tomcat Cluster Communication
Module</title>
+-<h3>QuickStart</h3>
+- <pre><code>
+- //create a channel
+- Channel myChannel = new GroupChannel();
+-
+- //create my listeners
+- MyMessageListener msgListener = new MyMessageListener();
+- MyMemberListener mbrListener = new MyMemberListener();
+-
+- //attach the listeners to the channel
+- myChannel.addMembershipListener(mbrListener);
+- myChannel.addChannelListener(msgListener);
+-
+- //start the channel
+- myChannel.start(Channel.DEFAULT);
+-
+- //create a message to be sent, message must implement java.io.Serializable
+- //for performance reasons you probably want them to implement
java.io.Externalizable
+- Serializable myMsg = new MyMessage();
+-
+- //retrieve my current members
+- Member[] group = myChannel.getMembers();
+-
+- //send the message
+- channel.send(group,myMsg,Channel.SEND_OPTIONS_DEFAULT);
+-
+- </code></pre>
+-<h3>Interfaces for the Application Developer</h3>
+- <ol>
+- <li><code>org.apache.catalina.tribes.Channel</code>
+- Main component to interact with to send messages
+- </li>
+-
<li><code>org.apache.catalina.tribes.MembershipListener</code>
+- Listen to membership changes
+- </li>
+- <li><code>org.apache.catalina.tribes.ChannelListener</code>
+- Listen to data messages
+- </li>
+- <li><code>org.apache.catalina.tribes.Member</code>
+- Identifies a node, implementation specific, default is
org.apache.catalina.tribes.membership.MemberImpl
+- </li>
+- </ol>
+- <h3>Interfaces for the Tribes Component Developer</h3>
+- <ol>
+- <li><code>org.apache.catalina.tribes.Channel</code>
+- Main component to that the application interacts with
+- </li>
+- <li><code>org.apache.catalina.tribes.ChannelReceiver</code>
+- IO Component to receive messages over some network transport
+- </li>
+- <li><code>org.apache.catalina.tribes.ChannelSender</code>
+- IO Component to send messages over some network transport
+- </li>
+- <li><code>org.apache.catalina.tribes.MembershipService</code>
+- IO Component that handles membership discovery and
+- </li>
+-
<li><code>org.apache.catalina.tribes.ChannelInterceptor</code>
+- interceptors between the Channel and the IO layer
+- </li>
+- <li><code>org.apache.catalina.tribes.ChannelMessage</code>
+- The message that is sent through the interceptor stack down to the IO layer
+- </li>
+-
+- <li><code>org.apache.catalina.tribes.Member</code>
+- Identifies a node, implementation specific to the underlying IO logic
+- </li>
+- </ol>
+-</body>
+Index: java/org/apache/catalina/tribes/LocalStrings.properties
+===================================================================
+--- java/org/apache/catalina/tribes/LocalStrings.properties (revision 590752)
++++ java/org/apache/catalina/tribes/LocalStrings.properties (working copy)
+@@ -1,16 +0,0 @@
+-# Licensed to the Apache Software Foundation (ASF) under one or more
+-# contributor license agreements. See the NOTICE file distributed with
+-# this work for additional information regarding copyright ownership.
+-# The ASF licenses this file to You under the Apache License, Version 2.0
+-# (the "License"); you may not use this file except in compliance with
+-# the License. You may obtain a copy of the License at
+-#
+-#
http://www.apache.org/licenses/LICENSE-2.0
+-#
+-# Unless required by applicable law or agreed to in writing, software
+-# distributed under the License is distributed on an "AS IS" BASIS,
+-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-# See the License for the specific language governing permissions and
+-# limitations under the License.
+-
+-cluster.mbean.register.already=MBean {0} already registered!
+Index: java/org/apache/catalina/tribes/UniqueId.java
+===================================================================
+--- java/org/apache/catalina/tribes/UniqueId.java (revision 590752)
++++ java/org/apache/catalina/tribes/UniqueId.java (working copy)
+@@ -1,72 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import org.apache.catalina.tribes.util.Arrays;
+-import java.io.Serializable;
+-
+-/**
+- * <p>Title: Represents a globabally unique Id</p>
+- *
+- * <p>Company: </p>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public final class UniqueId implements Serializable{
+- protected byte[] id;
+-
+- public UniqueId() {
+- }
+-
+- public UniqueId(byte[] id) {
+- this.id = id;
+- }
+-
+- public UniqueId(byte[] id, int offset, int length) {
+- this.id = new byte[length];
+- System.arraycopy(id,offset,this.id,0,length);
+- }
+-
+- public int hashCode() {
+- if ( id == null ) return 0;
+- return Arrays.hashCode(id);
+- }
+-
+- public boolean equals(Object other) {
+- boolean result = (other instanceof UniqueId);
+- if ( result ) {
+- UniqueId uid = (UniqueId)other;
+- if ( this.id == null && uid.id == null ) result = true;
+- else if ( this.id == null && uid.id != null ) result = false;
+- else if ( this.id != null && uid.id == null ) result = false;
+- else result = Arrays.equals(this.id,uid.id);
+- }//end if
+- return result;
+- }
+-
+- public byte[] getBytes() {
+- return id;
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer("UniqueId");
+- buf.append(org.apache.catalina.tribes.util.Arrays.toString(id));
+- return buf.toString();
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/mbeans-descriptors.xml
+===================================================================
+--- java/org/apache/catalina/tribes/mbeans-descriptors.xml (revision 590752)
++++ java/org/apache/catalina/tribes/mbeans-descriptors.xml (working copy)
+@@ -1,110 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<mbeans-descriptors>
+-
+- <mbean name="SimpleTcpCluster"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Tcp Cluster implementation"
+- domain="Catalina"
+- group="Cluster"
+- type="org.apache.catalina.ha.tcp.SimpleTcpCluster">
+-
+- <attribute name="protocolStack"
+- description="JavaGroups protocol stack selection"
+- type="java.lang.String"/>
+-
+- </mbean>
+-
+-
+- <mbean name="SimpleTcpReplicationManager"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Clustered implementation of the Manager interface"
+- domain="Catalina"
+- group="Manager"
+-
type="org.apache.catalina.ha.tcp.SimpleTcpReplicationManager">
+-
+- <attribute name="algorithm"
+- description="The message digest algorithm to be used when generating
+- session identifiers"
+- type="java.lang.String"/>
+-
+- <attribute name="checkInterval"
+- description="The interval (in seconds) between checks for expired
+- sessions"
+- type="int"/>
+-
+- <attribute name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- <attribute name="distributable"
+- description="The distributable flag for Sessions created by this
+- Manager"
+- type="boolean"/>
+-
+- <attribute name="entropy"
+- description="A String initialization parameter used to increase the
+- entropy of the initialization of our random number
+- generator"
+- type="java.lang.String"/>
+-
+- <attribute name="managedResource"
+- description="The managed resource this MBean is associated with"
+- type="java.lang.Object"/>
+-
+- <attribute name="maxActiveSessions"
+- description="The maximum number of active Sessions allowed, or -1
+- for no limit"
+- type="int"/>
+-
+- <attribute name="maxInactiveInterval"
+- description="The default maximum inactive interval for Sessions
+- created by this Manager"
+- type="int"/>
+-
+- <attribute name="name"
+- description="The descriptive name of this Manager implementation
+- (for logging)"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- </mbean>
+-
+-
+-
+-<mbean name="ReplicationValve"
+- className="org.apache.catalina.mbeans.ClassNameMBean"
+- description="Valve for simple tcp replication"
+- domain="Catalina"
+- group="Valve"
+- type="org.apache.catalina.ha.tcp.ReplicationValve">
+-
+- <attribute name="className"
+- description="Fully qualified class name of the managed object"
+- type="java.lang.String"
+- writeable="false"/>
+-
+- <attribute name="debug"
+- description="The debugging detail level for this component"
+- type="int"/>
+-
+- </mbean>
+-
+-
+-</mbeans-descriptors>
+Index: java/org/apache/catalina/tribes/ManagedChannel.java
+===================================================================
+--- java/org/apache/catalina/tribes/ManagedChannel.java (revision 590752)
++++ java/org/apache/catalina/tribes/ManagedChannel.java (working copy)
+@@ -1,78 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import java.util.Iterator;
+-
+-/**
+- * Channel interface
+- * A managed channel interface gives you access to the components of the channels
+- * such as senders, receivers, interceptors etc for configurations purposes
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public interface ManagedChannel extends Channel {
+-
+- /**
+- * Sets the channel sender
+- * @param sender ChannelSender
+- * @see ChannelSender
+- */
+- public void setChannelSender(ChannelSender sender);
+-
+- /**
+- * Sets the channel receiver
+- * @param receiver ChannelReceiver
+- * @see ChannelReceiver
+- */
+- public void setChannelReceiver(ChannelReceiver receiver);
+-
+- /**
+- * Sets the membership service
+- * @param service MembershipService
+- * @see MembershipService
+- */
+- public void setMembershipService(MembershipService service);
+-
+- /**
+- * returns the channel sender
+- * @return ChannelSender
+- * @see ChannelSender
+- */
+- public ChannelSender getChannelSender();
+-
+- /**
+- * returns the channel receiver
+- * @return ChannelReceiver
+- * @see ChannelReceiver
+- */
+- public ChannelReceiver getChannelReceiver();
+-
+- /**
+- * Returns the membership service
+- * @return MembershipService
+- * @see MembershipService
+- */
+- public MembershipService getMembershipService();
+-
+- /**
+- * Returns the interceptor stack
+- * @return Iterator
+- * @see Channel#addInterceptor(ChannelInterceptor)
+- */
+- public Iterator getInterceptors();
+-}
+Index: java/org/apache/catalina/tribes/io/ChannelData.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/ChannelData.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/ChannelData.java (working copy)
+@@ -1,359 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.io;
+-
+-import java.util.Arrays;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import org.apache.catalina.tribes.util.UUIDGenerator;
+-import org.apache.catalina.tribes.Channel;
+-import java.sql.Timestamp;
+-
+-/**
+- * The <code>ChannelData</code> object is used to transfer a message through
the
+- * channel interceptor stack and eventually out on a transport to be sent
+- * to another node. While the message is being processed by the different
+- * interceptors, the message data can be manipulated as each interceptor seems
appropriate.
+- * @author Peter Rossbach
+- * @author Filip Hanik
+- * @version $Revision$ $Date$
+- *
+- */
+-public class ChannelData implements ChannelMessage {
+- public static ChannelData[] EMPTY_DATA_ARRAY = new ChannelData[0];
+-
+- public static boolean USE_SECURE_RANDOM_FOR_UUID = false;
+-
+- /**
+- * The options this message was sent with
+- */
+- private int options = 0 ;
+- /**
+- * The message data, stored in a dynamic buffer
+- */
+- private XByteBuffer message ;
+- /**
+- * The timestamp that goes with this message
+- */
+- private long timestamp ;
+- /**
+- * A unique message id
+- */
+- private byte[] uniqueId ;
+- /**
+- * The source or reply-to address for this message
+- */
+- private Member address;
+-
+- /**
+- * Creates an empty channel data with a new unique Id
+- * @see #ChannelData(boolean)
+- */
+- public ChannelData() {
+- this(true);
+- }
+-
+- /**
+- * Create an empty channel data object
+- * @param generateUUID boolean - if true, a unique Id will be generated
+- */
+- public ChannelData(boolean generateUUID) {
+- if ( generateUUID ) generateUUID();
+- }
+-
+-
+-
+- /**
+- * Creates a new channel data object with data
+- * @param uniqueId - unique message id
+- * @param message - message data
+- * @param timestamp - message timestamp
+- */
+- public ChannelData(byte[] uniqueId, XByteBuffer message, long timestamp) {
+- this.uniqueId = uniqueId;
+- this.message = message;
+- this.timestamp = timestamp;
+- }
+-
+- /**
+- * @return Returns the message byte buffer
+- */
+- public XByteBuffer getMessage() {
+- return message;
+- }
+- /**
+- * @param message The message to send.
+- */
+- public void setMessage(XByteBuffer message) {
+- this.message = message;
+- }
+- /**
+- * @return Returns the timestamp.
+- */
+- public long getTimestamp() {
+- return timestamp;
+- }
+- /**
+- * @param timestamp The timestamp to send
+- */
+- public void setTimestamp(long timestamp) {
+- this.timestamp = timestamp;
+- }
+- /**
+- * @return Returns the uniqueId.
+- */
+- public byte[] getUniqueId() {
+- return uniqueId;
+- }
+- /**
+- * @param uniqueId The uniqueId to send.
+- */
+- public void setUniqueId(byte[] uniqueId) {
+- this.uniqueId = uniqueId;
+- }
+- /**
+- * @return returns the message options
+- * see
org.apache.catalina.tribes.Channel#sendMessage(org.apache.catalina.tribes.Member[],
java.io.Serializable, int)
+- *
+- */
+- public int getOptions() {
+- return options;
+- }
+- /**
+- * @param sets the message options
+- */
+- public void setOptions(int options) {
+- this.options = options;
+- }
+-
+- /**
+- * Returns the source or reply-to address
+- * @return Member
+- */
+- public Member getAddress() {
+- return address;
+- }
+-
+- /**
+- * Sets the source or reply-to address
+- * @param address Member
+- */
+- public void setAddress(Member address) {
+- this.address = address;
+- }
+-
+- /**
+- * Generates a UUID and invokes setUniqueId
+- */
+- public void generateUUID() {
+- byte[] data = new byte[16];
+- UUIDGenerator.randomUUID(USE_SECURE_RANDOM_FOR_UUID,data,0);
+- setUniqueId(data);
+- }
+-
+- public int getDataPackageLength() {
+- int length =
+- 4 + //options
+- 8 + //timestamp off=4
+- 4 + //unique id length off=12
+- uniqueId.length+ //id data off=12+uniqueId.length
+- 4 + //addr length off=12+uniqueId.length+4
+- ((MemberImpl)address).getDataLength()+ //member data
off=12+uniqueId.length+4+add.length
+- 4 + //message length off=12+uniqueId.length+4+add.length+4
+- message.getLength();
+- return length;
+-
+- }
+-
+- /**
+- * Serializes the ChannelData object into a byte[] array
+- * @return byte[]
+- */
+- public byte[] getDataPackage() {
+- int length = getDataPackageLength();
+- byte[] data = new byte[length];
+- int offset = 0;
+- return getDataPackage(data,offset);
+- }
+-
+- public byte[] getDataPackage(byte[] data, int offset) {
+- byte[] addr = ((MemberImpl)address).getData(false);
+- XByteBuffer.toBytes(options,data,offset);
+- offset += 4; //options
+- XByteBuffer.toBytes(timestamp,data,offset);
+- offset += 8; //timestamp
+- XByteBuffer.toBytes(uniqueId.length,data,offset);
+- offset += 4; //uniqueId.length
+- System.arraycopy(uniqueId,0,data,offset,uniqueId.length);
+- offset += uniqueId.length; //uniqueId data
+- XByteBuffer.toBytes(addr.length,data,offset);
+- offset += 4; //addr.length
+- System.arraycopy(addr,0,data,offset,addr.length);
+- offset += addr.length; //addr data
+- XByteBuffer.toBytes(message.getLength(),data,offset);
+- offset += 4; //message.length
+- System.arraycopy(message.getBytesDirect(),0,data,offset,message.getLength());
+- offset += message.getLength(); //message data
+- return data;
+- }
+-
+- /**
+- * Deserializes a ChannelData object from a byte array
+- * @param b byte[]
+- * @return ChannelData
+- */
+- public static ChannelData getDataFromPackage(XByteBuffer xbuf) {
+- ChannelData data = new ChannelData(false);
+- int offset = 0;
+- data.setOptions(XByteBuffer.toInt(xbuf.getBytesDirect(),offset));
+- offset += 4; //options
+- data.setTimestamp(XByteBuffer.toLong(xbuf.getBytesDirect(),offset));
+- offset += 8; //timestamp
+- data.uniqueId = new byte[XByteBuffer.toInt(xbuf.getBytesDirect(),offset)];
+- offset += 4; //uniqueId length
+-
System.arraycopy(xbuf.getBytesDirect(),offset,data.uniqueId,0,data.uniqueId.length);
+- offset += data.uniqueId.length; //uniqueId data
+- //byte[] addr = new byte[XByteBuffer.toInt(xbuf.getBytesDirect(),offset)];
+- int addrlen = XByteBuffer.toInt(xbuf.getBytesDirect(),offset);
+- offset += 4; //addr length
+- //System.arraycopy(xbuf.getBytesDirect(),offset,addr,0,addr.length);
+- data.setAddress(MemberImpl.getMember(xbuf.getBytesDirect(),offset,addrlen));
+- //offset += addr.length; //addr data
+- offset += addrlen;
+- int xsize = XByteBuffer.toInt(xbuf.getBytesDirect(),offset);
+- offset += 4; //xsize length
+- System.arraycopy(xbuf.getBytesDirect(),offset,xbuf.getBytesDirect(),0,xsize);
+- xbuf.setLength(xsize);
+- data.message = xbuf;
+- return data;
+-
+- }
+-
+- public static ChannelData getDataFromPackage(byte[] b) {
+- ChannelData data = new ChannelData(false);
+- int offset = 0;
+- data.setOptions(XByteBuffer.toInt(b,offset));
+- offset += 4; //options
+- data.setTimestamp(XByteBuffer.toLong(b,offset));
+- offset += 8; //timestamp
+- data.uniqueId = new byte[XByteBuffer.toInt(b,offset)];
+- offset += 4; //uniqueId length
+- System.arraycopy(b,offset,data.uniqueId,0,data.uniqueId.length);
+- offset += data.uniqueId.length; //uniqueId data
+- byte[] addr = new byte[XByteBuffer.toInt(b,offset)];
+- offset += 4; //addr length
+- System.arraycopy(b,offset,addr,0,addr.length);
+- data.setAddress(MemberImpl.getMember(addr));
+- offset += addr.length; //addr data
+- int xsize = XByteBuffer.toInt(b,offset);
+- //data.message = new XByteBuffer(new byte[xsize],false);
+- data.message = BufferPool.getBufferPool().getBuffer(xsize,false);
+- offset += 4; //message length
+- System.arraycopy(b,offset,data.message.getBytesDirect(),0,xsize);
+- data.message.append(b,offset,xsize);
+- offset += xsize; //message data
+- return data;
+- }
+-
+- public int hashCode() {
+- return XByteBuffer.toInt(getUniqueId(),0);
+- }
+-
+- /**
+- * Compares to ChannelData objects, only compares on
getUniqueId().equals(o.getUniqueId())
+- * @param o Object
+- * @return boolean
+- */
+- public boolean equals(Object o) {
+- if ( o instanceof ChannelData ) {
+- return Arrays.equals(getUniqueId(),((ChannelData)o).getUniqueId());
+- } else return false;
+- }
+-
+- /**
+- * Create a shallow clone, only the data gets recreated
+- * @return ClusterData
+- */
+- public Object clone() {
+-// byte[] d = this.getDataPackage();
+-// return ClusterData.getDataFromPackage(d);
+- ChannelData clone = new ChannelData(false);
+- clone.options = this.options;
+- clone.message = new XByteBuffer(this.message.getBytesDirect(),false);
+- clone.timestamp = this.timestamp;
+- clone.uniqueId = this.uniqueId;
+- clone.address = this.address;
+- return clone;
+- }
+-
+- /**
+- * Complete clone
+- * @return ClusterData
+- */
+- public Object deepclone() {
+- byte[] d = this.getDataPackage();
+- return ChannelData.getDataFromPackage(d);
+- }
+-
+- /**
+- * Utility method, returns true if the options flag indicates that an ack
+- * is to be sent after the message has been received and processed
+- * @param options int - the options for the message
+- * @return boolean
+- * @see org.apache.catalina.tribes.Channel#SEND_OPTIONS_USE_ACK
+- * @see org.apache.catalina.tribes.Channel#SEND_OPTIONS_SYNCHRONIZED_ACK
+- */
+- public static boolean sendAckSync(int options) {
+- return ( (Channel.SEND_OPTIONS_USE_ACK & options) ==
Channel.SEND_OPTIONS_USE_ACK) &&
+- ( (Channel.SEND_OPTIONS_SYNCHRONIZED_ACK & options) ==
Channel.SEND_OPTIONS_SYNCHRONIZED_ACK);
+- }
+-
+-
+- /**
+- * Utility method, returns true if the options flag indicates that an ack
+- * is to be sent after the message has been received but not yet processed
+- * @param options int - the options for the message
+- * @return boolean
+- * @see org.apache.catalina.tribes.Channel#SEND_OPTIONS_USE_ACK
+- * @see org.apache.catalina.tribes.Channel#SEND_OPTIONS_SYNCHRONIZED_ACK
+- */
+- public static boolean sendAckAsync(int options) {
+- return ( (Channel.SEND_OPTIONS_USE_ACK & options) ==
Channel.SEND_OPTIONS_USE_ACK) &&
+- ( (Channel.SEND_OPTIONS_SYNCHRONIZED_ACK & options) !=
Channel.SEND_OPTIONS_SYNCHRONIZED_ACK);
+- }
+-
+- public String toString() {
+- StringBuffer buf = new StringBuffer();
+- buf.append("ClusterData[src=");
+- buf.append(getAddress()).append("; id=");
+- buf.append(bToS(getUniqueId())).append("; sent=");
+- buf.append(new
Timestamp(this.getTimestamp()).toString()).append("]");
+- return buf.toString();
+- }
+-
+- public static String bToS(byte[] data) {
+- StringBuffer buf = new StringBuffer(4*16);
+- buf.append("{");
+- for (int i=0; data!=null && i<data.length; i++ )
buf.append(String.valueOf(data[i])).append(" ");
+- buf.append("}");
+- return buf.toString();
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/io/BufferPool.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/BufferPool.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/BufferPool.java (working copy)
+@@ -1,94 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.io;
+-
+-
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-
+-/**
+- *
+- * @author Filip Hanik
+- *
+- * @version 1.0
+- */
+-public class BufferPool {
+- protected static Log log = LogFactory.getLog(BufferPool.class);
+-
+- public static int DEFAULT_POOL_SIZE = 100*1024*1024; //100MB
+-
+-
+-
+- protected static volatile BufferPool instance = null;
+- protected BufferPoolAPI pool = null;
+-
+- private BufferPool(BufferPoolAPI pool) {
+- this.pool = pool;
+- }
+-
+- public XByteBuffer getBuffer(int minSize, boolean discard) {
+- if ( pool != null ) return pool.getBuffer(minSize, discard);
+- else return new XByteBuffer(minSize,discard);
+- }
+-
+- public void returnBuffer(XByteBuffer buffer) {
+- if ( pool != null ) pool.returnBuffer(buffer);
+- }
+-
+- public void clear() {
+- if ( pool != null ) pool.clear();
+- }
+-
+-
+- public static BufferPool getBufferPool() {
+- if ( (instance == null) ) {
+- synchronized (BufferPool.class) {
+- if ( instance == null ) {
+- BufferPoolAPI pool = null;
+- Class clazz = null;
+- try {
+- clazz =
Class.forName("org.apache.catalina.tribes.io.BufferPool15Impl");
+- pool = (BufferPoolAPI)clazz.newInstance();
+- } catch ( Throwable x ) {
+- try {
+- clazz =
Class.forName("org.apache.catalina.tribes.io.BufferPool14Impl");
+- pool = (BufferPoolAPI)clazz.newInstance();
+- } catch ( Throwable e ) {
+- log.warn("Unable to initilize BufferPool, not pooling
XByteBuffer objects:"+x.getMessage());
+- if ( log.isDebugEnabled() ) log.debug("Unable to
initilize BufferPool, not pooling XByteBuffer objects:",x);
+- }
+- }
+- pool.setMaxSize(DEFAULT_POOL_SIZE);
+- log.info("Created a buffer pool with max
size:"+DEFAULT_POOL_SIZE+" bytes of
type:"+(clazz!=null?clazz.getName():"null"));
+- instance = new BufferPool(pool);
+- }//end if
+- }//sync
+- }//end if
+- return instance;
+- }
+-
+-
+- public static interface BufferPoolAPI {
+- public void setMaxSize(int bytes);
+-
+- public XByteBuffer getBuffer(int minSize, boolean discard);
+-
+- public void returnBuffer(XByteBuffer buffer);
+-
+- public void clear();
+- }
+-}
+Index: java/org/apache/catalina/tribes/io/ReplicationStream.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/ReplicationStream.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/ReplicationStream.java (working copy)
+@@ -1,117 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.tribes.io;
+-
+-import java.io.IOException;
+-import java.io.InputStream;
+-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
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public final class ReplicationStream extends ObjectInputStream {
+-
+-
+- /**
+- * The class loader we will use to resolve classes.
+- */
+- private ClassLoader[] classLoaders = 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[] classLoaders)
+- throws IOException {
+-
+- super(stream);
+- this.classLoaders = classLoaders;
+- }
+-
+- /**
+- * 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 {
+- String name = classDesc.getName();
+- boolean tryRepFirst = name.startsWith("org.apache.catalina.tribes");
+- try {
+- try
+- {
+- if ( tryRepFirst ) return findReplicationClass(name);
+- else return findExternalClass(name);
+- }
+- catch ( Exception x )
+- {
+- if ( tryRepFirst ) return findExternalClass(name);
+- else return findReplicationClass(name);
+- }
+- } catch (ClassNotFoundException e) {
+- return super.resolveClass(classDesc);
+- }
+- }
+-
+- public Class findReplicationClass(String name)
+- throws ClassNotFoundException, IOException {
+- Class clazz = Class.forName(name, false, getClass().getClassLoader());
+- return clazz;
+- }
+-
+- public Class findExternalClass(String name) throws ClassNotFoundException {
+- ClassNotFoundException cnfe = null;
+- for (int i=0; i<classLoaders.length; i++ ) {
+- try {
+- Class clazz = Class.forName(name, false, classLoaders[i]);
+- return clazz;
+- } catch ( ClassNotFoundException x ) {
+- cnfe = x;
+- }
+- }
+- if ( cnfe != null ) throw cnfe;
+- else throw new ClassNotFoundException(name);
+- }
+-
+- public void close() throws IOException {
+- this.classLoaders = null;
+- super.close();
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/io/XByteBuffer.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/XByteBuffer.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/XByteBuffer.java (working copy)
+@@ -1,612 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.io;
+-
+-import java.io.ByteArrayInputStream;
+-import java.io.ByteArrayOutputStream;
+-import java.io.IOException;
+-import java.io.InputStream;
+-import java.io.ObjectInputStream;
+-import java.io.ObjectOutputStream;
+-import java.io.Serializable;
+-import java.nio.ByteBuffer;
+-
+-/**
+- * The XByteBuffer provides a dual functionality.
+- * One, it stores message bytes and automatically extends the byte buffer if
needed.<BR>
+- * Two, it can encode and decode packages so that they can be defined and identified
+- * as they come in on a socket.
+- * <br>
+- * <b>THIS CLASS IS NOT THREAD SAFE</B><BR>
+- * <br/>
+- * Transfer package:
+- * <ul>
+- * <li><b>START_DATA/b> - 7 bytes -
<i>FLT2002</i></li>
+- * <li><b>SIZE</b> - 4 bytes - size of the data
package</li>
+- * <li><b>DATA</b> - should be as many bytes as the prev
SIZE</li>
+- * <li><b>END_DATA</b> - 7 bytes -
<i>TLF2003</i></lI>
+- * </ul>
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public class XByteBuffer
+-{
+-
+- public static org.apache.juli.logging.Log log =
+- org.apache.juli.logging.LogFactory.getLog( XByteBuffer.class );
+-
+- /**
+- * This is a package header, 7 bytes (FLT2002)
+- */
+- public static final byte[] START_DATA = {70,76,84,50,48,48,50};
+-
+- /**
+- * This is the package footer, 7 bytes (TLF2003)
+- */
+- public static final byte[] END_DATA = {84,76,70,50,48,48,51};
+-
+- /**
+- * Default size on the initial byte buffer
+- */
+- private static final int DEF_SIZE = 2048;
+-
+- /**
+- * Default size to extend the buffer with
+- */
+- private static final int DEF_EXT = 1024;
+-
+- /**
+- * Variable to hold the data
+- */
+- protected byte[] buf = null;
+-
+- /**
+- * Current length of data in the buffer
+- */
+- protected int bufSize = 0;
+-
+- /**
+- * Flag for discarding invalid packages
+- * If this flag is set to true, and append(byte[],...) is called,
+- * the data added will be inspected, and if it doesn't start with
+- * <code>START_DATA</code> it will be thrown away.
+- *
+- */
+- protected boolean discard = true;
+-
+- /**
+- * Constructs a new XByteBuffer
+- * @param size - the initial size of the byte buffer
+- * @todo use a pool of byte[] for performance
+- */
+- public XByteBuffer(int size, boolean discard) {
+- buf = new byte[size];
+- this.discard = discard;
+- }
+-
+- public XByteBuffer(byte[] data,boolean discard) {
+- this(data,data.length+128,discard);
+- }
+-
+- public XByteBuffer(byte[] data, int size,boolean discard) {
+- int length = Math.max(data.length,size);
+- buf = new byte[length];
+- System.arraycopy(data,0,buf,0,data.length);
+- bufSize = data.length;
+- this.discard = discard;
+- }
+-
+- public int getLength() {
+- return bufSize;
+- }
+-
+- public void setLength(int size) {
+- if ( size > buf.length ) throw new ArrayIndexOutOfBoundsException("Size
is larger than existing buffer.");
+- bufSize = size;
+- }
+-
+- public void trim(int length) {
+- if ( (bufSize - length) < 0 )
+- throw new ArrayIndexOutOfBoundsException("Can't trim more bytes
than are available. length:"+bufSize+" trim:"+length);
+- bufSize -= length;
+- }
+-
+- public void reset() {
+- bufSize = 0;
+- }
+-
+- public byte[] getBytesDirect() {
+- return this.buf;
+- }
+-
+- /**
+- * Returns the bytes in the buffer, in its exact length
+- */
+- public byte[] getBytes() {
+- byte[] b = new byte[bufSize];
+- System.arraycopy(buf,0,b,0,bufSize);
+- return b;
+- }
+-
+- /**
+- * Resets the buffer
+- */
+- public void clear() {
+- bufSize = 0;
+- }
+-
+- /**
+- * Appends the data to the buffer. If the data is incorrectly formatted, ie, the
data should always start with the
+- * header, false will be returned and the data will be discarded.
+- * @param b - bytes to be appended
+- * @param off - the offset to extract data from
+- * @param len - the number of bytes to append.
+- * @return true if the data was appended correctly. Returns false if the package is
incorrect, ie missing header or something, or the length of data is 0
+- */
+- public boolean append(ByteBuffer b, int len) {
+- int newcount = bufSize + len;
+- if (newcount > buf.length) {
+- expand(newcount);
+- }
+- b.get(buf,bufSize,len);
+-
+- bufSize = newcount;
+-
+- if ( discard ) {
+- if (bufSize > START_DATA.length && (firstIndexOf(buf, 0,
START_DATA) == -1)) {
+- bufSize = 0;
+- log.error("Discarded the package, invalid header");
+- return false;
+- }
+- }
+- return true;
+-
+- }
+-
+- public boolean append(byte i) {
+- int newcount = bufSize + 1;
+- if (newcount > buf.length) {
+- expand(newcount);
+- }
+- buf[bufSize] = i;
+- bufSize = newcount;
+- return true;
+- }
+-
+-
+- public boolean append(boolean i) {
+- int newcount = bufSize + 1;
+- if (newcount > buf.length) {
+- expand(newcount);
+- }
+- XByteBuffer.toBytes(i,buf,bufSize);
+- bufSize = newcount;
+- return true;
+- }
+-
+- public boolean append(long i) {
+- int newcount = bufSize + 8;
+- if (newcount > buf.length) {
+- expand(newcount);
+- }
+- XByteBuffer.toBytes(i,buf,bufSize);
+- bufSize = newcount;
+- return true;
+- }
+-
+- public boolean append(int i) {
+- int newcount = bufSize + 4;
+- if (newcount > buf.length) {
+- expand(newcount);
+- }
+- XByteBuffer.toBytes(i,buf,bufSize);
+- bufSize = newcount;
+- return true;
+- }
+-
+- public boolean append(byte[] b, int off, int len) {
+- if ((off < 0) || (off > b.length) || (len < 0) ||
+- ((off + len) > b.length) || ((off + len) < 0)) {
+- throw new IndexOutOfBoundsException();
+- } else if (len == 0) {
+- return false;
+- }
+-
+- int newcount = bufSize + len;
+- if (newcount > buf.length) {
+- expand(newcount);
+- }
+- System.arraycopy(b, off, buf, bufSize, len);
+- bufSize = newcount;
+-
+- if ( discard ) {
+- if (bufSize > START_DATA.length && (firstIndexOf(buf, 0,
START_DATA) == -1)) {
+- bufSize = 0;
+- log.error("Discarded the package, invalid header");
+- return false;
+- }
+- }
+- return true;
+- }
+-
+- public void expand(int newcount) {
+- //don't change the allocation strategy
+- byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)];
+- System.arraycopy(buf, 0, newbuf, 0, bufSize);
+- buf = newbuf;
+- }
+-
+- public int getCapacity() {
+- return buf.length;
+- }
+-
+-
+- /**
+- * Internal mechanism to make a check if a complete package exists
+- * within the buffer
+- * @return - true if a complete package (header,compress,size,data,footer) exists
within the buffer
+- */
+- public int countPackages() {
+- return countPackages(false);
+- }
+-
+- public int countPackages(boolean first)
+- {
+- int cnt = 0;
+- int pos = START_DATA.length;
+- int start = 0;
+-
+- while ( start < bufSize ) {
+- //first check start header
+- int index = XByteBuffer.firstIndexOf(buf,start,START_DATA);
+- //if the header (START_DATA) isn't the first thing or
+- //the buffer isn't even 14 bytes
+- if ( index != start || ((bufSize-start)<14) ) break;
+- //next 4 bytes are compress flag not needed for count packages
+- //then get the size 4 bytes
+- int size = toInt(buf, pos);
+- //now the total buffer has to be long enough to hold
+- //START_DATA.length+4+size+END_DATA.length
+- pos = start + START_DATA.length + 4 + size;
+- if ( (pos + END_DATA.length) > bufSize) break;
+- //and finally check the footer of the package END_DATA
+- int newpos = firstIndexOf(buf, pos, END_DATA);
+- //mismatch, there is no package
+- if (newpos != pos) break;
+- //increase the packet count
+- cnt++;
+- //reset the values
+- start = pos + END_DATA.length;
+- pos = start + START_DATA.length;
+- //we only want to verify that we have at least one package
+- if ( first ) break;
+- }
+- return cnt;
+- }
+-
+- /**
+- * Method to check if a package exists in this byte buffer.
+- * @return - true if a complete package (header,options,size,data,footer) exists
within the buffer
+- */
+- public boolean doesPackageExist() {
+- return (countPackages(true)>0);
+- }
+-
+- /**
+- * Extracts the message bytes from a package.
+- * If no package exists, a IllegalStateException will be thrown.
+- * @param clearFromBuffer - if true, the package will be removed from the byte
buffer
+- * @return - returns the actual message bytes (header, compress,size and footer not
included).
+- */
+- public XByteBuffer extractDataPackage(boolean clearFromBuffer) {
+- int psize = countPackages(true);
+- if (psize == 0) {
+- throw new java.lang.IllegalStateException("No package exists in
XByteBuffer");
+- }
+- int size = toInt(buf, START_DATA.length);
+- XByteBuffer xbuf = BufferPool.getBufferPool().getBuffer(size,false);
+- xbuf.setLength(size);
+- System.arraycopy(buf, START_DATA.length + 4, xbuf.getBytesDirect(), 0, size);
+- if (clearFromBuffer) {
+- int totalsize = START_DATA.length + 4 + size + END_DATA.length;
+- bufSize = bufSize - totalsize;
+- System.arraycopy(buf, totalsize, buf, 0, bufSize);
+- }
+- return xbuf;
+-
+- }
+-
+- public ChannelData extractPackage(boolean clearFromBuffer) throws
java.io.IOException {
+- XByteBuffer xbuf = extractDataPackage(clearFromBuffer);
+- ChannelData cdata = ChannelData.getDataFromPackage(xbuf);
+- return cdata;
+- }
+-
+- /**
+- * Creates a complete data package
+- * @param indata - the message data to be contained within the package
+- * @param compressed - compression flag for the indata buffer
+- * @return - a full package (header,size,data,footer)
+- *
+- */
+- public static byte[] createDataPackage(ChannelData cdata) {
+-// return createDataPackage(cdata.getDataPackage());
+- //avoid one extra byte array creation
+- int dlength = cdata.getDataPackageLength();
+- int length = getDataPackageLength(dlength);
+- byte[] data = new byte[length];
+- int offset = 0;
+- System.arraycopy(START_DATA, 0, data, offset, START_DATA.length);
+- offset += START_DATA.length;
+- toBytes(dlength,data, START_DATA.length);
+- offset += 4;
+- cdata.getDataPackage(data,offset);
+- offset += dlength;
+- System.arraycopy(END_DATA, 0, data, offset, END_DATA.length);
+- offset += END_DATA.length;
+- return data;
+- }
+-
+- public static byte[] createDataPackage(byte[] data, int doff, int dlength, byte[]
buffer, int bufoff) {
+- if ( (buffer.length-bufoff) > getDataPackageLength(dlength) ) {
+- throw new ArrayIndexOutOfBoundsException("Unable to create data
package, buffer is too small.");
+- }
+- System.arraycopy(START_DATA, 0, buffer, bufoff, START_DATA.length);
+- toBytes(data.length,buffer, bufoff+START_DATA.length);
+- System.arraycopy(data, doff, buffer, bufoff+START_DATA.length + 4, dlength);
+- System.arraycopy(END_DATA, 0, buffer, bufoff+START_DATA.length + 4 +
data.length, END_DATA.length);
+- return buffer;
+- }
+-
+-
+- public static int getDataPackageLength(int datalength) {
+- int length =
+- START_DATA.length + //header length
+- 4 + //data length indicator
+- datalength + //actual data length
+- END_DATA.length; //footer length
+- return length;
+-
+- }
+-
+- public static byte[] createDataPackage(byte[] data) {
+- int length = getDataPackageLength(data.length);
+- byte[] result = new byte[length];
+- return createDataPackage(data,0,data.length,result,0);
+- }
+-
+-
+-
+-// public static void fillDataPackage(byte[] data, int doff, int dlength, XByteBuffer
buf) {
+-// int pkglen = getDataPackageLength(dlength);
+-// if ( buf.getCapacity() < pkglen ) buf.expand(pkglen);
+-// createDataPackage(data,doff,dlength,buf.getBytesDirect(),buf.getLength());
+-// }
+-
+- /**
+- * Convert four bytes to an int
+- * @param b - the byte array containing the four bytes
+- * @param off - the offset
+- * @return the integer value constructed from the four bytes
+- * @exception java.lang.ArrayIndexOutOfBoundsException
+- */
+- public static int toInt(byte[] b,int off){
+- return ( ( (int) b[off+3]) & 0xFF) +
+- ( ( ( (int) b[off+2]) & 0xFF) << 8) +
+- ( ( ( (int) b[off+1]) & 0xFF) << 16) +
+- ( ( ( (int) b[off+0]) & 0xFF) << 24);
+- }
+-
+- /**
+- * Convert eight bytes to a long
+- * @param b - the byte array containing the four bytes
+- * @param off - the offset
+- * @return the long value constructed from the eight bytes
+- * @exception java.lang.ArrayIndexOutOfBoundsException
+- */
+- public static long toLong(byte[] b,int off){
+- return ( ( (long) b[off+7]) & 0xFF) +
+- ( ( ( (long) b[off+6]) & 0xFF) << 8) +
+- ( ( ( (long) b[off+5]) & 0xFF) << 16) +
+- ( ( ( (long) b[off+4]) & 0xFF) << 24) +
+- ( ( ( (long) b[off+3]) & 0xFF) << 32) +
+- ( ( ( (long) b[off+2]) & 0xFF) << 40) +
+- ( ( ( (long) b[off+1]) & 0xFF) << 48) +
+- ( ( ( (long) b[off+0]) & 0xFF) << 56);
+- }
+-
+-
+- /**
+- * Converts an integer to four bytes
+- * @param n - the integer
+- * @return - four bytes in an array
+- * @deprecated use toBytes(boolean,byte[],int)
+- */
+- public static byte[] toBytes(boolean bool) {
+- byte[] b = new byte[1] ;
+- return toBytes(bool,b,0);
+-
+- }
+-
+- public static byte[] toBytes(boolean bool, byte[] data, int offset) {
+- data[offset] = (byte)(bool?1:0);
+- return data;
+- }
+-
+- /**
+- *
+- * @param <any> long
+- * @return use
+- */
+- public static boolean toBoolean(byte[] b, int offset) {
+- return b[offset] != 0;
+- }
+-
+-
+- /**
+- * Converts an integer to four bytes
+- * @param n - the integer
+- * @return - four bytes in an array
+- * @deprecated use toBytes(int,byte[],int)
+- */
+- public static byte[] toBytes(int n) {
+- return toBytes(n,new byte[4],0);
+- }
+-
+- public static byte[] toBytes(int n,byte[] b, int offset) {
+- b[offset+3] = (byte) (n);
+- n >>>= 8;
+- b[offset+2] = (byte) (n);
+- n >>>= 8;
+- b[offset+1] = (byte) (n);
+- n >>>= 8;
+- b[offset+0] = (byte) (n);
+- return b;
+- }
+-
+- /**
+- * Converts an long to eight bytes
+- * @param n - the long
+- * @return - eight bytes in an array
+- * @deprecated use toBytes(long,byte[],int)
+- */
+- public static byte[] toBytes(long n) {
+- return toBytes(n,new byte[8],0);
+- }
+- public static byte[] toBytes(long n, byte[] b, int offset) {
+- b[offset+7] = (byte) (n);
+- n >>>= 8;
+- b[offset+6] = (byte) (n);
+- n >>>= 8;
+- b[offset+5] = (byte) (n);
+- n >>>= 8;
+- b[offset+4] = (byte) (n);
+- n >>>= 8;
+- b[offset+3] = (byte) (n);
+- n >>>= 8;
+- b[offset+2] = (byte) (n);
+- n >>>= 8;
+- b[offset+1] = (byte) (n);
+- n >>>= 8;
+- b[offset+0] = (byte) (n);
+- return b;
+- }
+-
+- /**
+- * Similar to a String.IndexOf, but uses pure bytes
+- * @param src - the source bytes to be searched
+- * @param srcOff - offset on the source buffer
+- * @param find - the string to be found within src
+- * @return - the index of the first matching byte. -1 if the find array is not
found
+- */
+- public static int firstIndexOf(byte[] src, int srcOff, byte[] find){
+- int result = -1;
+- if (find.length > src.length) return result;
+- if (find.length == 0 || src.length == 0) return result;
+- if (srcOff >= src.length ) throw new
java.lang.ArrayIndexOutOfBoundsException();
+- boolean found = false;
+- int srclen = src.length;
+- int findlen = find.length;
+- byte first = find[0];
+- int pos = srcOff;
+- while (!found) {
+- //find the first byte
+- while (pos < srclen){
+- if (first == src[pos])
+- break;
+- pos++;
+- }
+- if (pos >= srclen)
+- return -1;
+-
+- //we found the first character
+- //match the rest of the bytes - they have to match
+- if ( (srclen - pos) < findlen)
+- return -1;
+- //assume it does exist
+- found = true;
+- for (int i = 1; ( (i < findlen) && found); i++)
+- found = found && (find[i] == src[pos + i]);
+- if (found)
+- result = pos;
+- else if ( (srclen - pos) < findlen)
+- return -1; //no more matches possible
+- else
+- pos++;
+- }
+- return result;
+- }
+-
+-
+- public static Serializable deserialize(byte[] data)
+- throws IOException, ClassNotFoundException, ClassCastException {
+- return deserialize(data,0,data.length);
+- }
+-
+- public static Serializable deserialize(byte[] data, int offset, int length)
+- throws IOException, ClassNotFoundException, ClassCastException {
+- return deserialize(data,offset,length,null);
+- }
+- public static int invokecount = 0;
+- public static Serializable deserialize(byte[] data, int offset, int length,
ClassLoader[] cls)
+- throws IOException, ClassNotFoundException, ClassCastException {
+- synchronized (XByteBuffer.class) { invokecount++;}
+- Object message = null;
+- if ( cls == null ) cls = new ClassLoader[0];
+- if (data != null) {
+- InputStream instream = new ByteArrayInputStream(data,offset,length);
+- ObjectInputStream stream = null;
+- stream = (cls.length>0)? new ReplicationStream(instream,cls):new
ObjectInputStream(instream);
+- message = stream.readObject();
+- instream.close();
+- stream.close();
+- }
+- if ( message == null ) {
+- return null;
+- } else if (message instanceof Serializable)
+- return (Serializable) message;
+- else {
+- throw new ClassCastException("Message has the wrong class. It should
implement Serializable, instead it is:"+message.getClass().getName());
+- }
+- }
+-
+- /**
+- * Serializes a message into cluster data
+- * @param msg ClusterMessage
+- * @param compress boolean
+- * @return
+- * @throws IOException
+- */
+- public static byte[] serialize(Serializable msg) throws IOException {
+- ByteArrayOutputStream outs = new ByteArrayOutputStream();
+- ObjectOutputStream out = new ObjectOutputStream(outs);
+- out.writeObject(msg);
+- out.flush();
+- byte[] data = outs.toByteArray();
+- return data;
+- }
+-
+- public void setDiscard(boolean discard) {
+- this.discard = discard;
+- }
+-
+- public boolean getDiscard() {
+- return discard;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/io/ObjectReader.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/ObjectReader.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/ObjectReader.java (working copy)
+@@ -1,165 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.io;
+-
+-import java.io.IOException;
+-import java.net.Socket;
+-import java.nio.ByteBuffer;
+-import java.nio.channels.SocketChannel;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-
+-
+-
+-/**
+- * The object reader object is an object used in conjunction with
+- * java.nio TCP messages. This object stores the message bytes in a
+- * <code>XByteBuffer</code> until a full package has been received.
+- * This object uses an XByteBuffer which is an extendable object buffer that also
allows
+- * for message encoding and decoding.
+- *
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public class ObjectReader {
+-
+- protected static org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog(ObjectReader.class);
+-
+- private XByteBuffer buffer;
+-
+- protected long lastAccess = System.currentTimeMillis();
+-
+- protected boolean accessed = false;
+- private boolean cancelled;
+-
+- /**
+- * Creates an <code>ObjectReader</code> for a TCP NIO socket channel
+- * @param channel - the channel to be read.
+- */
+- public ObjectReader(SocketChannel channel) {
+- this(channel.socket());
+- }
+-
+- /**
+- * Creates an <code>ObjectReader</code> for a TCP socket
+- * @param socket Socket
+- */
+- public ObjectReader(Socket socket) {
+- try{
+- this.buffer = new XByteBuffer(socket.getReceiveBufferSize(), true);
+- }catch ( IOException x ) {
+- //unable to get buffer size
+- log.warn("Unable to retrieve the socket receiver buffer size, setting
to default 43800 bytes.");
+- this.buffer = new XByteBuffer(43800,true);
+- }
+- }
+-
+- public synchronized void access() {
+- this.accessed = true;
+- this.lastAccess = System.currentTimeMillis();
+- }
+-
+- public synchronized void finish() {
+- this.accessed = false;
+- this.lastAccess = System.currentTimeMillis();
+- }
+-
+- public boolean isAccessed() {
+- return this.accessed;
+- }
+-
+- /**
+- * Append new bytes to buffer.
+- * @see XByteBuffer#countPackages()
+- * @param data new transfer buffer
+- * @param off offset
+- * @param len length in buffer
+- * @return number of messages that sended to callback
+- * @throws java.io.IOException
+- */
+- public int append(ByteBuffer data, int len, boolean count) throws
java.io.IOException {
+- buffer.append(data,len);
+- int pkgCnt = -1;
+- if ( count ) pkgCnt = buffer.countPackages();
+- return pkgCnt;
+- }
+-
+- public int append(byte[] data,int off,int len, boolean count) throws
java.io.IOException {
+- buffer.append(data,off,len);
+- int pkgCnt = -1;
+- if ( count ) pkgCnt = buffer.countPackages();
+- return pkgCnt;
+- }
+-
+- /**
+- * Send buffer to cluster listener (callback).
+- * Is message complete receiver send message to callback?
+- *
+- * @see
org.apache.catalina.tribes.transport.ClusterReceiverBase#messageDataReceived(ChannelMessage)
+- * @see XByteBuffer#doesPackageExist()
+- * @see XByteBuffer#extractPackage(boolean)
+- *
+- * @return number of received packages/messages
+- * @throws java.io.IOException
+- */
+- public ChannelMessage[] execute() throws java.io.IOException {
+- int pkgCnt = buffer.countPackages();
+- ChannelMessage[] result = new ChannelMessage[pkgCnt];
+- for (int i=0; i<pkgCnt; i++) {
+- ChannelMessage data = buffer.extractPackage(true);
+- result[i] = data;
+- }
+- return result;
+- }
+-
+- public int bufferSize() {
+- return buffer.getLength();
+- }
+-
+-
+- public boolean hasPackage() {
+- return buffer.countPackages(true)>0;
+- }
+- /**
+- * Returns the number of packages that the reader has read
+- * @return int
+- */
+- public int count() {
+- return buffer.countPackages();
+- }
+-
+- public void close() {
+- this.buffer = null;
+- }
+-
+- public long getLastAccess() {
+- return lastAccess;
+- }
+-
+- public boolean isCancelled() {
+- return cancelled;
+- }
+-
+- public void setLastAccess(long lastAccess) {
+- this.lastAccess = lastAccess;
+- }
+-
+- public void setCancelled(boolean cancelled) {
+- this.cancelled = cancelled;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/io/BufferPool14Impl.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/BufferPool14Impl.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/BufferPool14Impl.java (working copy)
+@@ -1,69 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.io;
+-
+-import java.util.LinkedList;
+-
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-class BufferPool14Impl implements BufferPool.BufferPoolAPI {
+- protected int maxSize;
+- protected int size = 0;
+- protected LinkedList queue = new LinkedList();
+-
+- public void setMaxSize(int bytes) {
+- this.maxSize = bytes;
+- }
+-
+- public synchronized int addAndGet(int val) {
+- size = size + (val);
+- return size;
+- }
+-
+-
+-
+- public synchronized XByteBuffer getBuffer(int minSize, boolean discard) {
+- XByteBuffer buffer = (XByteBuffer)(queue.size()>0?queue.remove(0):null);
+- if ( buffer != null ) addAndGet(-buffer.getCapacity());
+- if ( buffer == null ) buffer = new XByteBuffer(minSize,discard);
+- else if ( buffer.getCapacity() <= minSize ) buffer.expand(minSize);
+- buffer.setDiscard(discard);
+- buffer.reset();
+- return buffer;
+- }
+-
+- public synchronized void returnBuffer(XByteBuffer buffer) {
+- if ( (size + buffer.getCapacity()) <= maxSize ) {
+- addAndGet(buffer.getCapacity());
+- queue.add(buffer);
+- }
+- }
+-
+- public synchronized void clear() {
+- queue.clear();
+- size = 0;
+- }
+-
+- public int getMaxSize() {
+- return maxSize;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java (revision
590752)
++++ java/org/apache/catalina/tribes/io/DirectByteArrayOutputStream.java (working copy)
+@@ -1,63 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.io;
+-
+-import java.io.IOException;
+-import java.io.OutputStream;
+-
+-/**
+- * Byte array output stream that exposes the byte array directly
+- *
+- * @author not attributable
+- * @version 1.0
+- */
+-public class DirectByteArrayOutputStream extends OutputStream {
+-
+- private XByteBuffer buffer;
+-
+- public DirectByteArrayOutputStream(int size) {
+- buffer = new XByteBuffer(size,false);
+- }
+-
+- /**
+- * Writes the specified byte to this output stream.
+- *
+- * @param b the <code>byte</code>.
+- * @throws IOException if an I/O error occurs. In particular, an
+- * <code>IOException</code> may be thrown if the output stream has
+- * been closed.
+- * @todo Implement this java.io.OutputStream method
+- */
+- public void write(int b) throws IOException {
+- buffer.append((byte)b);
+- }
+-
+- public int size() {
+- return buffer.getLength();
+- }
+-
+- public byte[] getArrayDirect() {
+- return buffer.getBytesDirect();
+- }
+-
+- public byte[] getArray() {
+- return buffer.getBytes();
+- }
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/io/BufferPool15Impl.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/BufferPool15Impl.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/BufferPool15Impl.java (working copy)
+@@ -1,63 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.io;
+-
+-import java.util.concurrent.ConcurrentLinkedQueue;
+-import java.util.concurrent.atomic.AtomicInteger;
+-
+-/**
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-class BufferPool15Impl implements BufferPool.BufferPoolAPI {
+- protected int maxSize;
+- protected AtomicInteger size = new AtomicInteger(0);
+- protected ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
+-
+- public void setMaxSize(int bytes) {
+- this.maxSize = bytes;
+- }
+-
+-
+- public XByteBuffer getBuffer(int minSize, boolean discard) {
+- XByteBuffer buffer = (XByteBuffer)queue.poll();
+- if ( buffer != null ) size.addAndGet(-buffer.getCapacity());
+- if ( buffer == null ) buffer = new XByteBuffer(minSize,discard);
+- else if ( buffer.getCapacity() <= minSize ) buffer.expand(minSize);
+- buffer.setDiscard(discard);
+- buffer.reset();
+- return buffer;
+- }
+-
+- public void returnBuffer(XByteBuffer buffer) {
+- if ( (size.get() + buffer.getCapacity()) <= maxSize ) {
+- size.addAndGet(buffer.getCapacity());
+- queue.offer(buffer);
+- }
+- }
+-
+- public void clear() {
+- queue.clear();
+- size.set(0);
+- }
+-
+- public int getMaxSize() {
+- return maxSize;
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/io/ListenCallback.java
+===================================================================
+--- java/org/apache/catalina/tribes/io/ListenCallback.java (revision 590752)
++++ java/org/apache/catalina/tribes/io/ListenCallback.java (working copy)
+@@ -1,42 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes.io;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-
+-
+-
+-/**
+- * Internal interface, similar to the MessageListener but used
+- * at the IO base
+- * The listen callback interface is used by the replication system
+- * when data has been received. The interface does not care about
+- * objects and marshalling and just passes the bytes straight through.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public interface ListenCallback
+-{
+- /**
+- * This method is invoked on the callback object to notify it that new data has
+- * been received from one of the cluster nodes.
+- * @param data - the message bytes received from the cluster/replication system
+- */
+- public void messageDataReceived(ChannelMessage data);
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/ErrorHandler.java
+===================================================================
+--- java/org/apache/catalina/tribes/ErrorHandler.java (revision 590752)
++++ java/org/apache/catalina/tribes/ErrorHandler.java (working copy)
+@@ -1,46 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes;
+-
+-
+-
+-/**
+- * The <code>ErrorHandler</code> class is used when sending messages
+- * that are sent asynchronously and the application still needs to get
+- * confirmation when the message was sent successfully or when a message errored out.
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public interface ErrorHandler {
+-
+- /**
+- * Invoked if the message is dispatched asynch, and an error occurs
+- * @param x ChannelException - the error that happened
+- * @param id - the unique id for the message
+- * @see Channel#send(Member[], Serializable, int, ErrorHandler)
+- */
+- public void handleError(ChannelException x, UniqueId id);
+-
+- /**
+- * Invoked when the message has been sent successfully.
+- * @param id - the unique id for the message
+- * @see Channel#send(Member[], Serializable, int, ErrorHandler)
+- */
+- public void handleCompletion(UniqueId id);
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/ChannelInterceptor.java
+===================================================================
+--- java/org/apache/catalina/tribes/ChannelInterceptor.java (revision 590752)
++++ java/org/apache/catalina/tribes/ChannelInterceptor.java (working copy)
+@@ -1,180 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import org.apache.catalina.tribes.group.InterceptorPayload;
+-
+-/**
+- * A ChannelInterceptor is an interceptor that intercepts
+- * messages and membership messages in the channel stack.
+- * This allows interceptors to modify the message or perform
+- * other actions when a message is sent or received.<br>
+- * Interceptors are tied together in a linked list.
+- * @see org.apache.catalina.tribes.group.ChannelInterceptorBase
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public interface ChannelInterceptor extends MembershipListener, Heartbeat {
+-
+- /**
+- * An interceptor can react to a message based on a set bit on the
+- * message options. <br>
+- * When a message is sent, the options can be retrieved from
ChannelMessage.getOptions()
+- * and if the bit is set, this interceptor will react to it.<br>
+- * A simple evaluation if an interceptor should react to the message would
be:<br>
+- * <code>boolean react = (getOptionFlag() == (getOptionFlag() &
ChannelMessage.getOptions()));</code><br>
+- * The default option is 0, meaning there is no way for the application to trigger
the
+- * interceptor. The interceptor itself will decide.<br>
+- * @return int
+- * @see ChannelMessage#getOptions()
+- */
+- public int getOptionFlag();
+-
+- /**
+- * Sets the option flag
+- * @param flag int
+- * @see #getOptionFlag()
+- */
+- public void setOptionFlag(int flag);
+-
+- /**
+- * Set the next interceptor in the list of interceptors
+- * @param next ChannelInterceptor
+- */
+- public void setNext(ChannelInterceptor next) ;
+-
+- /**
+- * Retrieve the next interceptor in the list
+- * @return ChannelInterceptor - returns the next interceptor in the list or null if
no more interceptors exist
+- */
+- public ChannelInterceptor getNext();
+-
+- /**
+- * Set the previous interceptor in the list
+- * @param previous ChannelInterceptor
+- */
+- public void setPrevious(ChannelInterceptor previous);
+-
+- /**
+- * Retrieve the previous interceptor in the list
+- * @return ChannelInterceptor - returns the previous interceptor in the list or null
if no more interceptors exist
+- */
+- public ChannelInterceptor getPrevious();
+-
+- /**
+- * The <code>sendMessage</code> method is called when a message is being
sent to one more destinations.
+- * The interceptor can modify any of the parameters and then pass on the message
down the stack by
+- * invoking
<code>getNext().sendMessage(destination,msg,payload)</code><br>
+- * Alternatively the interceptor can stop the message from being sent by not
invoking
+- *
<code>getNext().sendMessage(destination,msg,payload)</code><br>
+- * If the message is to be sent asynchronous the application can be notified of
completion and
+- * errors by passing in an error handler attached to a payload object.<br>
+- * The ChannelMessage.getAddress contains Channel.getLocalMember, and can be
overwritten
+- * to simulate a message sent from another node.<br>
+- * @param destination Member[] - the destination for this message
+- * @param msg ChannelMessage - the message to be sent
+- * @param payload InterceptorPayload - the payload, carrying an error handler and
future useful data, can be null
+- * @throws ChannelException
+- * @see ErrorHandler
+- * @see InterceptorPayload
+- */
+- public void sendMessage(Member[] destination, ChannelMessage msg, InterceptorPayload
payload) throws ChannelException;
+-
+- /**
+- * the <code>messageReceived</code> is invoked when a message is
received.
+- * <code>ChannelMessage.getAddress()</code> is the sender, or the
reply-to address
+- * if it has been overwritten.
+- * @param data ChannelMessage
+- */
+- public void messageReceived(ChannelMessage data);
+-
+- /**
+- * The <code>heartbeat()</code> method gets invoked periodically
+- * to allow interceptors to clean up resources, time out object and
+- * perform actions that are unrelated to sending/receiving data.
+- */
+- public void heartbeat();
+-
+- /**
+- * Intercepts the <code>Channel.hasMembers()</code> method
+- * @return boolean - if the channel has members in its membership group
+- * @see Channel#hasMembers()
+- */
+- public boolean hasMembers() ;
+-
+- /**
+- * Intercepts the code>Channel.getMembers()</code> method
+- * @return Member[]
+- * @see Channel#getMembers()
+- */
+- public Member[] getMembers() ;
+-
+- /**
+- * Intercepts the code>Channel.getLocalMember(boolean)</code> method
+- * @param incAliveTime boolean
+- * @return Member
+- * @see Channel#getLocalMember(boolean)
+- */
+- public Member getLocalMember(boolean incAliveTime) ;
+-
+- /**
+- * Intercepts the code>Channel.getMember(Member)</code> method
+- * @param mbr Member
+- * @return Member - the actual member information, including stay alive
+- * @see Channel#getMember(Member)
+- */
+- public Member getMember(Member mbr);
+-
+- /**
+- * Starts up the channel. This can be called multiple times for individual services
to start
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * Channel.DEFAULT - will start all services <BR>
+- * Channel.MBR_RX_SEQ - starts the membership receiver <BR>
+- * Channel.MBR_TX_SEQ - starts the membership broadcaster <BR>
+- * Channel.SND_TX_SEQ - starts the replication transmitter<BR>
+- * Channel.SND_RX_SEQ - starts the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- * @see Channel
+- */
+- public void start(int svc) throws ChannelException;
+-
+- /**
+- * Shuts down the channel. This can be called multiple times for individual services
to shutdown
+- * The svc parameter can be the logical or value of any constants
+- * @param svc int value of <BR>
+- * Channel.DEFAULT - will shutdown all services <BR>
+- * Channel.MBR_RX_SEQ - stops the membership receiver <BR>
+- * Channel.MBR_TX_SEQ - stops the membership broadcaster <BR>
+- * Channel.SND_TX_SEQ - stops the replication transmitter<BR>
+- * Channel.SND_RX_SEQ - stops the replication receiver<BR>
+- * @throws ChannelException if a startup error occurs or the service is already
started.
+- * @see Channel
+- */
+- public void stop(int svc) throws ChannelException;
+-
+- public void fireInterceptorEvent(InterceptorEvent event);
+-
+- interface InterceptorEvent {
+- int getEventType();
+- String getEventTypeDesc();
+- ChannelInterceptor getInterceptor();
+- }
+-
+-
+-}
+Index: java/org/apache/catalina/tribes/ChannelException.java
+===================================================================
+--- java/org/apache/catalina/tribes/ChannelException.java (revision 590752)
++++ java/org/apache/catalina/tribes/ChannelException.java (working copy)
+@@ -1,178 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-import java.util.ArrayList;
+-
+-/**
+- * Channel Exception<br>
+- * A channel exception is thrown when an internal error happens
+- * somewhere in the channel. <br>
+- * When a global error happens, the cause can be retrieved using
<code>getCause()</code><br><br>
+- * If an application is sending a message and some of the recipients fail to receive
it,
+- * the application can retrieve what recipients failed by using the
<code>getFaultyMembers()</code>
+- * method. This way, an application will always know if a message was delivered
successfully or not.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-
+-public class ChannelException extends Exception {
+- /**
+- * Empty list to avoid reinstatiating lists
+- */
+- protected static final FaultyMember[] EMPTY_LIST = new FaultyMember[0];
+- /*
+- * Holds a list of faulty members
+- */
+- private ArrayList faultyMembers=null;
+-
+- /**
+- * Constructor, creates a ChannelException
+- * @see java.lang.Exception#Exception()
+- */
+- public ChannelException() {
+- super();
+- }
+-
+- /**
+- * Constructor, creates a ChannelException with an error message
+- * @see java.lang.Exception#Exception(String)
+- */
+- public ChannelException(String message) {
+- super(message);
+- }
+-
+- /**
+- * Constructor, creates a ChannelException with an error message and a cause
+- * @param message String
+- * @param cause Throwable
+- * @see java.lang.Exception#Exception(String,Throwable)
+- */
+- public ChannelException(String message, Throwable cause) {
+- super(message, cause);
+- }
+-
+- /**
+- * Constructor, creates a ChannelException with a cause
+- * @param cause Throwable
+- * @see java.lang.Exception#Exception(Throwable)
+- */
+- public ChannelException(Throwable cause) {
+- super(cause);
+- }
+-
+- /**
+- * Returns the message for this exception
+- * @return String
+- * @see java.lang.Exception#getMessage()
+- */
+- public String getMessage() {
+- StringBuffer buf = new StringBuffer(super.getMessage());
+- if (faultyMembers==null || faultyMembers.size() == 0 ) {
+- buf.append("; No faulty members identified.");
+- } else {
+- buf.append("; Faulty members:");
+- for ( int i=0; i<faultyMembers.size(); i++ ) {
+- FaultyMember mbr = (FaultyMember)faultyMembers.get(i);
+- buf.append(mbr.getMember().getName());
+- buf.append("; ");
+- }
+- }
+- return buf.toString();
+- }
+-
+- /**
+- * Adds a faulty member, and the reason the member failed.
+- * @param mbr Member
+- * @param x Exception
+- */
+- public boolean addFaultyMember(Member mbr, Exception x ) {
+- return addFaultyMember(new FaultyMember(mbr,x));
+- }
+-
+- /**
+- * Adds a list of faulty members
+- * @param mbrs FaultyMember[]
+- */
+- public int addFaultyMember(FaultyMember[] mbrs) {
+- int result = 0;
+- for (int i=0; mbrs!=null && i<mbrs.length; i++ ) {
+- if ( addFaultyMember(mbrs[i]) ) result++;
+- }
+- return result;
+- }
+-
+- /**
+- * Adds a faulty member
+- * @param mbr FaultyMember
+- */
+- public boolean addFaultyMember(FaultyMember mbr) {
+- if ( this.faultyMembers==null ) this.faultyMembers = new ArrayList();
+- if ( !faultyMembers.contains(mbr) ) return faultyMembers.add(mbr);
+- else return false;
+- }
+-
+- /**
+- * Returns an array of members that failed and the reason they failed.
+- * @return FaultyMember[]
+- */
+- public FaultyMember[] getFaultyMembers() {
+- if ( this.faultyMembers==null ) return EMPTY_LIST;
+- return (FaultyMember[])faultyMembers.toArray(new
FaultyMember[faultyMembers.size()]);
+- }
+-
+- /**
+- *
+- * <p>Title: FaultyMember class</p>
+- *
+- * <p>Description: Represent a failure to a specific member when a message was
sent
+- * to more than one member</p>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+- public static class FaultyMember {
+- protected Exception cause;
+- protected Member member;
+- public FaultyMember(Member mbr, Exception x) {
+- this.member = mbr;
+- this.cause = x;
+- }
+-
+- public Member getMember() {
+- return member;
+- }
+-
+- public Exception getCause() {
+- return cause;
+- }
+-
+- public String toString() {
+- return "FaultyMember:"+member.toString();
+- }
+-
+- public int hashCode() {
+- return (member!=null)?member.hashCode():0;
+- }
+-
+- public boolean equals(Object o) {
+- if (member==null || (!(o instanceof FaultyMember)) ||
(((FaultyMember)o).member==null)) return false;
+- return member.equals(((FaultyMember)o).member);
+- }
+- }
+-
+-}
+Index: java/org/apache/catalina/tribes/MessageListener.java
+===================================================================
+--- java/org/apache/catalina/tribes/MessageListener.java (revision 590752)
++++ java/org/apache/catalina/tribes/MessageListener.java (working copy)
+@@ -1,43 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes;
+-
+-/**
+- *
+- * <p>Title: MessageListener</p>
+- *
+- * <p>Description: The listener to be registered with the ChannelReceiver,
internal Tribes component</p>
+- *
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-
+-public interface MessageListener {
+-
+- /**
+- * Receive a message from the IO components in the Channel stack
+- * @param msg ChannelMessage
+- */
+- public void messageReceived(ChannelMessage msg);
+-
+- public boolean accept(ChannelMessage msg);
+-
+- public boolean equals(Object listener);
+-
+- public int hashCode();
+-
+-}
+Index: java/org/apache/catalina/tribes/util/Arrays.java
+===================================================================
+--- java/org/apache/catalina/tribes/util/Arrays.java (revision 590752)
++++ java/org/apache/catalina/tribes/util/Arrays.java (working copy)
+@@ -1,221 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.util;
+-
+-import java.util.ArrayList;
+-import java.util.List;
+-
+-import org.apache.catalina.tribes.ChannelMessage;
+-import org.apache.catalina.tribes.Member;
+-import org.apache.catalina.tribes.UniqueId;
+-import org.apache.catalina.tribes.group.AbsoluteOrder;
+-import org.apache.catalina.tribes.membership.MemberImpl;
+-import org.apache.catalina.tribes.membership.Membership;
+-import java.io.UnsupportedEncodingException;
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-import java.util.StringTokenizer;
+-
+-/**
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class Arrays {
+- protected static Log log = LogFactory.getLog(Arrays.class);
+-
+- public static boolean contains(byte[] source, int srcoffset, byte[] key, int
keyoffset, int length) {
+- if ( srcoffset < 0 || srcoffset >= source.length) throw new
ArrayIndexOutOfBoundsException("srcoffset is out of bounds.");
+- if ( keyoffset < 0 || keyoffset >= key.length) throw new
ArrayIndexOutOfBoundsException("keyoffset is out of bounds.");
+- if ( length > (key.length-keyoffset) ) throw new
ArrayIndexOutOfBoundsException("not enough data elements in the key, length is out of
bounds.");
+- //we don't have enough data to validate it
+- if ( length > (source.length-srcoffset) ) return false;
+- boolean match = true;
+- int pos = keyoffset;
+- for ( int i=srcoffset; match && i<length; i++ ) {
+- match = (source[i] == key[pos++]);
+- }
+- return match;
+- }
+-
+- public static String toString(byte[] data) {
+- return toString(data,0,data!=null?data.length:0);
+- }
+-
+- public static String toString(byte[] data, int offset, int length) {
+- StringBuffer buf = new StringBuffer("{");
+- if ( data != null && length > 0 ) {
+- buf.append(data[offset++]);
+- for (int i = offset; i < length; i++) {
+- buf.append(", ").append(data[i]);
+- }
+- }
+- buf.append("}");
+- return buf.toString();
+- }
+-
+- public static String toString(Object[] data) {
+- return toString(data,0,data!=null?data.length:0);
+- }
+-
+- public static String toString(Object[] data, int offset, int length) {
+- StringBuffer buf = new StringBuffer("{");
+- if ( data != null && length > 0 ) {
+- buf.append(data[offset++]);
+- for (int i = offset; i < length; i++) {
+- buf.append(", ").append(data[i]);
+- }
+- }
+- buf.append("}");
+- return buf.toString();
+- }
+-
+- public static String toNameString(Member[] data) {
+- return toNameString(data,0,data!=null?data.length:0);
+- }
+-
+- public static String toNameString(Member[] data, int offset, int length) {
+- StringBuffer buf = new StringBuffer("{");
+- if ( data != null && length > 0 ) {
+- buf.append(data[offset++].getName());
+- for (int i = offset; i < length; i++) {
+- buf.append(", ").append(data[i].getName());
+- }
+- }
+- buf.append("}");
+- return buf.toString();
+- }
+-
+- public static int add(int[] data) {
+- int result = 0;
+- for (int i=0;i<data.length; i++ ) result += data[i];
+- return result;
+- }
+-
+- public static UniqueId getUniqudId(ChannelMessage msg) {
+- return new UniqueId(msg.getUniqueId());
+- }
+-
+- public static UniqueId getUniqudId(byte[] data) {
+- return new UniqueId(data);
+- }
+-
+- public static boolean equals(byte[] o1, byte[] o2) {
+- return java.util.Arrays.equals(o1,o2);
+- }
+-
+- public static boolean equals(Object[] o1, Object[] o2) {
+- boolean result = o1.length == o2.length;
+- if ( result ) for (int i=0; i<o1.length && result; i++ ) result =
o1[i].equals(o2[i]);
+- return result;
+- }
+-
+- public static boolean sameMembers(Member[] m1, Member[] m2) {
+- AbsoluteOrder.absoluteOrder(m1);
+- AbsoluteOrder.absoluteOrder(m2);
+- return equals(m1,m2);
+- }
+-
+- public static Member[] merge(Member[] m1, Member[] m2) {
+- AbsoluteOrder.absoluteOrder(m1);
+- AbsoluteOrder.absoluteOrder(m2);
+- ArrayList list = new ArrayList(java.util.Arrays.asList(m1));
+- for (int i=0; i<m2.length; i++) if ( !list.contains(m2[i]) )
list.add(m2[i]);
+- Member[] result = new Member[list.size()];
+- list.toArray(result);
+- AbsoluteOrder.absoluteOrder(result);
+- return result;
+- }
+-
+- public static void fill(Membership mbrship, Member[] m) {
+- for (int i=0; i<m.length; i++ ) mbrship.addMember((MemberImpl)m[i]);
+- }
+-
+- public static Member[] diff(Membership complete, Membership local, MemberImpl
ignore) {
+- ArrayList result = new ArrayList();
+- MemberImpl[] comp = complete.getMembers();
+- for ( int i=0; i<comp.length; i++ ) {
+- if ( ignore!=null && ignore.equals(comp[i]) ) continue;
+- if ( local.getMember(comp[i]) == null ) result.add(comp[i]);
+- }
+- return (MemberImpl[])result.toArray(new MemberImpl[result.size()]);
+- }
+-
+- public static Member[] remove(Member[] all, Member remove) {
+- return extract(all,new Member[] {remove});
+- }
+-
+- public static Member[] extract(Member[] all, Member[] remove) {
+- List alist = java.util.Arrays.asList(all);
+- ArrayList list = new ArrayList(alist);
+- for (int i=0; i<remove.length; i++ ) list.remove(remove[i]);
+- return (Member[])list.toArray(new Member[list.size()]);
+- }
+-
+- public static int indexOf(Member member, Member[] members) {
+- int result = -1;
+- for (int i=0; (result==-1) && (i<members.length); i++ )
+- if ( member.equals(members[i]) ) result = i;
+- return result;
+- }
+-
+- public static int nextIndex(Member member, Member[] members) {
+- int idx = indexOf(member,members)+1;
+- if (idx >= members.length ) idx = ((members.length>0)?0:-1);
+-
+-//System.out.println("Next index:"+idx);
+-//System.out.println("Member:"+member.getName());
+-//System.out.println("Members:"+toNameString(members));
+- return idx;
+- }
+-
+- public static int hashCode(byte a[]) {
+- if (a == null)
+- return 0;
+-
+- int result = 1;
+- for (int i=0; i<a.length; i++) {
+- byte element = a[i];
+- result = 31 * result + element;
+- }
+- return result;
+- }
+-
+- public static byte[] fromString(String value) {
+- if ( value == null ) return null;
+- if ( !value.startsWith("{") ) throw new RuntimeException("byte
arrays must be represented as {1,3,4,5,6}");
+- StringTokenizer t = new StringTokenizer(value,"{,}",false);
+- byte[] result = new byte[t.countTokens()];
+- for (int i=0; i<result.length; i++ ) result[i] =
Byte.parseByte(t.nextToken());
+- return result;
+- }
+-
+-
+-
+- public static byte[] convert(String s) {
+- try {
+- return s.getBytes("ISO-8859-1");
+- }catch (UnsupportedEncodingException ux ) {
+- log.error("Unable to convert ["+s+"] into a byte[] using
ISO-8859-1 encoding, falling back to default encoding.");
+- return s.getBytes();
+- }
+- }
+-
+-
+-
+-
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/util/StringManager.java
+===================================================================
+--- java/org/apache/catalina/tribes/util/StringManager.java (revision 590752)
++++ java/org/apache/catalina/tribes/util/StringManager.java (working copy)
+@@ -1,253 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-
+-package org.apache.catalina.tribes.util;
+-
+-import java.text.MessageFormat;
+-import java.util.Hashtable;
+-import java.util.Locale;
+-import java.util.MissingResourceException;
+-import java.util.ResourceBundle;
+-import java.net.URLClassLoader;
+-
+-/**
+- * An internationalization / localization helper class which reduces
+- * the bother of handling ResourceBundles and takes care of the
+- * common cases of message formating which otherwise require the
+- * creation of Object arrays and such.
+- *
+- * <p>The StringManager operates on a package basis. One StringManager
+- * per package can be created and accessed via the getManager method
+- * call.
+- *
+- * <p>The StringManager will look for a ResourceBundle named by
+- * the package name given plus the suffix of "LocalStrings". In
+- * practice, this means that the localized information will be contained
+- * in a LocalStrings.properties file located in the package
+- * directory of the classpath.
+- *
+- * <p>Please see the documentation for java.util.ResourceBundle for
+- * more information.
+- *
+- * @author James Duncan Davidson [duncan(a)eng.sun.com]
+- * @author James Todd [gonzo(a)eng.sun.com]
+- */
+-
+-public class StringManager {
+-
+- /**
+- * The ResourceBundle for this StringManager.
+- */
+-
+- private ResourceBundle bundle;
+-
+- private static org.apache.juli.logging.Log log=
+- org.apache.juli.logging.LogFactory.getLog( StringManager.class );
+-
+- /**
+- * Creates a new StringManager for a given package. This is a
+- * private method and all access to it is arbitrated by the
+- * static getManager method call so that only one StringManager
+- * per package will be created.
+- *
+- * @param packageName Name of package to create StringManager for.
+- */
+-
+- private StringManager(String packageName) {
+- String bundleName = packageName + ".LocalStrings";
+- try {
+- bundle = ResourceBundle.getBundle(bundleName);
+- return;
+- } catch( MissingResourceException ex ) {
+- // Try from the current loader ( that's the case for trusted apps )
+- ClassLoader cl=Thread.currentThread().getContextClassLoader();
+- if( cl != null ) {
+- try {
+- bundle=ResourceBundle.getBundle(bundleName, Locale.getDefault(),
cl);
+- return;
+- } catch(MissingResourceException ex2) {
+- }
+- }
+- if( cl==null )
+- cl=this.getClass().getClassLoader();
+-
+- if (log.isDebugEnabled())
+- log.debug("Can't find resource " + bundleName +
+- " " + cl);
+- if( cl instanceof URLClassLoader ) {
+- if (log.isDebugEnabled())
+- log.debug( ((URLClassLoader)cl).getURLs());
+- }
+- }
+- }
+-
+- /**
+- * Get a string from the underlying resource bundle.
+- *
+- * @param key The resource name
+- */
+- public String getString(String key) {
+- return MessageFormat.format(getStringInternal(key), (Object [])null);
+- }
+-
+-
+- protected String getStringInternal(String key) {
+- if (key == null) {
+- String msg = "key is null";
+-
+- throw new NullPointerException(msg);
+- }
+-
+- String str = null;
+-
+- if( bundle==null )
+- return key;
+- try {
+- str = bundle.getString(key);
+- } catch (MissingResourceException mre) {
+- str = "Cannot find message associated with key '" + key +
"'";
+- }
+-
+- return str;
+- }
+-
+- /**
+- * Get a string from the underlying resource bundle and format
+- * it with the given set of arguments.
+- *
+- * @param key The resource name
+- * @param args Formatting directives
+- */
+-
+- public String getString(String key, Object[] args) {
+- String iString = null;
+- String value = getStringInternal(key);
+-
+- // this check for the runtime exception is some pre 1.1.6
+- // VM's don't do an automatic toString() on the passed in
+- // objects and barf out
+-
+- try {
+- // ensure the arguments are not null so pre 1.2 VM's don't barf
+- Object nonNullArgs[] = args;
+- for (int i=0; i<args.length; i++) {
+- if (args[i] == null) {
+- if (nonNullArgs==args) nonNullArgs=(Object[])args.clone();
+- nonNullArgs[i] = "null";
+- }
+- }
+-
+- iString = MessageFormat.format(value, nonNullArgs);
+- } catch (IllegalArgumentException iae) {
+- StringBuffer buf = new StringBuffer();
+- buf.append(value);
+- for (int i = 0; i < args.length; i++) {
+- buf.append(" arg[" + i + "]=" + args[i]);
+- }
+- iString = buf.toString();
+- }
+- return iString;
+- }
+-
+- /**
+- * Get a string from the underlying resource bundle and format it
+- * with the given object argument. This argument can of course be
+- * a String object.
+- *
+- * @param key The resource name
+- * @param arg Formatting directive
+- */
+-
+- public String getString(String key, Object arg) {
+- Object[] args = new Object[] {arg};
+- return getString(key, args);
+- }
+-
+- /**
+- * Get a string from the underlying resource bundle and format it
+- * with the given object arguments. These arguments can of course
+- * be String objects.
+- *
+- * @param key The resource name
+- * @param arg1 Formatting directive
+- * @param arg2 Formatting directive
+- */
+-
+- public String getString(String key, Object arg1, Object arg2) {
+- Object[] args = new Object[] {arg1, arg2};
+- return getString(key, args);
+- }
+-
+- /**
+- * Get a string from the underlying resource bundle and format it
+- * with the given object arguments. These arguments can of course
+- * be String objects.
+- *
+- * @param key The resource name
+- * @param arg1 Formatting directive
+- * @param arg2 Formatting directive
+- * @param arg3 Formatting directive
+- */
+-
+- public String getString(String key, Object arg1, Object arg2,
+- Object arg3) {
+- Object[] args = new Object[] {arg1, arg2, arg3};
+- return getString(key, args);
+- }
+-
+- /**
+- * Get a string from the underlying resource bundle and format it
+- * with the given object arguments. These arguments can of course
+- * be String objects.
+- *
+- * @param key The resource name
+- * @param arg1 Formatting directive
+- * @param arg2 Formatting directive
+- * @param arg3 Formatting directive
+- * @param arg4 Formatting directive
+- */
+-
+- public String getString(String key, Object arg1, Object arg2,
+- Object arg3, Object arg4) {
+- Object[] args = new Object[] {arg1, arg2, arg3, arg4};
+- return getString(key, args);
+- }
+- // --------------------------------------------------------------
+- // STATIC SUPPORT METHODS
+- // --------------------------------------------------------------
+-
+- private static Hashtable managers = new Hashtable();
+-
+- /**
+- * Get the StringManager for a particular package. If a manager for
+- * a package already exists, it will be reused, else a new
+- * StringManager will be created and returned.
+- *
+- * @param packageName The package name
+- */
+-
+- public synchronized static StringManager getManager(String packageName) {
+- StringManager mgr = (StringManager)managers.get(packageName);
+-
+- if (mgr == null) {
+- mgr = new StringManager(packageName);
+- managers.put(packageName, mgr);
+- }
+- return mgr;
+- }
+-}
+Index: java/org/apache/catalina/tribes/util/Logs.java
+===================================================================
+--- java/org/apache/catalina/tribes/util/Logs.java (revision 590752)
++++ java/org/apache/catalina/tribes/util/Logs.java (working copy)
+@@ -1,29 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.util;
+-
+-import org.apache.juli.logging.Log;
+-import org.apache.juli.logging.LogFactory;
+-/**
+- *
+- * Simple class that holds references to global loggers
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class Logs {
+- public static Log MESSAGES = LogFactory.getLog(
"org.apache.catalina.tribes.MESSAGES" );
+-}
+Index: java/org/apache/catalina/tribes/util/UUIDGenerator.java
+===================================================================
+--- java/org/apache/catalina/tribes/util/UUIDGenerator.java (revision 590752)
++++ java/org/apache/catalina/tribes/util/UUIDGenerator.java (working copy)
+@@ -1,77 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-package org.apache.catalina.tribes.util;
+-
+-import java.security.SecureRandom;
+-import java.util.Random;
+-
+-/**
+- * simple generation of a UUID
+- * @author Filip Hanik
+- * @version 1.0
+- */
+-public class UUIDGenerator {
+- public static final int UUID_LENGTH = 16;
+- public static final int UUID_VERSION = 4;
+- public static final int BYTES_PER_INT = 4;
+- public static final int BITS_PER_BYTE = 8;
+-
+- protected static SecureRandom secrand = null;
+- protected static Random rand = new Random(System.currentTimeMillis());
+- static {
+- secrand = new SecureRandom();
+- secrand.setSeed(rand.nextLong());
+- }
+-
+- public static byte[] randomUUID(boolean secure) {
+- byte[] result = new byte[UUID_LENGTH];
+- return randomUUID(secure,result,0);
+- }
+-
+- public static byte[] randomUUID(boolean secure, byte[] into, int offset) {
+- if ( (offset+UUID_LENGTH)>into.length )
+- throw new ArrayIndexOutOfBoundsException("Unable to fit
"+UUID_LENGTH+" bytes into the array. length:"+into.length+" required
length:"+(offset+UUID_LENGTH));
+- Random r = (secure&&(secrand!=null))?secrand:rand;
+- nextBytes(into,offset,UUID_LENGTH,r);
+- into[6+offset] &= 0x0F;
+- into[6+offset] |= (UUID_VERSION << 4);
+- into[8+offset] &= 0x3F; //0011 1111
+- into[8+offset] |= 0x80; //1000 0000
+- return into;
+- }
+-
+- /**
+- * Same as java.util.Random.nextBytes except this one we dont have to allocate a new
byte array
+- * @param into byte[]
+- * @param offset int
+- * @param length int
+- * @param r Random
+- */
+- public static void nextBytes(byte[] into, int offset, int length, Random r) {
+- int numRequested = length;
+- int numGot = 0, rnd = 0;
+- while (true) {
+- for (int i = 0; i < BYTES_PER_INT; i++) {
+- if (numGot == numRequested) return;
+- rnd = (i == 0 ? r.nextInt() : rnd >> BITS_PER_BYTE);
+- into[offset+numGot] = (byte) rnd;
+- numGot++;
+- }
+- }
+- }
+-
+-}
+\ No newline at end of file
+Index: java/org/apache/catalina/tribes/ChannelReceiver.java
+===================================================================
+--- java/org/apache/catalina/tribes/ChannelReceiver.java (revision 590752)
++++ java/org/apache/catalina/tribes/ChannelReceiver.java (working copy)
+@@ -1,75 +0,0 @@
+-/*
+- * Licensed to the Apache Software Foundation (ASF) under one or more
+- * contributor license agreements. See the NOTICE file distributed with
+- * this work for additional information regarding copyright ownership.
+- * The ASF licenses this file to You under the Apache License, Version 2.0
+- * (the "License"); you may not use this file except in compliance with
+- * the License. You may obtain a copy of the License at
+- *
+- *
http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-package org.apache.catalina.tribes;
+-
+-
+-/**
+- * ChannelReceiver Interface<br>
+- * The <code>ChannelReceiver</code> interface is the data receiver component
+- * at the bottom layer, the IO layer (for layers see the javadoc for the {@link Channel}
interface).
+- * This class may optionally implement a thread pool for parallel processing of incoming
messages.
+- * @author Filip Hanik
+- * @version $Revision$, $Date$
+- */
+-public interface ChannelReceiver extends Heartbeat {
+- /**
+- * Start listening for incoming messages on the host/port
+- * @throws java.io.IOException
+- */
+- public void start() throws java.io.IOException;
+-
+- /**
+- * Stop listening for messages
+- */
+- public void stop();
+-
+- /**
+- * String representation of the IPv4 or IPv6 address that this host is listening
+- * to.
+- * @return the host that this receiver is listening to
+- */
+- public String getHost();
+-
+-
+- /**
+- * Returns the listening port
+- * @return port
+- */
+- public int getPort();
+-
+- /**
+- * Returns the secure listening port
+- * @return port, -1 if a secure port is not activated
+- */
+- public int getSecurePort();
+-
+- /**
+- * Sets the message listener to receive notification of incoming
+- * @param listener MessageListener
+- * @see MessageListener
+- */
+- public void setMessageListener(MessageListener listener);
+-
+- /**
+- * Returns the message listener that is associated with this receiver
+- * @return MessageListener
+- * @see MessageListener
+- */
+- public MessageListener getMessageListener();
+-
+-}
+Index: build.xml
+===================================================================
+--- build.xml (revision 590988)
++++ build.xml (working copy)
+@@ -22,7 +22,9 @@
+
+ <!-- See "build.properties.sample" in the top level directory for all
-->
+ <!-- property values you must customize for successful building!!! -->
++<!--
+ <property file="${user.home}/build.properties"/>
++ -->
+ <property file="build.properties"/>
+
+ <property file="build.properties.default"/>
+@@ -60,8 +62,6 @@
+ <property name="jsp-api.jar"
value="${tomcat.build}/lib/jsp-api.jar"/>
+ <property name="el-api.jar"
value="${tomcat.build}/lib/el-api.jar"/>
+ <property name="catalina.jar"
value="${tomcat.build}/lib/catalina.jar"/>
+- <property name="catalina-tribes.jar"
value="${tomcat.build}/lib/catalina-tribes.jar"/>
+- <property name="catalina-ha.jar"
value="${tomcat.build}/lib/catalina-ha.jar"/>
+ <property name="catalina-ant.jar"
value="${tomcat.build}/lib/catalina-ant.jar"/>
+ <property name="catalina-ant-jmx.jar"
value="${tomcat.build}/lib/catalina-ant-jmx.jar"/>
+ <property name="tomcat-coyote.jar"
value="${tomcat.build}/lib/tomcat-coyote.jar"/>
+@@ -225,34 +225,12 @@
+ <exclude name="**/LocalStrings_*" />
+ <!-- Modules -->
+ <exclude name="org/apache/catalina/ant/**" />
+- <exclude name="org/apache/catalina/cluster/**" />
+- <exclude name="org/apache/catalina/ha/**" />
+- <exclude name="org/apache/catalina/tribes/**" />
+ <exclude name="org/apache/catalina/launcher/**" />
+ <exclude name="org/apache/catalina/storeconfig/**" />
+ <exclude name="org/apache/naming/factory/webservices/**" />
+ </fileset>
+ </jar>
+
+- <!-- Catalina GroupCom/Tribes JAR File -->
+- <jar jarfile="${catalina-tribes.jar}">
+- <fileset dir="${tomcat.classes}">
+- <exclude name="**/package.html" />
+- <exclude name="**/LocalStrings_*" />
+- <!-- Modules -->
+- <include name="org/apache/catalina/tribes/**" />
+- </fileset>
+- </jar>
+- <!-- Catalina Cluster/HA JAR File -->
+- <jar jarfile="${catalina-ha.jar}">
+- <fileset dir="${tomcat.classes}">
+- <exclude name="**/package.html" />
+- <exclude name="**/LocalStrings_*" />
+- <!-- Modules -->
+- <include name="org/apache/catalina/ha/**" />
+- </fileset>
+- </jar>
+-
+ <!-- Catalina Ant Tasks JAR File -->
+ <jar jarfile="${catalina-ant.jar}">
+ <fileset dir="${tomcat.classes}">
+@@ -391,17 +369,8 @@
+ <param name="relative-path" expression=".."/>
+ </style>
+
+- <style basedir="webapps/docs/tribes"
+- destdir="${tomcat.build}/webapps/docs/tribes"
+- extension=".html"
+- style="webapps/docs/tomcat-docs.xsl"
+- excludes="project.xml"
+- includes="*.xml">
+- <param name="relative-path" expression=".."/>
+- </style>
+-
+- <!-- Print friendly version -->
+- <mkdir dir="${tomcat.build}/webapps/docs/printer" />
++ <!-- Print friendly version -->
++ <mkdir dir="${tomcat.build}/webapps/docs/printer" />
+ <copy todir="${tomcat.build}/webapps/docs/printer">
+ <fileset dir=".">
+ <include name="BUILDING.txt"/>
+Index: webapps/docs/not-supported.xml
+===================================================================
+--- webapps/docs/not-supported.xml (revision 0)
++++ webapps/docs/not-supported.xml (revision 0)
+@@ -0,0 +1,41 @@
++<?xml version="1.0"?>
++<!--
++ Licensed to the Apache Software Foundation (ASF) under one or more
++ contributor license agreements. See the NOTICE file distributed with
++ this work for additional information regarding copyright ownership.
++ The ASF licenses this file to You under the Apache License, Version 2.0
++ (the "License"); you may not use this file except in compliance with
++ the License. You may obtain a copy of the License at
++
++
http://www.apache.org/licenses/LICENSE-2.0
++
++ Unless required by applicable law or agreed to in writing, software
++ distributed under the License is distributed on an "AS IS" BASIS,
++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ See the License for the specific language governing permissions and
++ limitations under the License.
++-->
++<!DOCTYPE document [
++ <!ENTITY project SYSTEM "project.xml">
++]>
++<document url="not-supported.html">
++
++ &project;
++
++ <properties>
++ <author email="jclere(a)readhat.com">Jean-Frederic
Clere</author>
++ <title>Not supported in this distribution</title>
++ </properties>
++
++<body>
++
++
++<section name="Introduction">
++
++<p>This feature is not supported is this distribution.</p>
++
++</section>
++
++</body>
++
++</document>
+Index: webapps/docs/tribes/leader-election-initiate-election.jpg
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: webapps/docs/tribes/setup.xml
+===================================================================
+--- webapps/docs/tribes/setup.xml (revision 590752)
++++ webapps/docs/tribes/setup.xml (working copy)
+@@ -1,37 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="introduction.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>Apache Tribes - Configuration</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Configuration Overview">
+-</section>
+-</body>
+-
+-</document>
+Index: webapps/docs/tribes/introduction.xml
+===================================================================
+--- webapps/docs/tribes/introduction.xml (revision 590752)
++++ webapps/docs/tribes/introduction.xml (working copy)
+@@ -1,271 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="tribes.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>Apache Tribes - Introduction</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Quick Start">
+-
+- <p>Apache Tribes is a group or peer-to-peer communcation framework that enables
you to easily connect
+- your remote objects to communicate with each other.
+- </p>
+- <ul>
+- <li>Import:
<code>org.apache.catalina.tribes.Channel</code></li>
+- <li>Import:
<code>org.apache.catalina.tribes.Member</code></li>
+- <li>Import:
<code>org.apache.catalina.tribes.MembershipListener</code></li>
+- <li>Import:
<code>org.apache.catalina.tribes.ChannelListener</code></li>
+- <li>Import:
<code>org.apache.catalina.tribes.group.GroupChannel</code></li>
+- <li>Create a class that implements:
<code>org.apache.catalina.tribes.ChannelListener</code></li>
+- <li>Create a class that implements:
<code>org.apache.catalina.tribes.MembershipListener</code></li>
+- <li>Simple class to demonstrate how to send a message:
+- <source>
+- //create a channel
+- Channel myChannel = new GroupChannel();
+-
+- //create my listeners
+- ChannelListener msgListener = new MyMessageListener();
+- MembershipListener mbrListener = new MyMemberListener();
+-
+- //attach the listeners to the channel
+- myChannel.addMembershipListener(mbrListener);
+- myChannel.addChannelListener(msgListener);
+-
+- //start the channel
+- myChannel.start(Channel.DEFAULT);
+-
+- //create a message to be sent, message must implement java.io.Serializable
+- //for performance reasons you probably want them to implement
java.io.Externalizable
+- Serializable myMsg = new MyMessage();
+-
+- //retrieve my current members
+- Member[] group = myChannel.getMembers();
+-
+- //send the message
+- channel.send(group,myMsg,Channel.SEND_OPTIONS_DEFAULT);
+- </source>
+- </li>
+- </ul>
+- <p>
+- Simple yeah? There is a lot more to Tribes than we have shown, hopefully the docs
will be able
+- to explain more to you. Remember, that we are always interested in suggestions,
improvements, bug fixes
+- and anything that you think would help this project.
+- </p>
+- <p>
+- Note: Tribes is currently built for JDK1.5, you can run on JDK1.4 by a small
modifications to locks used from the <code>java.util.concurrent</code>
package.
+- </p>
+-</section>
+-
+-
+-<section name="What is Tribes">
+- <p>
+- Tribes is a messaging framework with group communication abilities. Tribes allows
you to send and receive
+- messages over a network, it also allows for dynamic discovery of other nodes in the
network.<br/>
+- And that is the short story, it really is as simple as that. What makes Tribes
useful and unique will be
+- described in the section below.<br/>
+- </p>
+- <p>
+- The Tribes module was started early 2006 and a small part of the code base comes
from the clustering module
+- that has been existing since 2003 or 2004.
+- The current cluster implementation has several short comings and many work arounds
were created due
+- to the complexity in group communication. Long story short, what should have been
two modules a long time
+- ago, will be now. Tribes takes out the complexity of messaging from the replication
module and becomes
+- a fully independent and highly flexible group communication module.<br/>
+- </p>
+- <p>
+- In Tomcat the old <code>modules/cluster</code> has now become
<code>modules/groupcom</code>(Tribes) and
+- <code>modules/ha</code> (replication). This will allow development to
proceed and let the developers
+- focus on the issues they are actually working on rather than getting boggled down in
details of a module
+- they are not interested in. The understanding is that both communication and
replication are complex enough,
+- and when trying to develop them in the same module, well you know, it becomes a
cluster :)<br/>
+- </p>
+- <p>
+- Tribes allows for guaranteed messaging, and can be customized in many ways. Why is
this important?<br/>
+- Well, you as a developer want to know that the messages you are sending are reaching
their destination.
+- More than that, if a message doesn't reach its destination, the application on
top of Tribes will be notified
+- that the message was never sent, and what node it failed.
+- </p>
+-
+-</section>
+-
+-<section name="Why another messaging framework">
+- <p>
+- I am a big fan of reusing code and would never dream of developing something if
someone else has already
+- done it and it was available to me and the community I try to serve.<br/>
+- When I did my research to improve the clustering module I was constantly faced with
a few obstacles:<br/>
+- 1. The framework wasn't flexible enough<br/>
+- 2. The framework was licensed in a way that neither I nor the community could use
it<br/>
+- 3. Several features that I needed were missing<br/>
+- 4. Messaging was guaranteed, but no feedback was reported to me<br/>
+- 5. The semantics of my message delivery had to be configured before
runtime<br/>
+- And the list continues...
+- </p>
+- <p>
+- So I came up with Tribes, to address these issues and other issues that came along.
+- When designing Tribes I wanted to make sure I didn't lose any of the flexibility
and
+- delivery semantics that the existing frameworks already delivered. The goal was to
create a framework
+- that could do everything that the others already did, but to provide more
flexibility for the application
+- developer. In the next section will give you the high level overview of what
features tribes offers or will offer.
+- </p>
+-</section>
+-
+-<section name="Feature Overview">
+- <p>
+- To give you an idea of the feature set I will list it out here.
+- Some of the features are not yet completed, if that is the case they are marked
accordingly.
+- </p>
+- <p>
+- <b>Pluggable modules</b><br/>
+- Tribes is built using interfaces. Any of the modules or components that are part of
Tribes can be swapped out
+- to customize your own Tribes implementation.
+- </p>
+- <p>
+- <b>Guaranteed Messaging</b><br/>
+- In the default implementation of Tribes uses TCP for messaging. TCP already has
guaranteed message delivery
+- and flow control built in. I believe that the performance of Java TCP, will
outperform an implementation of
+- Java/UDP/flow-control/message guarantee since the logic happens further down the
stack.<br/>
+- Tribes supports both non-blocking and blocking IO operations. The recommended
setting is to use non blocking
+- as it promotes better parallelism when sending and receiving messages. The blocking
implementation is available
+- for those platforms where NIO is still a trouble child.
+- </p>
+- <p>
+- <b>Different Guarantee Levels</b><br/>
+- There are three different levels of delivery guarantee when a message is
sent.<br/>
+- <ol>
+- <li>IO Based send guarantee. - fastest, least reliable<br/>
+- This means that Tribes considers the message transfer to be successful
+- if the message was sent to the socket send buffer and accepted.<br/>
+- On blocking IO, this would be
<code>socket.getOutputStream().write(msg)</code><br/>
+- On non blocking IO, this would be
<code>socketChannel.write()</code>, and the buffer byte buffer gets emptied
+- followed by a <code>socketChannel.read()</code> to ensure the
channel still open.
+- The <code>read()</code> has been added since
<code>write()</code> will succeed if the connection has been
"closed"
+- when using NIO.
+- </li>
+- <li>ACK based. - recommended, guaranteed delivery<br/>
+- When the message has been received on a remote node, an ACK is sent back to
the sender,
+- indicating that the message was received successfully.
+- </li>
+- <li>SYNC_ACK based. - guaranteed delivery, guaranteed processed,
slowest<br/>
+- When the message has been received on a remote node, the node will process
+- the message and if the message was processed successfully, an ACK is sent back
to the sender
+- indicating that the message was received and processed successfully.
+- If the message was received, but processing it failed, an ACK_FAIL will be
sent back
+- to the sender. This is a unique feature that adds an incredible amount value
to the application
+- developer. Most frameworks here will tell you that the message was delivered,
and the application
+- developer has to build in logic on whether the message was actually processed
properly by the application
+- on the remote node. If configured, Tribes will throw an exception when it
receives an ACK_FAIL
+- and associate that exception with the member that didn't process the
message.
+- </li>
+- </ol>
+- You can of course write even more sophisticated guarantee levels, and some of them
will be mentioned later on
+- in the documentation. One mentionable level would be a 2-Phase-Commit, where the
remote applications don't receive
+- the message until all nodes have received the message. Sort of like a all-or-nothing
protocol.
+- </p>
+- <p>
+- <b>Per Message Delivery Attributes</b><br/>
+- Perhaps the feature that makes Tribes stand out from the crowd of group
communication frameworks.
+- Tribes enables you to send to decide what delivery semantics a message transfer
should have on a per
+- message basis. Meaning, that your messages are not delivered based on some static
configuration
+- that remains fixed after the message framework has been started.<br/>
+- To give you an example of how powerful this feature is, I'll try to illustrate
it with a simple example.
+- Imagine you need to send 10 different messsages, you could send the the following
way:
+- <source>
+- Message_1 - asynchronous and fast, no guarantee required, fire and forget
+- Message_2 - all-or-nothing, either all receivers get it, or none.
+- Message_3 - encrypted and SYNC_ACK based
+- Message_4 - asynchronous, SYNC_ACK and call back when the message is processed on
the remote nodes
+- Message_5 - totally ordered, this message should be received in the same order on
all nodes that have been
+- send totally ordered
+- Message_6 - asynchronous and totally ordered
+- Message_7 - RPC message, send a message, wait for all remote nodes to reply before
returning
+- Message_8 - RPC message, wait for the first reply
+- Message_9 - RPC message, asynchronous, don't wait for a reply, collect them
via a callback
+- Message_10- sent to a member that is not part of this group
+- </source>
+- As you can imagine by now, these are just examples. The number of different
semantics you can apply on a
+- per-message-basis is almost limitless. Tribes allows you to set up to 28 different
on a message
+- and then configure Tribes to what flag results in what action on the
message.<br/>
+- Imagine a shared transactional cache, probably >90% are reads, and the dirty
reads should be completely
+- unordered and delivered as fast as possible. But transactional writes on the other
hand, have to
+- be ordered so that no cache gets corrupted. With tribes you would send the write
messages totally ordered,
+- while the read messages you simple fire to achieve highest throughput.<br/>
+- There are probably better examples on how this powerful feature can be used, so use
your imagination and
+- your experience to think of how this could benefit you in your application.
+- </p>
+- <p>
+- <b>Interceptor based message processing</b><br/>
+- Tribes uses a customizable interceptor stack to process messages that are sent and
received.<br/>
+- <i>So what, all frameworks have this!</i><br/>
+- Yes, but in Tribes interceptors can react to a message based on the
per-message-attributes
+- that are sent runtime. Meaning, that if you add a encryption interceptor that
encrypts message
+- you can decide if this interceptor will encrypt all messages, or only certain
messages that are decided
+- by the applications running on top of Tribes.<br/>
+- This is how Tribes is able to send some messages totally ordered and others fire and
forget style
+- like the example above.<br/>
+- The number of interceptors that are available will keep growing, and we would
appreciate any contributions
+- that you might have.
+- </p>
+- <p>
+- <b>Threadless Interceptor stack</b>
+- The interceptor don't require any separate threads to perform their message
manipulation.<br/>
+- Messages that are sent will piggy back on the thread that is sending them all the
way through transmission.
+- The exception is the <code>MessageDispatchInterceptor</code> that will
queue up the message
+- and send it on a separate thread for asynchronous message delivery.
+- Messages received are controlled by a thread pool in the
<code>receiver</code> component.<br/>
+- The channel object can send a <code>heartbeat()</code> through the
interceptor stack to allow
+- for timeouts, cleanup and other events.<br/>
+- The <code>MessageDispatchInterceptor</code> is the only interceptor that
is configured by default.
+- </p>
+- <p>
+- <b>Parallel Delivery</b><br/>
+- Tribes support parallel delivery of messages. Meaning that node_A could send three
messages to node_B in
+- parallel. This feature becomes useful when sending messages with different delivery
semantics.
+- Otherwise if Message_1 was sent totally ordered, Message_2 would have to wait for
that message to complete.<br/>
+- Through NIO, Tribes is also able to send a message to several receivers at the same
time on the same thread.
+- </p>
+- <p>
+- <b>Silent Member Messaging</b><br/>
+- With Tribes you are able to send messages to members that are not in your group.
+- So by default, you can already send messages over a wide area network, even though
the dynamic discover
+- module today is limited to local area networks by using multicast for dynamic node
discovery.
+- Of course, the membership component will be expanded to support WAN memberships in
the future.
+- But this is very useful, when you want to hide members from the rest of the group
and only communicate with them
+- </p>
+-</section>
+-
+-<section name="Where can I get Tribes">
+- <p>
+-
+- </p>
+-
+-
+-</section>
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/tribes/tomcat-docs.xsl
+===================================================================
+--- webapps/docs/tribes/tomcat-docs.xsl (revision 590752)
++++ webapps/docs/tribes/tomcat-docs.xsl (working copy)
+@@ -1,452 +0,0 @@
+-<?xml version="1.0" encoding="ISO-8859-1"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!-- Content Stylesheet for "tomcat-docs" Documentation -->
+-
+-<!-- $Id$ -->
+-
+-<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+- version="1.0">
+-
+-
+- <!-- Output method -->
+- <xsl:output method="html"
+- encoding="iso-8859-1"
+- indent="no"/>
+-
+-
+- <!-- Defined parameters (overrideable) -->
+- <xsl:param name="home-name" select="'The Tomcat
Project'"/>
+- <xsl:param name="home-href"
select="'http://tomcat.apache.org/'"/>
+- <xsl:param name="home-logo"
select="'/images/tomcat.gif'"/>
+- <xsl:param name="printer-logo"
select="'/images/printer.gif'"/>
+- <xsl:param name="apache-logo"
select="'/images/asf-logo.gif'"/>
+- <xsl:param name="relative-path"
select="'.'"/>
+- <xsl:param name="void-image"
select="'/images/void.gif'"/>
+- <xsl:param name="project-menu"
select="'menu'"/>
+- <xsl:param name="standalone" select="''"/>
+- <xsl:param name="buglink"
select="'http://issues.apache.org/bugzilla/show_bug.cgi?id='...
+-
+- <!-- Defined variables (non-overrideable) -->
+- <xsl:variable name="body-bg"
select="'#ffffff'"/>
+- <xsl:variable name="body-fg"
select="'#000000'"/>
+- <xsl:variable name="body-link"
select="'#525D76'"/>
+- <xsl:variable name="banner-bg"
select="'#525D76'"/>
+- <xsl:variable name="banner-fg"
select="'#ffffff'"/>
+- <xsl:variable name="sub-banner-bg"
select="'#828DA6'"/>
+- <xsl:variable name="sub-banner-fg"
select="'#ffffff'"/>
+- <xsl:variable name="source-color"
select="'#023264'"/>
+- <xsl:variable name="attributes-color"
select="'#023264'"/>
+- <xsl:variable name="table-th-bg"
select="'#039acc'"/>
+- <xsl:variable name="table-td-bg"
select="'#a0ddf0'"/>
+-
+- <!-- Process an entire document into an HTML page -->
+- <xsl:template match="document">
+- <xsl:variable name="project"
+- select="document('project.xml')/project"/>
+- <html>
+- <head>
+- <title><xsl:value-of select="project/title"/> -
<xsl:value-of select="properties/title"/></title>
+- <xsl:for-each select="properties/author">
+- <xsl:variable name="name">
+- <xsl:value-of select="."/>
+- </xsl:variable>
+- <xsl:variable name="email">
+- <xsl:value-of select="@email"/>
+- </xsl:variable>
+- <meta name="author" value="{$name}"/>
+- <meta name="email" value="{$email}"/>
+- </xsl:for-each>
+- </head>
+-
+- <body bgcolor="{$body-bg}" text="{$body-fg}"
link="{$body-link}"
+- alink="{$body-link}" vlink="{$body-link}">
+-
+- <table border="0" width="100%" cellspacing="0">
+-
+- <xsl:comment>PAGE HEADER</xsl:comment>
+- <tr>
+- <td>
+- <xsl:if test="project/logo">
+- <xsl:variable name="alt">
+- <xsl:value-of select="project/logo"/>
+- </xsl:variable>
+- <xsl:variable name="home">
+- <xsl:value-of select="project/@href"/>
+- </xsl:variable>
+- <xsl:variable name="src">
+- <!--<xsl:value-of
select="$relative-path"/>--><xsl:value-of
select="project/logo/@href"/>
+- </xsl:variable>
+-
+- <xsl:comment>PROJECT LOGO</xsl:comment>
+- <a href="{$home}">
+- <img src="{$src}" align="right"
alt="{$alt}" border="0"/>
+- </a>
+- </xsl:if>
+- </td>
+- <td>
+- <font face="arial,helvetica,sanserif">
+- <h1><xsl:value-of
select="$project/title"/></h1>
+- </font>
+- </td>
+- <td>
+- <xsl:comment>APACHE LOGO</xsl:comment>
+- <xsl:variable name="src">
+- <xsl:value-of select="$relative-path"/><xsl:value-of
select="$apache-logo"/>
+- </xsl:variable>
+- <a
href="http://www.apache.org/">
+- <img
src="http://tomcat.apache.org/tomcat-5.5-doc/images/asf-logo.gif"
align="right" alt="Apache Logo" border="0"/>
+- </a>
+- </td>
+- </tr>
+- </table>
+-
+- <table border="0" width="100%" cellspacing="4">
+-
+- <xsl:comment>HEADER SEPARATOR</xsl:comment>
+- <tr>
+- <td colspan="2">
+- <hr noshade="noshade" size="1"/>
+- </td>
+- </tr>
+-
+- <tr>
+-
+- <!-- Don't generate a menu if styling printer friendly docs -->
+- <xsl:if test="$project-menu = 'menu'">
+- <xsl:comment>LEFT SIDE NAVIGATION</xsl:comment>
+- <td width="20%" valign="top"
nowrap="true">
+- <xsl:apply-templates select="project/body/menu"/>
+- </td>
+- </xsl:if>
+-
+- <xsl:comment>RIGHT SIDE MAIN BODY</xsl:comment>
+- <td width="80%" valign="top" align="left">
+- <table border="0" width="100%"
cellspacing="4">
+- <tr>
+- <td align="left" valign="top">
+- <h1><xsl:value-of
select="project/title"/></h1>
+- <h2><xsl:value-of
select="properties/title"/></h2>
+- </td>
+- <td align="right" valign="top"
nowrap="true">
+- <!-- Add the printer friendly link for docs with a menu -->
+- <xsl:if test="$project-menu = 'menu'">
+- <xsl:variable name="src">
+- <xsl:value-of
select="$relative-path"/><xsl:value-of
select="$printer-logo"/>
+- </xsl:variable>
+- <xsl:variable name="url">
+- <xsl:value-of select="/document/@url"/>
+- </xsl:variable>
+- <small>
+- <a href="printer/{$url}">
+- <img src="{$src}" border="0"
alt="Printer Friendly Version"/>
+- <br />print-friendly<br />version
+- </a>
+- </small>
+- </xsl:if>
+- <xsl:if test="$project-menu != 'menu'">
+- <xsl:variable name="void">
+- <xsl:value-of
select="$relative-path"/><xsl:value-of
select="$void-image"/>
+- </xsl:variable>
+- <img src="{$void}" width="1"
height="1" vspace="0" hspace="0" border="0"/>
+- </xsl:if>
+- </td>
+- </tr>
+- </table>
+- <xsl:apply-templates select="body/section"/>
+- </td>
+-
+- </tr>
+-
+- <xsl:comment>FOOTER SEPARATOR</xsl:comment>
+- <tr>
+- <td colspan="2">
+- <hr noshade="noshade" size="1"/>
+- </td>
+- </tr>
+-
+- <xsl:comment>PAGE FOOTER</xsl:comment>
+- <tr><td colspan="2">
+- <div align="center"><font color="{$body-link}"
size="-1"><em>
+- Copyright © 1999-2006, Apache Software Foundation
+- </em></font></div>
+- </td></tr>
+-
+- </table>
+- </body>
+- </html>
+-
+- </xsl:template>
+-
+-
+- <!-- Process a menu for the navigation bar -->
+- <xsl:template match="menu">
+- <p><strong><xsl:value-of
select="@name"/></strong></p>
+- <ul>
+- <xsl:apply-templates select="item"/>
+- </ul>
+- </xsl:template>
+-
+-
+- <!-- Process a menu item for the navigation bar -->
+- <xsl:template match="item">
+- <xsl:variable name="href">
+- <xsl:value-of select="@href"/>
+- </xsl:variable>
+- <li><a href="{$href}"><xsl:value-of
select="@name"/></a></li>
+- </xsl:template>
+-
+-
+- <!-- Process a documentation section -->
+- <xsl:template match="section">
+- <xsl:variable name="name">
+- <xsl:value-of select="@name"/>
+- </xsl:variable>
+- <table border="0" cellspacing="0"
cellpadding="2">
+- <!-- Section heading -->
+- <tr><td bgcolor="{$banner-bg}">
+- <font color="{$banner-fg}"
face="arial,helvetica.sanserif">
+- <a name="{$name}">
+- <strong><xsl:value-of
select="@name"/></strong></a></font>
+- </td></tr>
+- <!-- Section body -->
+- <tr><td><blockquote>
+- <xsl:apply-templates/>
+- </blockquote></td></tr>
+- </table>
+- </xsl:template>
+-
+-
+- <!-- Process a documentation subsection -->
+- <xsl:template match="subsection">
+- <xsl:variable name="name">
+- <xsl:value-of select="@name"/>
+- </xsl:variable>
+- <table border="0" cellspacing="0"
cellpadding="2">
+- <!-- Subsection heading -->
+- <tr><td bgcolor="{$sub-banner-bg}">
+- <font color="{$sub-banner-fg}"
face="arial,helvetica.sanserif">
+- <a name="{$name}">
+- <strong><xsl:value-of
select="@name"/></strong></a></font>
+- </td></tr>
+- <!-- Subsection body -->
+- <tr><td><blockquote>
+- <xsl:apply-templates/>
+- </blockquote></td></tr>
+- </table>
+- </xsl:template>
+-
+-
+- <!-- Process a source code example -->
+- <xsl:template match="source">
+- <xsl:variable name="void">
+- <xsl:value-of select="$relative-path"/><xsl:value-of
select="$void-image"/>
+- </xsl:variable>
+- <div align="left">
+- <table cellspacing="4" cellpadding="0"
border="0">
+- <tr>
+- <td bgcolor="{$source-color}" width="1"
height="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- <td bgcolor="{$source-color}" height="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- <td bgcolor="{$source-color}" width="1"
height="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- </tr>
+- <tr>
+- <td bgcolor="{$source-color}" width="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- <td bgcolor="#ffffff" height="1"><pre>
+- <xsl:value-of select="."/>
+- </pre></td>
+- <td bgcolor="{$source-color}" width="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- </tr>
+- <tr>
+- <td bgcolor="{$source-color}" width="1"
height="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- <td bgcolor="{$source-color}" height="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- <td bgcolor="{$source-color}" width="1"
height="1">
+- <img src="{$void}" width="1" height="1"
vspace="0" hspace="0" border="0"/>
+- </td>
+- </tr>
+- </table>
+- </div>
+- </xsl:template>
+-
+-
+- <!-- Process an attributes list with nested attribute elements -->
+- <xsl:template match="attributes">
+- <table border="1" cellpadding="5">
+- <tr>
+- <th width="15%" bgcolor="{$attributes-color}">
+- <font color="#ffffff">Attribute</font>
+- </th>
+- <th width="85%" bgcolor="{$attributes-color}">
+- <font color="#ffffff">Description</font>
+- </th>
+- </tr>
+- <xsl:for-each select="attribute">
+- <tr>
+- <td align="left" valign="center">
+- <xsl:if test="@required = 'true'">
+- <strong><code><xsl:value-of
select="@name"/></code></strong>
+- </xsl:if>
+- <xsl:if test="@required != 'true'">
+- <code><xsl:value-of select="@name"/></code>
+- </xsl:if>
+- </td>
+- <td align="left" valign="center">
+- <xsl:apply-templates/>
+- </td>
+- </tr>
+- </xsl:for-each>
+- </table>
+- </xsl:template>
+-
+- <!-- Fix relative links in printer friendly versions of the docs -->
+- <xsl:template match="a">
+- <xsl:variable name="href" select="@href"/>
+- <xsl:choose>
+- <xsl:when test="$standalone = 'standalone'">
+- <xsl:apply-templates/>
+- </xsl:when>
+- <xsl:when test="$project-menu != 'menu' and
starts-with(@href,'../')">
+- <a href="../{$href}"><xsl:apply-templates/></a>
+- </xsl:when>
+- <xsl:when test="$project-menu != 'menu' and
starts-with((a)href,'./') and contains(substring(@href,3),'/')">
+- <a href=".{$href}"><xsl:apply-templates/></a>
+- </xsl:when>
+- <xsl:when test="$project-menu != 'menu' and
not(contains(@href,'//')) and not(starts-with(@href,'/')) and
not(starts-with(@href,'#')) and contains(@href,'/')">
+- <a href="../{$href}"><xsl:apply-templates/></a>
+- </xsl:when>
+- <xsl:when test="$href != ''">
+- <a href="{$href}"><xsl:apply-templates/></a>
+- </xsl:when>
+- <xsl:otherwise>
+- <xsl:variable name="name" select="@name"/>
+- <a name="{$name}"><xsl:apply-templates/></a>
+- </xsl:otherwise>
+- </xsl:choose>
+- </xsl:template>
+-
+- <!-- Changelog related tags -->
+- <xsl:template match="changelog">
+- <table border="0" cellpadding="2"
cellspacing="2">
+- <xsl:apply-templates/>
+- </table>
+- </xsl:template>
+-
+- <xsl:template match="changelog/add">
+- <tr>
+- <xsl:variable name="src"><xsl:value-of
select="$relative-path"/>/images/add.gif</xsl:variable>
+- <td><img alt="add" class="icon"
src="{$src}"/></td>
+- <td><xsl:apply-templates/></td>
+- </tr>
+- </xsl:template>
+-
+- <xsl:template match="changelog/update">
+- <tr>
+- <xsl:variable name="src"><xsl:value-of
select="$relative-path"/>/images/update.gif</xsl:variable>
+- <td><img alt="update" class="icon"
src="{$src}"/></td>
+- <td><xsl:apply-templates/></td>
+- </tr>
+- </xsl:template>
+-
+- <xsl:template match="changelog/design">
+- <tr>
+- <xsl:variable name="src"><xsl:value-of
select="$relative-path"/>/images/design.gif</xsl:variable>
+- <td><img alt="design" class="icon"
src="{$src}"/></td>
+- <td><xsl:apply-templates/></td>
+- </tr>
+- </xsl:template>
+-
+- <xsl:template match="changelog/docs">
+- <tr>
+- <xsl:variable name="src"><xsl:value-of
select="$relative-path"/>/images/docs.gif</xsl:variable>
+- <td><img alt="docs" class="icon"
src="{$src}"/></td>
+- <td><xsl:apply-templates/></td>
+- </tr>
+- </xsl:template>
+-
+- <xsl:template match="changelog/fix">
+- <tr>
+- <xsl:variable name="src"><xsl:value-of
select="$relative-path"/>/images/fix.gif</xsl:variable>
+- <td><img alt="fix" class="icon"
src="{$src}"/></td>
+- <td><xsl:apply-templates/></td>
+- </tr>
+- </xsl:template>
+-
+- <xsl:template match="changelog/scode">
+- <tr>
+- <xsl:variable name="src"><xsl:value-of
select="$relative-path"/>/images/code.gif</xsl:variable>
+- <td><img alt="code" class="icon"
src="{$src}"/></td>
+- <td><xsl:apply-templates/></td>
+- </tr>
+- </xsl:template>
+-
+- <!-- Process an attributes list with nested attribute elements -->
+- <xsl:template match="status">
+- <table border="1" cellpadding="5">
+- <tr>
+- <th width="15%" bgcolor="{$attributes-color}">
+- <font color="#ffffff">Priority</font>
+- </th>
+- <th width="50%" bgcolor="{$attributes-color}">
+- <font color="#ffffff">Action Item</font>
+- </th>
+- <th width="25%" bgcolor="{$attributes-color}">
+- <font color="#ffffff">Volunteers</font>
+- </th>
+- <xsl:for-each select="item">
+- <tr>
+- <td align="left" valign="center">
+- <xsl:value-of select="@priority"/>
+- </td>
+- <td align="left" valign="center">
+- <xsl:apply-templates/>
+- </td>
+- <td align="left" valign="center">
+- <xsl:value-of select="@owner"/>
+- </td>
+- </tr>
+- </xsl:for-each>
+- </tr>
+- </table>
+- </xsl:template>
+-
+- <!-- Link to a bug report -->
+- <xsl:template match="bug">
+- <xsl:variable name="link"><xsl:value-of
select="$buglink"/><xsl:value-of
select="text()"/></xsl:variable>
+- <a href="{$link}"><xsl:apply-templates/></a>
+- </xsl:template>
+-
+- <!-- Process everything else by just passing it through -->
+- <xsl:template match="*|@*">
+- <xsl:copy>
+- <xsl:apply-templates select="@*|*|text()"/>
+- </xsl:copy>
+- </xsl:template>
+-
+-</xsl:stylesheet>
+Index: webapps/docs/tribes/leader-election-message-arrives.dia
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: webapps/docs/tribes/project.xml
+===================================================================
+--- webapps/docs/tribes/project.xml (revision 590752)
++++ webapps/docs/tribes/project.xml (working copy)
+@@ -1,54 +0,0 @@
+-<?xml version="1.0" encoding="ISO-8859-1"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<project name="Apache Tribes Documentation - Top Level Directory"
+-
href="http://tomcat.apache.org/">
+-
+- <title>Apache Tribes - The Tomcat Cluster Communication Module</title>
+-
+- <logo href="/images/tomcat.gif">Apache Tomcat</logo>
+-
+-
+- <body>
+-
+- <menu name="Links">
+- <item name="Docs Home"
href="../index.html"/>
+- <item name="FAQ"
href="http://tomcat.apache.org/faq" />
+- </menu>
+-
+- <menu name="User Guide">
+- <item name="1) Introduction"
href="introduction.html"/>
+- <item name="2) Setup"
href="setup.html"/>
+- <item name="3) FAQ" href="faq.html"/>
+- </menu>
+-
+- <menu name="Reference">
+- <item name="Release Notes"
href="RELEASE-NOTES.txt"/>
+- <item name="JavaDoc"
href="/api/index.html"/>
+- </menu>
+-
+- <menu name="Apache Tribes Development">
+- <item name="Membership"
href="membership.html"/>
+- <item name="Transport"
href="transport.html"/>
+- <item name="Interceptors"
href="interceptors.html"/>
+- <item name="Status"
href="status.html"/>
+- <item name="Developers"
href="developers.html"/>
+- </menu>
+-
+- </body>
+-
+-</project>
+Index: webapps/docs/tribes/faq.xml
+===================================================================
+--- webapps/docs/tribes/faq.xml (revision 590752)
++++ webapps/docs/tribes/faq.xml (working copy)
+@@ -1,37 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="introduction.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>Apache Tribes - Frequently Asked Questions</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Frequently Asked Questions">
+-</section>
+-</body>
+-
+-</document>
+Index: webapps/docs/tribes/leader-election-message-arrives.jpg
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: webapps/docs/tribes/leader-election-initiate-election.dia
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: webapps/docs/class-loader-howto.xml
+===================================================================
+--- webapps/docs/class-loader-howto.xml (revision 590752)
++++ webapps/docs/class-loader-howto.xml (working copy)
+@@ -106,8 +106,6 @@
+ <li><em>catalina.jar</em> - Implementation of the Catalina
servlet
+ container portion of Tomcat 6.</li>
+ <li><em>catalina-ant.jar</em> - Tomcat Catalina Ant
tasks.</li>
+- <li><em>catalina-ha.jar</em> - High availability
package.</li>
+- <li><em>catalina-tribes.jar</em> - Group communication
package.</li>
+ <li><em>el-api.jar</em> - EL 2.1 API.</li>
+ <li><em>jasper.jar</em> - Jasper 2 Compiler and
Runtime.</li>
+ <li><em>jasper-el.jar</em> - Jasper 2 EL
implementation.</li>
+Index: webapps/docs/index.xml
+===================================================================
+--- webapps/docs/index.xml (revision 590752)
++++ webapps/docs/index.xml (working copy)
+@@ -107,8 +107,6 @@
+ Configuring MBean descriptors files for custom components.</li>
+ <li><a href="default-servlet.html"><strong>Default
Servlet</strong></a> -
+ Configuring the default servlet and customizing directory listings.</li>
+-<li><a href="cluster-howto.html"><strong>Apache Tomcat
Clustering</strong></a> -
+- Enable session replication in a Apache Tomcat environment.</li>
+ <li><a
href="balancer-howto.html"><strong>Balancer</strong></a> -
+ Configuring, using, and extending the load balancer application.</li>
+ <li><a
href="connectors.html"><strong>Connectors</strong></a> -
+Index: webapps/docs/config/cluster.xml
+===================================================================
+--- webapps/docs/config/cluster.xml (revision 590752)
++++ webapps/docs/config/cluster.xml (working copy)
+@@ -1,151 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- The tomcat cluster implementation provides session replication, context attribute
replication and
+- cluster wide WAR file deployment.
+- While the <code>Cluster</code> configuration is fairly complex, the
default configuration will work
+- for most people out of the box. </p><p>
+- The Tomcat Cluster implementation is very extensible, and hence we have exposed a
myriad of options,
+- making the configuration seem like a lot, but don't lose faith, instead you have a
tremendous control
+- over what is going on.</p>
+-</section>
+-<section name="Engine vs Host placement">
+- <p>
+- You can place the <code><Cluster></code> element inside
either the <code><Engine></code>
+- container or the <code><Host></code> container.<br/>
+- Placing it in the engine, means that you will support clustering in all virtual hosts
of Tomcat,
+- and share the messaging component. When you place the
<code><Cluster></code> inside the
<code><Engine></code>
+- element, the cluster will append the host name of each session manager to the
managers name so that two contexts with
+- the same name but sitting inside two different hosts will be distinguishable.
+- </p>
+-</section>
+-<section name="Context Attribute Replication">
+- <p>To configure context attribute replication, simply do this by swapping out
the context implementation
+- used for your application context.
+- <source><Context
className="org.apache.catalina.ha.context.ReplicatedContext"/></source>
+- This context extends the Tomcat <code><a
href="context.html">StandardContext</a></code>
+- so all the options from the <a href="context.html">base
implementation</a> are valid.
+- </p>
+-</section>
+-<section name="Nested Components">
+- <p><b><a
href="cluster-manager.html">Manager</a>:</b> <br/>
+- The session manager element identifies what kind of session manager is used in this
cluster implementation.
+- This manager configuration is identical to the one you would use in a regular
<code><a
href="context.html#Nested%20xComponents"><Context></a></code>
configuration.
+- <br/>The default value is the
<code>org.apache.catalina.ha.session.DeltaManager</code> that is closely
coupled with
+- the <code>SimpleTcpCluster</code> implementation. Other managers like
the <code>org.apache.catalina.ha.session.BackupManager</code>
+- are/could be loosely coupled and don't rely on the
<code>SimpleTcpCluster</code> for its data replication.
+- </p>
+- <p><b><a
href="cluster-channel.html">Channel</a>:</b> <br/>
+- The Channel and its sub components are all part of the IO layer
+- for the cluster group, and is a module in it's own that we have nick named
"Tribes"
+- <br/>
+- Any configuring and tuning of the network layer, the messaging and the membership
logic
+- will be done in the channel and its nested components.
+- You can always find out more about <a
href="../tribes/introduction.html">Apache Tribes</a>
+- </p>
+- <p><b><a
href="cluster-valve.html">Valve</a>:</b> <br/>
+- The Tomcat Cluster implementation uses <code>Tomcat <a
href="valve.html">Valves</a></code> to
+- track when requests enter and exit the servlet container. It uses these valves to be
able to make
+- intelligent decisions on when to replicate data, which is always at the end of a
request.
+- </p>
+- <p><b><a
href="cluster-deployer.html">Deployer</a>:</b> <br/>
+- The Deployer component is the Tomcat Farm Deployer. It allows you to deploy and
undeploy applications
+- cluster wide.
+- </p>
+- <p><b><a
href="cluster-listener.html">ClusterListener</a>:</b>
<br/>
+- ClusterListener's are used to track messages sent and received using the
<code>SimpleTcpCluster</code>.
+- If you wish to track messages, you can add a listener here, or you can add a valve
to the channel object.
+- </p>
+-</section>
+-
+-<section name="Deprecated configuration options">
+- <p>
+- <b>Deprecated settings:</b> In the previous version of Tomcat you were
able to control session
+- manager settings using manager.<property>=value.
+- This has been discontinued, as the way it was written interfers with
+- the ability to support multiple different manager classes under one cluster
implementation,
+- as the same properties might have the different effect on different managers.
+- </p>
+-</section>
+-
+-<section name="Attributes">
+- <subsection name="SimpleTcpCluster Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- <p>The main cluster class, currently only one is available,
+- <code>org.apache.catalina.ha.tcp.SimpleTcpCluster</code>
+- </p>
+- </attribute>
+- <attribute name="channelSendOptions" required="true">
+- <p>The Tribes channel send options, default is
<code>8</code>.<br/>
+- This option is used to set the flag that all messages sent through the
+- SimpleTcpCluster uses. The flag decides how the messages are sent, and is a
simple logical OR.<br/>
+-
+- <source>
+- int options= Channel.SEND_OPTIONS_ASYNCHRONOUS |
+- Channel.SEND_OPTIONS_SYNCHRONIZED_ACK |
+- Channel.SEND_OPTIONS_USE_ACK;
+- </source>
+- Some of the values are:<br/>
+- <code>Channel.SEND_OPTIONS_SYNCHRONIZED_ACK =
0x0004</code><br/>
+- <code>Channel.SEND_OPTIONS_ASYNCHRONOUS = 0x0008</code><br/>
+- <code>Channel.SEND_OPTIONS_USE_ACK = 0x0002</code><br/>
+- So to use ACK and ASYNC messaging, the flag would be <code>10 (8+2) or
0x000B</code><br/>
+- </p>
+- </attribute>
+-
+- <attribute name="heartbeatBackgroundEnabled"
required="false">
+- <p>Enable this flag don't forget to disable the channel heartbeat
thread.
+- </p>
+- </attribute>
+-
+- <attribute name="doClusterLog" required="false">
+- <p><b>Deprecated since 6.0.0</b></p>
+- <p>Possible values are <code>true</code> or
<code>false</code><br/>
+- Value is inherited from Tomcat 5.5 and has no official meaning.
+- to configure logging, use the standard tomcat logging configuration.
+- </p>
+- </attribute>
+- <attribute name="clusterLogName" required="false">
+- <p><b>Deprecated since 6.0.0</b></p>
+- <p>
+- Value is inherited from Tomcat 5.5 and has no official meaning.
+- to configure logging, use the standard tomcat logging configuration.
+- </p>
+- </attribute>
+- </attributes>
+- </subsection>
+-</section>
+-</body>
+-</document>
+Index: webapps/docs/config/cluster-receiver.xml
+===================================================================
+--- webapps/docs/config/cluster-receiver.xml (revision 590752)
++++ webapps/docs/config/cluster-receiver.xml (working copy)
+@@ -1,158 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-receiver.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster Receiver object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- The receiver component is responsible for receiving cluster messages.
+- As you might notice through the configuration, is that the receiving of messages
+- and sending of messages are two different components, this is different from many
other
+- frameworks, but there is a good reason for it, to decouple the logic for how messages
are sent from
+- how messages are received.<br/>
+- The receiver is very much like the Tomcat Connector, its the base of the thread pool
+- for incoming cluster messages. The receiver is straight forward, but all the socket
settings
+- for incoming traffic are managed here.
+- </p>
+-</section>
+-
+-<section name="Blocking vs Non-Blocking Receiver">
+- <p>
+- The receiver supports both a non blocking,
<code>org.apache.catalina.tribes.transport.nio.NioReceiver</code>, and a
+- blocking,
<code>org.apache.catalina.tribes.transport.bio.BioReceiver</code>. It is
preferred to use the non blocking receiver
+- to be able to grow your cluster without running into thread starvation.<br/>
+- Using the non blocking receiver allows you to with a very limited thread count to
serve a large number of messages.
+- Usually the rule is to use 1 thread per node in the cluster for small clusters, and
then depending on your message frequency
+- and your hardware, you'll find an optimal number of threads peak out at a certain
number.
+- </p>
+-</section>
+-
+-<section name="Attributes">
+- <subsection name="Common Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- The implementation of the receiver component. Two implementations available,
+- <code>org.apache.catalina.tribes.transport.nio.NioReceiver</code> and
+-
<code>org.apache.catalina.tribes.transport.bio.BioReceiver</code>.<br/>
+- The <code>org.apache.catalina.tribes.transport.nio.NioReceiver</code>
is the
+- preferred implementation
+- </attribute>
+- <attribute name="address" required="false">
+- The address (network interface) to listen for incoming traffic.
+- Same as the bind address. The default value is <code>auto</code> and
translates to
+- <code>java.net.InetAddress.getLocalHost().getHostAddress()</code>.
+- </attribute>
+- <attribute name="direct" required="false">
+- Possible values are <code>true</code> or
<code>false</code>.
+- Set to true if you want the receiver to use direct bytebuffers when reading data
+- from the sockets.
+- </attribute>
+- <attribute name="port" required="false">
+- The listen port for incoming data. The default value is
<code>4000</code>.
+- To avoid port conflicts the receiver will automatically bind to a free port within
the range of
+- <code> port <= bindPort <= port+autoBind</code>
+- So for example, if port is 4000, and autoBind is set to 10, then the receiver will
open up
+- a server socket on the first available port in the range 4000-4100.
+- </attribute>
+- <attribute name="autoBind" required="false">
+- Default value is <code>100</code>.
+- Use this value if you wish to automatically avoid port conflicts the cluster
receiver will try to open a
+- server socket on the <code>port</code> attribute port, and then work
up <code>autoBind</code> number of times.
+- </attribute>
+- <attribute name="securePort" required="false">
+- The secure listen port. This port is SSL enabled. If this attribute is omitted no
SSL port is opened up.
+- There default value is unset, meaning there is no SSL socket available.
+- </attribute>
+- <attribute name="selectorTimeout" required="false">
+- The value in milliseconds for the polling timeout in the
<code>NioReceiver</code>. On older versions of the JDK
+- there have been bugs, that should all now be cleared out where the selector never
woke up.
+- The default value is a very high <code>5000</code> milliseconds.
+- </attribute>
+- <attribute name="maxThreads" required="false">
+- The maximum number of threads in the receiver thread pool. The default value is
<code>6</code>
+- Adjust this value relative to the number of nodes in the cluster, the number of
messages being exchanged and
+- the hardware you are running on. A higher value doesn't mean more effiecency,
tune this value according to your
+- own test results.
+- </attribute>
+- <attribute name="minThreads" required="false">
+- Minimum number of threads to be created when the receiver is started up. Default
value is <code>6</code>
+- </attribute>
+- <attribute name="ooBInline" required="false">
+- Boolean value for the socket OOBINLINE option. Possible values are
<code>true</code> or <code>false</code>.
+- </attribute>
+- <attribute name="rxBufSize" required="false">
+- The receiver buffer size on the receiving sockets. Value is in bytes, the default
value is <code>43800</code> bytes.
+- </attribute>
+- <attribute name="txBufSize" required="false">
+- The sending buffer size on the receiving sockets. Value is in bytes, the default
value is <code>25188</code> bytes.
+- </attribute>
+- <attribute name="soKeepAlive" required="false">
+- Boolean value for the socket SO_KEEPALIVE option. Possible values are
<code>true</code> or <code>false</code>.
+- </attribute>
+- <attribute name="soLingerOn" required="false">
+- Boolean value to determine whether to use the SO_LINGER socket option.
+- Possible values are <code>true</code> or
<code>false</code>. Default value is <code>true</code>.
+- </attribute>
+- <attribute name="soLingerTime" required="false">
+- Sets the SO_LINGER socket option time value. The value is in seconds.
+- The default value is <code>3</code> seconds.
+- </attribute>
+- <attribute name="soReuseAddress" required="false">
+- Boolean value for the socket SO_REUSEADDR option. Possible values are
<code>true</code> or <code>false</code>.
+- </attribute>
+- <attribute name="soTrafficClass" required="false">
+- Sets the traffic class level for the socket, the value is between 0 and 255.
+- Different values are defined in <a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/Socket.html#s...
+- java.net.Socket#setTrafficClass(int)</a>.
+- </attribute>
+- <attribute name="tcpNoDelay" required="false">
+- Boolean value for the socket TCP_NODELAY option. Possible values are
<code>true</code> or <code>false</code>.
+- The default value is <code>true</code>
+- </attribute>
+- <attribute name="timeout" required="false">
+- Sets the SO_TIMEOUT option on the socket. The value is in milliseconds and the
default value is <code>3000</code>
+- milliseconds.
+- </attribute>
+- <attribute name="useBufferPool" required="false">
+- Boolean value whether to use a shared buffer pool of cached
<code>org.apache.catalina.tribes.io.XByteBuffer</code>
+- objects. If set to true, the XByteBuffer that is used to pass a message up the
channel, will be recycled at the end
+- of the requests. This means that interceptors in the channel must not maintain a
reference to the object
+- after the
<code>org.apache.catalina.tribes.ChannelInterceptor#messageReceived</code>
method has exited.
+- </attribute>
+- </attributes>
+- </subsection>
+- <subsection name="NioReceiver">
+- </subsection>
+- <subsection name="BioReceiver">
+- </subsection>
+-
+-</section>
+-</body>
+-</document>
+Index: webapps/docs/config/cluster-channel.xml
+===================================================================
+--- webapps/docs/config/cluster-channel.xml (revision 590752)
++++ webapps/docs/config/cluster-channel.xml (working copy)
+@@ -1,102 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-channel.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster Channel object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- The cluster channel is the main component of a small framework we've nicknamed
Apache Tribes.<br/>
+- The channel manages a set of sub components and together they create a group
communication framework.<br/>
+- This framework is then used internally by the components that need to send messages
between different Tomcat instances.
+- <br/>
+- A few examples of these components would be the SimpleTcpCluster that does the
messaging for the DeltaManager,
+- or the BackupManager that uses a different replication strategy. The ReplicatedContext
object does also
+- use the channel object to communicate context attribute changes.
+-</section>
+-<section name="Nested Components">
+- <p><b><a
href="cluster-membership.html">Channel/Membership</a>:</b>
<br/>
+- The Membership component is responsible for auto discovering new nodes in the
cluster
+- and also to provide for notifications for any nodes that have not responded with a
heartbeat.
+- The default implementation uses multicast.<br/>
+- In the membership component you configure how your nodes, aka. members, are to be
discovered and/or
+- divided up.
+- You can always find out more about <a
href="../tribes/introduction.html">Apache Tribes</a>
+- </p>
+- <p><b><a
href="cluster-sender.html">Channel/Sender</a>:</b> <br/>
+- The Sender component manages all outbound connections and data messages that are
sent
+- over the network from one node to another.
+- This component allows messages to be sent in parallel.
+- The default implementation uses TCP client sockets, and socket tuning for outgoing
messages are
+- configured here.<br/>
+- You can always find out more about <a
href="../tribes/introduction.html">Apache Tribes</a>
+- </p>
+- <p><b><a
href="cluster-sender.html#transport">Channel/Sender/Transport</a>:</b>
<br/>
+- The Transport component is the bottom IO layer for the sender component.
+- The default implementation uses non-blocking TCP client sockets.<br/>
+- You can always find out more about <a
href="../tribes/introduction.html">Apache Tribes</a>
+- </p>
+- <p><b><a
href="cluster-receiver.html">Channel/Receiver</a>:</b>
<br/>
+- The receiver component listens for messages from other nodes.
+- Here you will configure the cluster thread pool, as it will dispatch incoming
+- messages to a thread pool for faster processing.
+- The default implementation uses non-blocking TCP server sockets.<br/>
+- You can always find out more about <a
href="../tribes/introduction.html">Apache Tribes</a>
+- </p>
+- <p><b><a
href="cluster-interceptor.html">Channel/Interceptor</a>:</b>
<br/>
+- The channel will send messages through an interceptor stack. Because of this, you
have the ability to
+- customize the way messages are sent and received, and even how membership is
handled.<br/>
+- You can always find out more about <a
href="../tribes/introduction.html">Apache Tribes</a>
+- </p>
+-</section>
+-
+-
+-<section name="Attributes">
+-
+- <subsection name="Common Attributes">
+-
+- <attributes>
+-
+- <attribute name="className" required="true">
+- The default value here is
<code>org.apache.catalina.tribes.group.GroupChannel</code> and is
+- currently the only implementation available.
+- </attribute>
+-
+-
+- </attributes>
+-
+-
+- </subsection>
+-
+-
+-</section>
+-
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/config/cluster-manager.xml
+===================================================================
+--- webapps/docs/config/cluster-manager.xml (revision 590752)
++++ webapps/docs/config/cluster-manager.xml (working copy)
+@@ -1,112 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-manager.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The ClusterManager object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- A cluster manager is an extension to Tomcat's session manager interface,
+- <code>org.apache.catalina.Manager</code>
+- A cluster manager must implement the
<code>org.apache.catalina.ha.ClusterManager</code> and is solely
+- responsible for how the session is replicated.<br/>
+- There are currently two different managers, the
<code>org.apache.catalina.ha.session.DeltaManager</code> replicates deltas
+- of session data to all members in the cluster. This implementation is proven and works
very well, but has a limitation
+- as it requires the cluster members to be homogeneous, all nodes must deploy the same
applications and be exact replicas.
+- The <code>org.apache.catalina.ha.session.BackupManager</code> also
replicates deltas but only to one backup node.
+- The location of the backup node is known to all nodes in the cluster. It also supports
heterogeneous deployments,
+- so the manager knows at what locations the webapp is deployed.<br/>
+- We are planning to add more managers with even more sophisticated backup mechanism to
support even larger clusters.
+- Check back soon!
+- </p>
+-</section>
+-
+-<section name="The <Manager>">
+- <p>
+- The <code><Manager></code> element defined inside the
<code><Cluster></code> element
+- is the template defined for all web applications that are marked
<code><distributable/></code>
+- in their <code>web.xml</code> file.
+- However, you can still override the manager implementation on a per web application
basis,
+- by putting the <code><Manager></code> inside the
<code><Context></code> element either in the
+- <code><a href="context.html">context.xml</a></code>
file or the <code><a
href="index.html">server.xml</a></code> file.
+- </p>
+-</section>
+-
+-<section name="Attributes">
+- <subsection name="Common Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- </attribute>
+- <attribute name="name" required="false">
+- <b>The name of this cluster manager, the name is used to identify a session
manager on a node.
+- The name might get modified by the <code>Cluster</code> element to
make it unique in the container.</b>
+- </attribute>
+- <attribute name="defaultMode" required="false">
+- <b>Deprecated since 6.0.0</b>
+- </attribute>
+- <attribute name="notifyListenersOnReplication"
required="false">
+- Set to <code>true</code> if you wish to have session listeners
notified when
+- session attributes are being replicated or removed across Tomcat nodes in the
cluster.
+- </attribute>
+- <attribute name="expireSessionsOnShutdown"
required="false">
+- When a webapplication is being shutdown, Tomcat issues an expire call to each
session to
+- notify all the listeners. If you wish for all sessions to expire on all nodes
when
+- a shutdown occurs on one node, set this value to <code>true</code>.
+- Default value is <code>false</code>.
+- </attribute>
+-
+- </attributes>
+- </subsection>
+- <subsection name="org.apache.catalina.ha.session.DeltaManager
Attributes">
+- <attributes>
+- <attribute name="domainReplication" required="false">
+- Set to true if you wish sessions to be replicated only to members that have the
same logical
+- domain set. If set to false, session replication will ignore the domain setting
the
+- <code><a
href="cluster-membership.html"><Membership></a></code>
+- element.
+- </attribute>
+- <attribute name="expireSessionsOnShutdown"
required="false">
+- When a webapplication is being shutdown, Tomcat issues an expire call to each
session to
+- notify all the listeners. If you wish for all sessions to expire on all nodes
when
+- a shutdown occurs on one node, set this value to <code>true</code>.
+- Default value is <code>false</code>.
+- </attribute>
+- </attributes>
+- </subsection>
+- <subsection name="org.apache.catalina.ha.session.BackupManager
Attributes">
+- <attributes>
+- <attribute name="mapSendOptions" required="false">
+- The backup manager uses a replicated map, this map is sending and receiving
messages.
+- You can setup the flag for how this map is sending messages, the default value is
<code>6</code>(asynchronous).
+- </attribute>
+- </attributes>
+- </subsection>
+-</section>
+-</body>
+-</document>
+Index: webapps/docs/config/cluster-valve.xml
+===================================================================
+--- webapps/docs/config/cluster-valve.xml (revision 590752)
++++ webapps/docs/config/cluster-valve.xml (working copy)
+@@ -1,103 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-valve.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster Valve object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- A cluster valve is no different from any other <a
href="valve.html">Tomcat <code>Valve</code></a>.
+- The cluster valves are interceptors in the invokation chain for HTTP requests, and the
clustering implementation
+- uses these valves to make intelligent decision around data and when data should be
replicated.
+- </p>
+- <p>
+- A cluster valve must implement the
<code>org.apache.catalina.ha.ClusterValve</code> interface.
+- This is a simple interface that extends the
<code>org.apache.catalina.Valve</code> interface.
+- </p>
+-</section>
+-
+-<section name="org.apache.catalina.ha.tcp.ReplicationValve">
+- The <code>ReplicationValve</code> will notify the cluster at the end of a
HTTP request
+- so that the cluster can make a decision whether there is data to be replicated or
not.
+- <subsection name="Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Set value to
<code>org.apache.catalina.ha.tcp.ReplicationValve</code>
+- </attribute>
+- <attribute name="filter" required="false">
+- For known file extensions or urls, you can use a filter to
+- notify the cluster that the session has not been modified during this
+- request and the cluster doesn't have to probe the session managers for
changes.
+- If there is a filter match, the cluster assumes there has been no session
change.
+- An example filter would look like
<code>filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"</code>
+- The filter uses regular expressions and each filter is delimited by a semi
colon.
+- <code>Pattern#compile(java.lang.String)</code>
+- </attribute>
+- <attribute name="primaryIndicator" required="false">
+- Boolean value, so to true, and the replication valve will insert a request
attribute with the name
+- defined by the <code>primaryIndicatorName</code> attribute.
+- The value inserted into the request attribute is either
<code>Boolean.TRUE</code> or
+- <code>Boolean.FALSE</code>
+- </attribute>
+- <attribute name="primaryIndicatorName"
required="false">
+- Default value is
<code>org.apache.catalina.ha.tcp.isPrimarySession</code>
+- The value defined here is the name of the request attribute that contains the
boolean value
+- if the session is primary on this server or not.
+- </attribute>
+- <attribute name="statistics" required="false">
+- Boolean value. Set to <code>true</code> if you want the valve to
collect request statistics.
+- Default value is <code>false</code>
+- </attribute>
+- </attributes>
+- </subsection>
+-</section>
+-
+-<section name="org.apache.catalina.ha.session.JvmRouteBinderValve">
+- In case of a mod_jk failover, the <code>JvmRouteBinderValve</code> will
replace the
+- <code>jvmWorker</code> attribute in the session Id, to make future
requests stick to this
+- node. If you want failback capability, don't enable this valve, but if you want
your failover to stick,
+- and for mod_jk not to have to keep probing the node that went down, you use this
valve.
+- <subsection name="Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- <code>org.apache.catalina.ha.session.JvmRouteBinderValve</code>
+- </attribute>
+- <attribute name="enabled" required="false">
+- Default value is <code>true</code>
+- Runtime attribute to turn on and off turn over of the session's jvmRoute
value.
+- </attribute>
+-
+- </attributes>
+- </subsection>
+-</section>
+-
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/config/cluster-sender.xml
+===================================================================
+--- webapps/docs/config/cluster-sender.xml (revision 590752)
++++ webapps/docs/config/cluster-sender.xml (working copy)
+@@ -1,163 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-sender.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster Sender object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- The channel sender component is responsible for delivering outgoing cluster messages
over the network.
+- In the default implementation,
<code>org.apache.catalina.tribes.transport.ReplicationTransmitter</code>,
+- the sender is a fairly empty shell with not much logic around a fairly complex
<code><Transport></code>
+- component the implements the actual delivery mechanism.
+- </p>
+-</section>
+-
+-<section name="Concurrent Parallel Delivery">
+- <p>
+- In the default <code>transport</code> implementation,
<code>org.apache.catalina.tribes.transport.nio.PooledParallelSender</code>,
+- Apache Tribes implements what we like to call "Concurrent Parallel
Delivery".
+- This means that we can send a message to more than one destination at the same
time(parallel), and
+- deliver two messages to the same destination at the same time(concurrent). Combine
these two and we have
+- "Concurrent Parallel Delivery".
+- </p>
+- <p>
+- When is this useful? The simplest example we can think of is when part of your code is
sending a 10MB message,
+- like a war file being deployed, and you need to push through a small 10KB message, say
a session being replicated,
+- you don't have to wait for the 10MB message to finish, as a separate thread will
push in the small message
+- transmission at the same time. Currently there is no interrupt, pause or priority
mechanism avaiable, but check back soon.
+- </p>
+-</section>
+-
+-<section name="Nested Elements">
+- <p>
+- The nested element <code><Transport></code> is is not
required, by encouraged, as this is where
+- you would set all the socket options for the outgoing messages. Please see its
attributes below.
+- There are two implementations, in a similar manner to the <a
href="cluster-receiver.html">receiver</a>, one is non-blocking
+- based and the other is built using blocking IO. <br/>
+- <code>org.apache.catalina.tribes.transport.bio.PooledMultiSender</code>
is the blocking implemenation and
+-
<code>org.apache.catalina.tribes.transport.nio.PooledParallelSender</code>.
+- Parallel delivery is not available for the blocking implementation due to the fact
that it is blocking a thread on sending data.
+- </p>
+-</section>
+-
+-<section name="Attributes">
+- <subsection name="Common Sender Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Required, only available implementation is
<code>org.apache.catalina.tribes.transport.ReplicationTransmitter</code>
+- </attribute>
+- </attributes>
+- </subsection>
+- <subsection name="Common Transport Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Required, an implementation of the
<code>org.apache.catalina.tribes.transport.MultiPointSender</code>.<br/>
+- Non-blocking implementation is
<code>org.apache.catalina.tribes.transport.nio.PooledParallelSender</code><br/>
+- Blocking implementation is
<code>org.apache.catalina.tribes.transport.bio.PooledMultiSender</code>
+- </attribute>
+- <attribute name="rxBufSize" required="false">
+- The receive buffer size on the socket.
+- Default value is <code>25188</code> bytes.
+- </attribute>
+- <attribute name="txBufSize" required="false">
+- The send buffer size on the socket.
+- Default value is <code>43800</code> bytes.
+- </attribute>
+- <attribute name="direct" required="false">
+- Possible values are <code>true</code> or
<code>false</code>.
+- Set to true if you want the receiver to use direct bytebuffers when reading data
+- from the sockets. Default value is <code>false</code>
+- </attribute>
+- <attribute name="keepAliveCount" required="false">
+- The number of requests that can go through the socket before the socket is
closed, and reopened
+- for the next request. The default value is <code>-1</code>, which is
unlimited.
+- </attribute>
+- <attribute name="keepAliveTime" required="false">
+- The number of milliseconds a connection is kept open after its been opened.
+- The default value is <code>-1</code>, which is unlimited.
+- </attribute>
+- <attribute name="timeout" required="false">
+- Sets the SO_TIMEOUT option on the socket. The value is in milliseconds and the
default value is <code>3000</code>
+- milliseconds.
+- </attribute>
+- <attribute name="maxRetryAttempts" required="false">
+- How many times do we retry a failed message, that received a IOException at the
socket level.
+- The default value is <code>1</code>, meaning we will retry a message
that has failed once.
+- In other words, we will attempt a message send no more than twice. One is the
original send, and one is the
+- <code>maxRetryAttempts</code>.
+- </attribute>
+- <attribute name="ooBInline" required="false">
+- Boolean value for the socket OOBINLINE option. Possible values are
<code>true</code> or <code>false</code>.
+- </attribute>
+- <attribute name="soKeepAlive" required="false">
+- Boolean value for the socket SO_KEEPALIVE option. Possible values are
<code>true</code> or <code>false</code>.
+- </attribute>
+- <attribute name="soLingerOn" required="false">
+- Boolean value to determine whether to use the SO_LINGER socket option.
+- Possible values are <code>true</code> or
<code>false</code>. Default value is <code>true</code>.
+- </attribute>
+- <attribute name="soLingerTime" required="false">
+- Sets the SO_LINGER socket option time value. The value is in seconds.
+- The default value is <code>3</code> seconds.
+- </attribute>
+- <attribute name="soReuseAddress" required="false">
+- Boolean value for the socket SO_REUSEADDR option. Possible values are
<code>true</code> or <code>false</code>.
+- </attribute>
+- <attribute name="soTrafficClass" required="false">
+- Sets the traffic class level for the socket, the value is between 0 and 255.
+- Default value is <code>int soTrafficClass = 0x04 | 0x08 |
0x010;</code>
+- Different values are defined in <a
href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/Socket.html#s...
+- java.net.Socket#setTrafficClass(int)</a>.
+- </attribute>
+- <attribute name="tcpNoDelay" required="false">
+- Boolean value for the socket TCP_NODELAY option. Possible values are
<code>true</code> or <code>false</code>.
+- The default value is <code>true</code>
+- </attribute>
+- <attribute name="throwOnFailedAck" required="false">
+- Boolean value, default value is <code>true</code>.
+- If set to true, the sender will throw a
<code>org.apache.catalina.tribes.RemoteProcessException</code>
+- when we receive a negative ack from the remote member.
+- Set to false, and Tribes will treat a positive ack the same way as a negative
ack, that the message was received.
+- </attribute>
+- </attributes>
+- </subsection>
+- <subsection name="PooledParallelSender Attributes">
+- <attributes>
+- <attribute name="poolSize" required="false">
+- The maximum number of concurrent connections from A to B.
+- The value is based on a per-destination count.
+- The default value is <code>25</code>
+- </attribute>
+-
+- </attributes>
+- </subsection>
+-</section>
+-</body>
+-</document>
+Index: webapps/docs/config/cluster-deployer.xml
+===================================================================
+--- webapps/docs/config/cluster-deployer.xml (revision 590752)
++++ webapps/docs/config/cluster-deployer.xml (working copy)
+@@ -1,62 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-deployer.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster Deployer object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>This goober is currently pretty broken, but we are working hard to fix
it</p>
+-
+-
+-</section>
+-
+-
+-<section name="Attributes">
+-
+- <subsection name="Common Attributes">
+-
+- <attributes>
+-
+- <attribute name="className" required="true">
+-
+- </attribute>
+-
+-
+- </attributes>
+-
+-
+- </subsection>
+-
+-
+-</section>
+-
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/config/cluster-listener.xml
+===================================================================
+--- webapps/docs/config/cluster-listener.xml (revision 590752)
++++ webapps/docs/config/cluster-listener.xml (working copy)
+@@ -1,77 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-listener.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The ClusterListener object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- The <code>org.apache.catalina.ha.ClusterListener</code> base class
+- lets you listen in on messages that are received by the
<code>Cluster</code> component.
+- </p>
+-
+-</section>
+-<section name="org.apache.catalina.ha.session.ClusterSessionListener">
+- <p>
+- When using the DeltaManager, the messages are received by the Cluster object and are
propagated to the
+- to the manager through this listener.
+- </p>
+-</section>
+-<section
name="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener">
+- <p>
+- Listens for session Id changes. This listener is only used if you are using mod_jk
+- along with the <code>jvmRoute</code> attribute where the session Id can
change.
+- In the event of a change, the <code>JvmRouteBinderValve</code> will
broadcast the
+- session change and it will get picked up by this listener.
+- </p>
+-</section>
+-
+-<section name="Attributes">
+-
+- <subsection name="Common Attributes">
+-
+- <attributes>
+-
+- <attribute name="className" required="true">
+-
+- </attribute>
+-
+-
+- </attributes>
+-
+-
+- </subsection>
+-
+-
+-</section>
+-
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/config/project.xml
+===================================================================
+--- webapps/docs/config/project.xml (revision 590752)
++++ webapps/docs/config/project.xml (working copy)
+@@ -50,7 +50,6 @@
+ <item name="Context"
href="context.html"/>
+ <item name="Engine"
href="engine.html"/>
+ <item name="Host" href="host.html"/>
+- <item name="Cluster"
href="cluster.html"/>
+ </menu>
+
+ <menu name="Nested Components">
+@@ -62,18 +61,6 @@
+ <item name="Valve"
href="valve.html"/>
+ </menu>
+
+- <menu name="Cluster Elements">
+- <item name="Cluster"
href="cluster.html"/>
+- <item name="Manager"
href="cluster-manager.html"/>
+- <item name="Channel"
href="cluster-channel.html"/>
+- <item name="Channel/Membership"
href="cluster-membership.html"/>
+- <item name="Channel/Sender"
href="cluster-sender.html"/>
+- <item name="Channel/Receiver"
href="cluster-receiver.html"/>
+- <item name="Channel/Interceptor"
href="cluster-interceptor.html"/>
+- <item name="Valve"
href="cluster-valve.html"/>
+- <item name="Deployer"
href="cluster-deployer.html"/>
+- <item name="ClusterListener"
href="cluster-listener.html"/>
+- </menu>
+ </body>
+
+ </project>
+Index: webapps/docs/config/cluster-membership.xml
+===================================================================
+--- webapps/docs/config/cluster-membership.xml (revision 590752)
++++ webapps/docs/config/cluster-membership.xml (working copy)
+@@ -1,141 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-membership.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Cluster Membership object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- The membership component in the Apache Tribes <a
href="cluster-channel.html">Channel</a> is responsible
+- for dynamic discovery of other members(nodes) in the cluster.
+- </p>
+-</section>
+-
+-<section name="Default Implementation">
+- <p>
+- The default implementation of the cluster group notification is built on top of
multicast heartbeats
+- sent using UDP packets to a multicast IP address.
+- Cluster members are grouped together by using the same multicast address/port
combination.
+- Each member sends out a heartbeat with a given interval
(<code>frequency</code>), and this
+- heartbeat is used for dynamic discovery.
+- In a similar fashion, if a heartbeat has not been received in a timeframe specified
by <code>dropTime</code>
+- ms. a member is considered suspect and the channel and any membership listener will
be notified.
+- </p>
+-</section>
+-
+-
+-
+-<section name="Attributes">
+-
+- <subsection name="Multicast Attributes">
+-
+- <attributes>
+-
+- <attribute name="className" required="true">
+- <p>
+- The default value is
<code>org.apache.catalina.tribes.membership.McastService</code>
+- and is currently the only implementation.
+- This implementation uses multicast heartbeats for member discovery.
+- </p>
+- </attribute>
+- <attribute name="address" required="false">
+- <p>
+- The multicast address that the membership will broadcast its presence and listen
+- for other heartbeats on. The default value is <code>228.0.0.4</code>
+- Make sure your network is enabled for multicast traffic.<br/>
+- The multicast address, in conjunction with the <code>port</code> is
what
+- creates a cluster group. To divide up your farm into several different group, or
to
+- split up QA from production, change the <code>port</code> or the
<code>address</code>
+- <br/>Previously known as mcastAddr.
+- </p>
+- </attribute>
+- <attribute name="port" required="false">
+- <p>
+- The multicast port, the default value is
<code>45564</code><br/>
+- The multicast port, in conjunction with the <code>address</code> is
what
+- creates a cluster group. To divide up your farm into several different group, or
to
+- split up QA from production, change the <code>port</code> or the
<code>address</code>
+- </p>
+- </attribute>
+- <attribute name="frequency" required="false">
+- <p>
+- The frequency in milliseconds in which heartbeats are sent out. The default value
is <code>500</code> ms.<br/>
+- In most cases the default value is sufficient. Changing this value, simply changes
the interval in between heartbeats.
+- </p>
+- </attribute>
+- <attribute name="dropTime" required="false">
+- <p>
+- The membership component will time out members and notify the Channel if a member
fails to send a heartbeat within
+- a give time. The default value is <code>3000</code> ms. This means,
that if a heartbeat is not received from a
+- member in that timeframe, the membership component will notify the cluster of
this.<br/>
+- On a high latency network you may wish to increase this value, to protect against
false positives.<br/>
+- Apache Tribes also provides a <a
href="cluster-interceptor.html#tcpfailuredetector"><code>TcpFailureDetector</code></a>
that will
+- verify a timeout using a TCP connection when a heartbeat timeout has occurred.
This protects against false positives.
+- </p>
+- </attribute>
+- <attribute name="bind" required="false">
+- <p>
+- Use this attribute if you wish to bind your multicast traffic to a specific
network interface.
+- By default, or when this attribute is unset, it tries to bind to
<code>0.0.0.0</code> and sometimes on multihomed hosts
+- this becomes a problem.
+- </p>
+- </attribute>
+- <attribute name="ttl" required="false">
+- <p>
+- The time-to-live setting for the multicast heartbeats.
+- This setting should be a value between 0 and 255. The default value is VM
implementation specific.
+- </p>
+- </attribute>
+- <attribute name="domain" required="false">
+- <p>
+- Apache Tribes has the ability to logically group members into domains, by using
this domain attribute.
+- The <code>org.apache.catalina.tribes.Member.getDomain()</code> method
returns the value specified here.
+- </p>
+- </attribute>
+- <attribute name="soTimeout" required="false">
+- <p>
+- The sending and receiving of heartbeats is done on a single thread, hence to avoid
blocking this thread forever,
+- you can control the <code>SO_TIMEOUT</code> value on this
socket.<br/>
+- If a value smaller or equal to 0 is presented, the code will default this value to
frequency
+- </p>
+- </attribute>
+-
+-
+- </attributes>
+-
+-
+- </subsection>
+-
+-
+-</section>
+-
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/config/cluster-interceptor.xml
+===================================================================
+--- webapps/docs/config/cluster-interceptor.xml (revision 590752)
++++ webapps/docs/config/cluster-interceptor.xml (working copy)
+@@ -1,172 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-interceptor.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <title>The Channel Interceptor object</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Introduction">
+- <p>
+- Apache Tribes supports an interceptor architecture to intercept both messages and
membership notifications.
+- This architecture allows decoupling of logic and opens the way for some very kewl
feature add ons.
+- </p>
+-</section>
+-
+-<section name="Available Interceptors">
+- <p>
+- <ul>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.TcpFailureDetector</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.NonBlockingCoordinator</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.OrderInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.TwoPhaseCommitInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.DomainFilterInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.FragmentationInterceptor</code></li>
+-
<li><code>org.apache.catalina.tribes.group.interceptors.GzipInterceptor</code></li>
+- </ul>
+- </p>
+-</section>
+-
+-<section name="Static Membership">
+- <p>
+- In addition to dynamic discovery, Apache Tribes also supports static membership, with
membership verification.
+- To achieve this add the
<code>org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor</code>
+- underneath the
<code>org.apache.catalina.tribes.group.interceptors.TcpFailureDetector</code>
interceptor.
+- Inside the <code>StaticMembershipInterceptor</code> you can add the
static members you wish to have.
+- The <code>TcpFailureDetector</code> will do a health check on the static
members,and also monitor them for crashes
+- so they will have the same level of notification mechanism as the members that are
automatically discovered.
+- <source>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
+- <Member
className="org.apache.catalina.tribes.membership.StaticMember"
+- port="5678"
+- securePort="-1"
+- host="tomcat01.mydomain.com"
+- domain="staging-cluster"
+- uniqueId="{0,1,2,3,4,5,6,7,8,9}"/>
+- </Interceptor>
+-
+- </source>
+- </p>
+-</section>
+-
+-<section name="Attributes">
+-
+- <subsection name="Common Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Required, as there is no default
+- </attribute>
+- <attribute name="optionFlag" required="false">
+- If you want the interceptor to trigger on certain message depending on the
message's option flag,
+- you can setup the interceptors flag here.
+- The default value is <code>0</code>, meaning this interceptor will
trigger on all messages.
+- </attribute>
+- </attributes>
+- </subsection>
+-
+- <subsection
name="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor
Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Required, This dispatcher uses JDK 1.5 java.util.concurrent package
+- </attribute>
+- <attribute name="optionFlag" required="false">
+- The default and hard coded value is <code>8
(org.apache.catalina.tribes.Channel.SEND_OPTIONS_ASYNCHRONOUS)</code>.
+- The dispatcher will trigger on this value only, as it is predefined by Tribes.
+- </attribute>
+- </attributes>
+- </subsection>
+- <subsection
name="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor
Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Required, Same implementation as
<code>MessageDispatch15Interceptor</code>, but with JDK 1.4 compliance.
+- </attribute>
+- <attribute name="optionFlag" required="false">
+- The default and hard coded value is <code>8
(org.apache.catalina.tribes.Channel.SEND_OPTIONS_ASYNCHRONOUS)</code>.
+- The dispatcher will trigger on this value only, as it is predefined by Tribes.
+- </attribute>
+- </attributes>
+- </subsection>
+- <subsection
name="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector
Attributes">
+- <attributes>
+- </attributes>
+- </subsection>
+- <subsection
name="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor
Attributes">
+- <attributes>
+- <attribute name="interval" required="false">
+- Defines the interval in number of messages when we are to report the throughput
statistics.
+- The report is logged to the
<code>org.apache.juli.logging.LogFactory.getLog(ThroughputInterceptor.class)</code>
+- logger under the <code>INFO</code> level.
+- Default value is to report every <code>10000</code> messages.
+- </attribute>
+- </attributes>
+- </subsection>
+-
+- <subsection name="Nested element StaticMember Attributes">
+- <attributes>
+- <attribute name="className" required="true">
+- Only one implementation
available:<code>org.apache.catalina.tribes.membership.StaticMember</code>
+- </attribute>
+- <attribute name="port" required="true">
+- The port that this static member listens to for cluster messages
+- </attribute>
+- <attribute name="securePort" required="false">
+- The secure port this static member listens to for encrypted cluster messages
+- default value is <code>-1</code>, this value means the member is not
listening on a secure port
+- </attribute>
+- <attribute name="host" required="true">
+- The host (or network interface) that this static member listens for cluster
messages.
+- Three different type of values are possible:<br/>
+- 1. IP address in the form of "216.123.1.23"<br/>
+- 2. Hostnames like "tomcat01.mydomain.com" or
"tomcat01" as long as they resolve correctly<br/>
+- 3. byte array in string form, for example {216,123,12,3}<br/>
+- </attribute>
+- <attribute name="domain" required="true">
+- The logical cluster domain for this this static member listens for cluster
messages.
+- Two different type of values are possible:<br/>
+- 1. Regular string values like "staging-domain" or
"tomcat-cluster" will be converted into bytes
+- using ISO-8859-1 encoding.
+- 2. byte array in string form, for example {216,123,12,3}<br/>
+- </attribute>
+- <attribute name="uniqueId" required="true">
+- A universally uniqueId for this static member.
+- The values must be 16 bytes in the following form:<br/>
+- 1. byte array in string form, for example
{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}<br/>
+- </attribute>
+- </attributes>
+- </subsection>
+- <!--TODO Document all the interceptors-->
+-
+-</section>
+-
+-
+-</body>
+-
+-</document>
+Index: webapps/docs/project.xml
+===================================================================
+--- webapps/docs/project.xml (revision 590752)
++++ webapps/docs/project.xml (working copy)
+@@ -53,7 +53,7 @@
+ <item name="16) MBean Descriptor"
+ href="mbeans-descriptor-howto.html"/>
+ <item name="17) Default Servlet"
href="default-servlet.html"/>
+- <item name="18) Clustering"
href="cluster-howto.html"/>
++ <item name="18) Clustering"
href="not-supported.html"/>
+ <item name="19) Load Balancer"
href="balancer-howto.html"/>
+ <item name="20) Connectors"
href="connectors.html"/>
+ <item name="21) Monitoring and Management"
+Index: webapps/docs/cluster-howto.xml
+===================================================================
+--- webapps/docs/cluster-howto.xml (revision 590752)
++++ webapps/docs/cluster-howto.xml (working copy)
+@@ -1,701 +0,0 @@
+-<?xml version="1.0"?>
+-<!--
+- Licensed to the Apache Software Foundation (ASF) under one or more
+- contributor license agreements. See the NOTICE file distributed with
+- this work for additional information regarding copyright ownership.
+- The ASF licenses this file to You under the Apache License, Version 2.0
+- (the "License"); you may not use this file except in compliance with
+- the License. You may obtain a copy of the License at
+-
+-
http://www.apache.org/licenses/LICENSE-2.0
+-
+- Unless required by applicable law or agreed to in writing, software
+- distributed under the License is distributed on an "AS IS" BASIS,
+- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- See the License for the specific language governing permissions and
+- limitations under the License.
+--->
+-<!DOCTYPE document [
+- <!ENTITY project SYSTEM "project.xml">
+-]>
+-<document url="cluster-howto.html">
+-
+- &project;
+-
+- <properties>
+- <author email="fhanik(a)apache.org">Filip Hanik</author>
+- <author email="pero(a)apache.org">Peter Rossbach</author>
+- <title>Clustering/Session Replication HOW-TO</title>
+- </properties>
+-
+-<body>
+-
+-
+-<section name="Important Note">
+-<p><b>You can also check the <a
href="config/cluster.html">configuration reference
documentation.</a></b>
+-</p>
+-</section>
+-
+-<section name="For the impatient">
+- <p>
+- Simply add <source><Cluster
className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/></source>
+- to your <code><Engine></code> or your
<code><Host></code> element to enable clustering.
+- </p>
+- <p>
+- Using the above configuration will enable all-to-all session replication
+- using the <code>DeltaManager</code> to replicate session deltas. By
all-to-all we mean that the session gets replicated to all the other
+- nodes in the cluster. This works great for smaller cluster but we don't
recommend it for larger clusters(a lot of tomcat nodes).
+- Also when using the delta manager it will replicate to all nodes, even nodes that
don't have the application deployed.<br/>
+- To get around this problem, you'll want to use the BackupManager. This manager
only replicates the session data to one backup
+- node, and only to nodes that have the application deployed. Downside of the
BackupManager: not quite as battle tested as the delta manager.
+- <br/>
+- Here are some of the important default values:<br/>
+- 1. Multicast address is 228.0.0.4<br/>
+- 2. Multicast port is 45564 (the port and the address together determine cluster
membership.<br/>
+- 3. The IP broadcasted is
<code>java.net.InetAddress.getLocalHost().getHostAddress()</code> (make sure
you don't broadcast 127.0.0.1, this is a common error)<br/>
+- 4. The TCP port listening for replication messages is the first available server
socket in range <code>4000-4100</code><br/>
+- 5. Two listeners are configured <code>ClusterSessionListener</code> and
<code>JvmRouteSessionIDBinderListener</code><br/>
+- 6. Two interceptors are configured <code>TcpFailureDetector</code> and
<code>MessageDispatch15Interceptor</code><br/>
+- The following is the default cluster configuration:<br/>
+- <source>
+- <Cluster
className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
+- channelSendOptions="8">
+-
+- <Manager
className="org.apache.catalina.ha.session.DeltaManager"
+- expireSessionsOnShutdown="false"
+- notifyListenersOnReplication="true"/>
+-
+- <Channel
className="org.apache.catalina.tribes.group.GroupChannel">
+- <Membership
className="org.apache.catalina.tribes.membership.McastService"
+- address="228.0.0.4"
+- port="45564"
+- frequency="500"
+- dropTime="3000"/>
+- <Receiver
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
+- address="auto"
+- port="4000"
+- autoBind="100"
+- selectorTimeout="5000"
+- maxThreads="6"/>
+-
+- <Sender
className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
+- <Transport
className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
+- </Sender>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
+- </Channel>
+-
+- <Valve
className="org.apache.catalina.ha.tcp.ReplicationValve"
+- filter=""/>
+- <Valve
className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
+-
+- <Deployer
className="org.apache.catalina.ha.deploy.FarmWarDeployer"
+- tempDir="/tmp/war-temp/"
+- deployDir="/tmp/war-deploy/"
+- watchDir="/tmp/war-listen/"
+- watchEnabled="false"/>
+-
+- <ClusterListener
className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
+- <ClusterListener
className="org.apache.catalina.ha.session.ClusterSessionListener"/>
+- </Cluster>
+- </source>
+- </p>
+- <p>Will cover this section in more detail later in this document.</p>
+-</section>
+-
+-<section name="Cluster Basics">
+-
+-<p>To run session replication in your Tomcat 6.0 container, the following steps
+-should be completed:</p>
+-<ul>
+- <li>All your session attributes must implement
<code>java.io.Serializable</code></li>
+- <li>Uncomment the <code>Cluster</code> element in
server.xml</li>
+- <li>If you have defined custom cluster valves, make sure you have the
<code>ReplicationValve</code> defined as well under the Cluster element in
server.xml</li>
+- <li>If your Tomcat instances are running on the same machine, make sure the
<code>tcpListenPort</code>
+- attribute is unique for each instance, in most cases Tomcat is smart enough to
resolve this on it's own by autodetecting available ports in the range
4000-4100</li>
+- <li>Make sure your <code>web.xml</code> has the
<code><distributable/></code> element
+- or set at your <code><Context distributable="true"
/></code></li>
+- <li>If you are using mod_jk, make sure that jvmRoute attribute is set at your
Engine <code><Engine name="Catalina" jvmRoute="node01"
></code>
+- and that the jvmRoute attribute value matches your worker name in
workers.properties</li>
+- <li>Make sure that all nodes have the same time and sync with NTP
service!</li>
+- <li>Make sure that your loadbalancer is configured for sticky session
mode.</li>
+-</ul>
+-<p>Load balancing can be achieved through many techniques, as seen in the
+-<a href="balancer-howto.html">Load Balancing</a>
chapter.</p>
+-<p>Note: Remember that your session state is tracked by a cookie, so your URL must
look the same from the out
+- side otherwise, a new session will be created.</p>
+-<p>Note: Clustering support currently requires the JDK version 1.5 or
later.</p>
+-<p>The Cluster module uses the Tomcat JULI logging framework, so you can configure
logging
+- through the regular logging.properties file. To track messages, you can enable
logging on the key:<code>org.apache.catalina.tribes.MESSAGES</code></p>
+-</section>
+-
+-
+-<section name="Overview">
+-
+-<p>To enable session replication in Tomcat, three different paths can be followed
to achieve the exact same thing:</p>
+-<ol>
+- <li>Using session persistence, and saving the session to a shared file system
(PersistenceManager + FileStore)</li>
+- <li>Using session persistence, and saving the session to a shared database
(PersistenceManager + JDBCStore)</li>
+- <li>Using in-memory-replication, using the SimpleTcpCluster that ships with
Tomcat 6 (lib/catalina-tribes.jar + lib/catalina-ha.jar)</li>
+-</ol>
+-
+-<p>In this release of session replication, Tomcat can perform an all-to-all
replication of session state using the <code>DeltaManager</code> or
+- perform backup replication to only one node using the
<code>BackupManager</code>.
+- The all-to-all replication is an algorithm that is only efficient when the clusters
are small. For larger clusters, to use
+- a primary-secondary session replication where the session will only be stored at one
backup server simply setup the BackupManager. <br/>
+- Currently you can use the domain worker attribute (mod_jk > 1.2.8) to build
cluster partitions
+- with the potential of having a more scaleable cluster solution with the
DeltaManager(you'll need to configure the domain interceptor for this).
+- In order to keep the network traffic down in an all-to-all environment, you can split
your cluster
+- into smaller groups. This can be easily achieved by using different multicast
addresses for the different groups.
+- A very simple setup would look like this:
+- </p>
+-
+-<source>
+- DNS Round Robin
+- |
+- Load Balancer
+- / \
+- Cluster1 Cluster2
+- / \ / \
+- Tomcat1 Tomcat2 Tomcat3 Tomcat4
+-</source>
+-
+-<p>What is important to mention here, is that session replication is only the
beginning of clustering.
+- Another popular concept used to implement clusters is farming, ie, you deploy your
apps only to one
+- server, and the cluster will distribute the deployments across the entire cluster.
+- This is all capabilities that can go into with the FarmWarDeployer (s. cluster
example at <code>server.xml</code>)</p>
+-<p>In the next section will go deeper into how session replication works and how
to configure it.</p>
+-
+-</section>
+-
+-<section name="Cluster Information">
+-<p>Membership is established using multicast heartbeats.
+- Hence, if you wish to subdivide your clusters, you can do this by
+- changing the multicast IP address or port in the
<code><Membership></code> element.
+-</p>
+-<p>
+- The heartbeat contains the IP address of the Tomcat node and the TCP port that
+- Tomcat listens to for replication traffic. All data communication happens over TCP.
+-</p>
+-<p>
+- The <code>ReplicationValve</code> is used to find out when the request
has been completed and initiate the
+- replication, if any. Data is only replicated if the session has changed (by calling
setAttribute or removeAttribute
+- on the session).
+-</p>
+-<p>
+- One of the most important performance considerations is the synchronous versus
asynchronous replication.
+- In a synchronous replication mode the request doesn't return until the
replicated session has been
+- sent over the wire and reinstantiated on all the other cluster nodes.
+- Synchronous vs asynchronous is configured using the
<code>channelSendOptions</code>
+- flag and is an integer value. The default value for the
<code>SimpleTcpCluster/DeltaManager</code> combo is
+- 8, which is asynchronous. You can read more on the <a
href="tribes/introduction.html">send flag(overview)</a> or the
+- <a
href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalin...
flag(javadoc)</a>.
+- During async replication, the request is returned before the data has been
replicated. async replication yields shorter
+- request times, and synchronous replication guarantees the session to be replicated
before the request returns.
+-</p>
+-</section>
+-
+-<section name="Bind session after crash to failover node">
+-<p>
+- If you are using mod_jk and not using sticky sessions or for some reasons sticky
session don't
+- work, or you are simply failing over, the session id will need to be modified as it
previously contained
+- the worker id of the previous tomcat (as defined by jvmRoute in the Engine
element).
+- To solve this, we will use the JvmRouteBinderValve.
+-</p>
+-<p>
+- The JvmRouteBinderValve rewrites the session id to ensure that the next request will
remain sticky
+- (and not fall back to go to random nodes since the worker is no longer available)
after a fail over.
+- The valve rewrites the JSESSIONID value in the cookie with the same name.
+- Not having this valve in place, will make it harder to ensure stickyness in case of
a failure for the mod_jk module.
+-</p>
+-<p>
+- By default, if no valves are configured, the JvmRouteBinderValve is added on.
+- The cluster message listener called JvmRouteSessionIDBinderListener is also defined
by default and is used to actually rewrite the
+- session id on the other nodes in the cluster once a fail over has occurred.
+- Remember, if you are adding your own valves or cluster listeners in server.xml then
the defaults are no longer valid,
+- make sure that you add in all the appropriate valves and listeners as defined by the
default.
+-</p>
+-<p>
+- <b>Hint:</b><br/>
+- With attribute <i>sessionIdAttribute</i> you can change the request
attribute name that included the old session id.
+- Default attribuite name is
<i>org.apache.catalina.cluster.session.JvmRouteOrignalSessionID</i>.
+-</p>
+-<p>
+- <b>Trick:</b><br/>
+- You can enable this mod_jk turnover mode via JMX before you drop a node to all
backup nodes!
+- Set enable true on all JvmRouteBinderValve backups, disable worker at mod_jk
+- and then drop node and restart it! Then enable mod_jk Worker and disable
JvmRouteBinderValves again.
+- This use case means that only requested session are migrated.
+-</p>
+-
+-
+-
+-</section>
+-
+-<section name="Configuration Example">
+- <source>
+- <Cluster
className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
+- channelSendOptions="6">
+-
+- <Manager
className="org.apache.catalina.ha.session.BackupManager"
+- expireSessionsOnShutdown="false"
+- notifyListenersOnReplication="true"
+- mapSendOptions="6"/>
+- <!--
+- <Manager
className="org.apache.catalina.ha.session.DeltaManager"
+- expireSessionsOnShutdown="false"
+- notifyListenersOnReplication="true"/>
+- -->
+- <Channel
className="org.apache.catalina.tribes.group.GroupChannel">
+- <Membership
className="org.apache.catalina.tribes.membership.McastService"
+- address="228.0.0.4"
+- port="45564"
+- frequency="500"
+- dropTime="3000"/>
+- <Receiver
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
+- address="auto"
+- port="5000"
+- selectorTimeout="100"
+- maxThreads="6"/>
+-
+- <Sender
className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
+- <Transport
className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
+- </Sender>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
+- </Channel>
+-
+- <Valve
className="org.apache.catalina.ha.tcp.ReplicationValve"
+-
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
+-
+- <Deployer
className="org.apache.catalina.ha.deploy.FarmWarDeployer"
+- tempDir="/tmp/war-temp/"
+- deployDir="/tmp/war-deploy/"
+- watchDir="/tmp/war-listen/"
+- watchEnabled="false"/>
+-
+- <ClusterListener
className="org.apache.catalina.ha.session.ClusterSessionListener"/>
+- </Cluster>
+- </source>
+- <p>
+- Break it down!!
+- </p>
+- <source>
+- <Cluster
className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
+- channelSendOptions="6">
+- </source>
+- <p>
+- The main element, inside this element all cluster details can be configured.
+- The <code>channelSendOptions</code> is the flag that is attached to
each message sent by the
+- SimpleTcpCluster class or any objects that are invoking the SimpleTcpCluster.send
method.
+- The description of the send flags is available at <a
href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalin...
+- our javadoc site</a>
+- The <code>DeltaManager</code> sends information using the
SimpleTcpCluster.send method, while the backup manager
+- sends it itself directly through the channel.
+- <br/>For more info, Please visit the <a
href="config/cluster.html">reference documentation</a>
+- </p>
+- <source>
+- <Manager
className="org.apache.catalina.ha.session.BackupManager"
+- expireSessionsOnShutdown="false"
+- notifyListenersOnReplication="true"
+- mapSendOptions="6"/>
+- <!--
+- <Manager
className="org.apache.catalina.ha.session.DeltaManager"
+- expireSessionsOnShutdown="false"
+- notifyListenersOnReplication="true"/>
+- -->
+- </source>
+- <p>
+- This is a template for the manager configuration that will be used if no manager
is defined in the <Context>
+- element. In Tomcat 5.x each webapp marked distributable had to use the same
manager, this is no longer the case
+- since Tomcat 6 you can define a manager class for each webapp, so that you can
mix managers in your cluster.
+- Obviously the managers on one node's application has to correspond with the
same manager on the same application on the other node.
+- If no manager has been specified for the webapp, and the webapp is marked
<distributable/> Tomcat will take this manager configuration
+- and create a manager instance cloning this configuration.
+- <br/>For more info, Please visit the <a
href="config/cluster-manager.html">reference documentation</a>
+- </p>
+- <source>
+- <Channel
className="org.apache.catalina.tribes.group.GroupChannel">
+- </source>
+- <p>
+- The channel element is <a
href="tribes/introduction.html">Tribes</a>, the group communication
framework
+- used inside Tomcat. This element encapsulates everything that has to do with
communication and membership logic.
+- <br/>For more info, Please visit the <a
href="config/cluster-channel.html">reference documentation</a>
+- </p>
+- <source>
+- <Membership
className="org.apache.catalina.tribes.membership.McastService"
+- address="228.0.0.4"
+- port="45564"
+- frequency="500"
+- dropTime="3000"/>
+- </source>
+- <p>
+- Membership is done using multicasting. Please note that Tribes also supports
static memberships using the
+- <code>StaticMembershipInterceptor</code> if you want to extend your
membership to points beyond multicasting.
+- The address attribute is the multicast address used and the port is the
multicast port. These two together
+- create the cluster separation. If you want a QA cluster and a production
cluster, the easiest config is to
+- have the QA cluster be on a separate multicast address/port combination the the
production cluster.<br/>
+- The membership component broadcasts TCP adress/port of itselt to the other nodes
so that communication between
+- nodes can be done over TCP. Please note that the address being broadcasted is
the one of the
+- <code>Receiver.address</code> attribute.
+- <br/>For more info, Please visit the <a
href="config/cluster-membership.html">reference documentation</a>
+- </p>
+- <source>
+- <Receiver
className="org.apache.catalina.tribes.transport.nio.NioReceiver"
+- address="auto"
+- port="5000"
+- selectorTimeout="100"
+- maxThreads="6"/>
+- </source>
+- <p>
+- In tribes the logic of sending and receiving data has been broken into two
functional components. The Receiver, as the name suggests
+- is responsible for receiving messages. Since the Tribes stack is thread less, (a
popular improvement now adopted by other frameworks as well),
+- there is a thread pool in this component that has a maxThreads and minThreads
setting.<br/>
+- The address attribute is the host address that will be broadcasted by the
membership component to the other nodes.
+- <br/>For more info, Please visit the <a
href="config/cluster-receiver.html">reference documentation</a>
+- </p>
+- <source>
+-
+- <Sender
className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
+- <Transport
className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
+- </Sender>
+- </source>
+- <p>
+- The sender component, as the name indicates is responsible for sending messages
to other nodes.
+- The sender has a shell component, the
<code>ReplicationTransmitter</code> but the real stuff done is done in the
+- sub component, <code>Transport</code>.
+- Tribes support having a pool of senders, so that messages can be sent in
parallel and if using the NIO sender,
+- you can send messages concurrently as well.<br/>
+- Concurrently means one message to multiple senders at the same time and Parallel
means multiple messages to multiple senders
+- at the same time.
+- <br/>For more info, Please visit the <a
href="config/cluster-sender.html">reference documentation</a>
+- </p>
+- <source>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
+- <Interceptor
className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
+- </Channel>
+- </source>
+- <p>
+- Tribes uses a stack to send messages through. Each element in the stack is
called an interceptor, and works much like the valves do
+- in the Tomcat servlet container.
+- Using interceptors, logic can be broken into more managable pieces of code. The
interceptors configured above are:<br/>
+- TcpFailureDetector - verifies crashed members through TCP, if multicast packets
get dropped, this interceptor protects against false positives,
+- ie the node marked as crashed even though it still is alive and
running.<br/>
+- MessageDispatch15Interceptor - dispatches messages to a thread (thread pool) to
send message asynchrously.<br/>
+- ThroughputInterceptor - prints out simple stats on message traffic.<br/>
+- Please note that the order of interceptors is important. the way they are
defined in server.xml is the way they are represented in the
+- channel stack. Think of it as a linked list, with the head being the first most
interceptor and the tail the last.
+- <br/>For more info, Please visit the <a
href="config/cluster-interceptor.html">reference documentation</a>
+- </p>
+- <source>
+- <Valve
className="org.apache.catalina.ha.tcp.ReplicationValve"
+-
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
+- </source>
+- <p>
+- The cluster uses valves to track requests to web applications, we've
mentioned the ReplicationValve and the JvmRouteBinderValve above.
+- The <Cluster> element itself is not part of the pipeline in
Tomcat, instead the cluster adds the valve to its parent container.
+- If the <Cluster> elements is configured in the
<Engine> element, the valves get added to the engine and so on.
+- <br/>For more info, Please visit the <a
href="config/cluster-valve.html">reference documentation</a>
+- </p>
+- <source>
+- <Deployer
className="org.apache.catalina.ha.deploy.FarmWarDeployer"
+- tempDir="/tmp/war-temp/"
+- deployDir="/tmp/war-deploy/"
+- watchDir="/tmp/war-listen/"
+- watchEnabled="false"/>
+- </source>
+- <p>
+- The default tomcat cluster supports farmed deployment, ie, the cluster can
deploy and undeploy applications on the other nodes.
+- The state of this component is currently in flux but will be addressed soon.
There was a change in the deployment algorithm
+- between Tomcat 5.0 and 5.5 and at that point, the logic of this component
changed to where the deploy dir has to match the
+- webapps directory.
+- <br/>For more info, Please visit the <a
href="config/cluster-deployer.html">reference documentation</a>
+- </p>
+- <source>
+- <ClusterListener
className="org.apache.catalina.ha.session.ClusterSessionListener"/>
+- </Cluster>
+- </source>
+- <p>
+- Since the SimpleTcpCluster itself is a sender and receiver of the Channel
object, components can register themselves as listeners to
+- the SimpleTcpCluster. The listener above
<code>ClusterSessionListener</code> listens for DeltaManager replication
messages
+- and applies the deltas to the manager that in turn applies it to the session.
+- <br/>For more info, Please visit the <a
href="config/cluster-listener.html">reference documentation</a>
+- </p>
+-
+-</section>
+-
+-<section name="Cluster Architecture">
+-
+-<p><b>Component Levels:</b>
+-<source>
+- Server
+- |
+- Service
+- |
+- Engine
+- | \
+- | --- Cluster --*
+- |
+- Host
+- |
+- ------
+- / \
+- Cluster Context(1-N)
+- | \
+- | -- Manager
+- | \
+- | -- DeltaManager
+- | -- BackupManager
+- |
+- ---------------------------
+- | \
+- Channel \
+- ----------------------------- \
+- | \
+- Interceptor_1 .. \
+- | \
+- Interceptor_N \
+- ----------------------------- \
+- | | | \
+- Receiver Sender Membership \
+- -- Valve
+- | \
+- | -- ReplicationValve
+- | -- JvmRouteBinderValve
+- |
+- -- LifecycleListener
+- |
+- -- ClusterListener
+- | \
+- | -- ClusterSessionListener
+- | -- JvmRouteSessionIDBinderListener
+- |
+- -- Deployer
+- \
+- -- FarmWarDeployer
+-
+-
+-</source>
+-</p>
+-
+-</section>
+-<section name="How it Works">
+-<p>To make it easy to understand how clustering works, We are gonna take you
through a series of scenarios.
+- In the scenario we only plan to use two tomcat instances
<code>TomcatA</code> and <code>TomcatB</code>.
+- We will cover the following sequence of events:</p>
+-
+-<ol>
+-<li><code>TomcatA</code> starts up</li>
+-<li><code>TomcatB</code> starts up (Wait that TomcatA start is
complete)</li>
+-<li><code>TomcatA</code> receives a request, a session
<code>S1</code> is created.</li>
+-<li><code>TomcatA</code> crashes</li>
+-<li><code>TomcatB</code> receives a request for session
<code>S1</code></li>
+-<li><code>TomcatA</code> starts up</li>
+-<li><code>TomcatA</code> receives a request, invalidate is called on
the session (<code>S1</code>)</li>
+-<li><code>TomcatB</code> receives a request, for a new session
(<code>S2</code>)</li>
+-<li><code>TomcatA</code> The session <code>S2</code>
expires due to inactivity.</li>
+-</ol>
+-
+-<p>Ok, now that we have a good sequence, we will take you through exactly what
happens in the session repliction code</p>
+-
+-<ol>
+-<li><b><code>TomcatA</code> starts up</b>
+- <p>
+- Tomcat starts up using the standard start up sequence. When the Host object is
created, a cluster object is associated with it.
+- When the contexts are parsed, if the distributable element is in place in
web.xml
+- Tomcat asks the Cluster class (in this case
<code>SimpleTcpCluster</code>) to create a manager
+- for the replicated context. So with clustering enabled, distributable set in
web.xml
+- Tomcat will create a <code>DeltaManager</code> for that context
instead of a <code>StandardManager</code>.
+- The cluster class will start up a membership service (multicast) and a
replication service (tcp unicast).
+- More on the architecture further down in this document.
+- </p><p></p>
+-</li>
+-<li><b><code>TomcatB</code> starts up</b>
+- <p>
+- When TomcatB starts up, it follows the same sequence as TomcatA did with one
exception.
+- The cluster is started and will establish a membership (TomcatA,TomcatB).
+- TomcatB will now request the session state from a server that already exists in
the cluster,
+- in this case TomcatA. TomcatA responds to the request, and before TomcatB starts
listening
+- for HTTP requests, the state has been transferred from TomcatA to TomcatB.
+- In case TomcatA doesn't respond, TomcatB will time out after 60 seconds, and
issue a log
+- entry. The session state gets transferred for each web application that has
distributable in
+- its web.xml. Note: To use session replication efficiently, all your tomcat
instances should be
+- configured the same.
+- </p><p></p>
+-</li>
+-<li><B><code>TomcatA</code> receives a request, a session
<code>S1</code> is created.</B>
+- <p>
+- The request coming in to TomcatA is treated exactly the same way as without
session replication.
+- The action happens when the request is completed, the
<code>ReplicationValve</code> will intercept
+- the request before the response is returned to the user.
+- At this point it finds that the session has been modified, and it uses TCP to
replicata the
+- session to TomcatB. Once the serialized data has been handed off to the
operating systems TCP logic,
+- the request returns to the user, back through the valve pipeline.
+- For each request the entire session is replicated, this allows code that
modifies attributes
+- in the session without calling setAttribute or removeAttribute to be
replicated.
+- a useDirtyFlag configuration parameter can be used to optimize the number of
times
+- a session is replicated.
+- </p><p></p>
+-
+-</li>
+-<li><b><code>TomcatA</code> crashes</b>
+- <p>
+- When TomcatA crashes, TomcatB receives a notification that TomcatA has dropped
out
+- of the cluster. TomcatB removes TomcatA from its membership list, and TomcatA
will no longer
+- be notified of any changes that occurs in TomcatB.
+- The load balancer will redirect the requests from TomcatA to TomcatB and all the
sessions
+- are current.
+- </p><p></p>
+-</li>
+-<li><b><code>TomcatB</code> receives a request for session
<code>S1</code></b>
+- <p>Nothing exciting, TomcatB will process the request as any other request.
+- </p><p></p>
+-</li>
+-<li><b><code>TomcatA</code> starts up</b>
+- <p>Upon start up, before TomcatA starts taking new request and making itself
+- available to it will follow the start up sequence described above 1) 2).
+- It will join the cluster, contact TomcatB for the current state of all the
sessions.
+- And once it receives the session state, it finishes loading and opens its
HTTP/mod_jk ports.
+- So no requests will make it to TomcatA until it has received the session state from
TomcatB.
+- </p><p></p>
+-</li>
+-<li><b><code>TomcatA</code> receives a request, invalidate is
called on the session (<code>S1</code>)</b>
+- <p>The invalidate is call is intercepted, and the session is queued with
invalidated sessions.
+- When the request is complete, instead of sending out the session that has
changed, it sends out
+- an "expire" message to TomcatB and TomcatB will invalidate the session
as well.
+- </p><p></p>
+-
+-</li>
+-<li><b><code>TomcatB</code> receives a request, for a new
session (<code>S2</code>)</b>
+- <p>Same scenario as in step 3)
+- </p><p></p>
+-
+-
+-</li>
+-<li><code>TomcatA</code> The session <code>S2</code>
expires due to inactivity.
+- <p>The invalidate is call is intercepted the same was as when a session is
invalidated by the user,
+- and the session is queued with invalidated sessions.
+- At this point, the invalidet session will not be replicated across until
+- another request comes through the system and checks the invalid queue.
+- </p><p></p>
+-</li>
+-</ol>
+-
+-<p>Phuuuhh! :)</p>
+-
+-<p><b>Membership</b>
+- Clustering membership is established using very simple multicast pings.
+- Each Tomcat instance will periodically send out a multicast ping,
+- in the ping message the instance will broad cast its IP and TCP listen port
+- for replication.
+- If an instance has not received such a ping within a given timeframe, the
+- member is considered dead. Very simple, and very effective!
+- Of course, you need to enable multicasting on your system.
+-</p>
+-
+-<p><b>TCP Replication</b>
+- Once a multicast ping has been received, the member is added to the cluster
+- Upon the next replication request, the sending instance will use the host and
+- port info and establish a TCP socket. Using this socket it sends over the serialized
data.
+- The reason I choose TCP sockets is because it has built in flow control and
guaranteed delivery.
+- So I know, when I send some data, it will make it there :)
+-</p>
+-
+-<p><b>Distributed locking and pages using frames</b>
+- Tomcat does not keep session instances in sync across the cluster.
+- The implementation of such logic would be to much overhead and cause all
+- kinds of problems. If your client accesses the same session
+- simultanously using multiple requests, then the last request
+- will override the other sessions in the cluster.
+-</p>
+-
+-</section>
+-
+-
+-
+-
+-
+-
+-<section name="Monitoring your Cluster with JMX">
+-<p>Monitoring is a very important question when you use a cluster. Some of the
cluster objects are JMX MBeans </p>
+-<p>Add the following parameter to your startup script with Java 5:
+-<source>
+-set CATALINA_OPTS=\
+--Dcom.sun.management.jmxremote \
+--Dcom.sun.management.jmxremote.port=%my.jmx.port% \
+--Dcom.sun.management.jmxremote.ssl=false \
+--Dcom.sun.management.jmxremote.authenticate=false
+-</source>
+-</p>
+-<p>Activate JMX with JDK 1.4:
+-<ol>
+-<li>Install the compat package</li>
+-<li>Install the mx4j-tools.jar at common/lib (use the same mx4j version as your
tomcat release)</li>
+-<li>Configure a MX4J JMX HTTP Adaptor at your AJP Connector<p></p>
+-<source>
+-<Connector port="${AJP.PORT}"
+- handler.list="mx"
+- mx.enabled="true"
+- mx.httpHost="${JMX.HOST}"
+- mx.httpPort="${JMX.PORT}"
+- protocol="AJP/1.3" />
+-</source>
+-</li>
+-<li>Start your tomcat and look with your browser to
http://${JMX.HOST}:${JMX.PORT}</li>
+-<li>With the connector parameter <code>mx.authMode="basic"
mx.authUser="tomcat" mx.authPassword="strange"</code> you can
control the access!</li>
+-</ol>
+-</p>
+-<p>
+-List of Cluster Mbeans<br/>
+-<table border="1" cellpadding="5">
+-
+- <tr>
+- <th align="center" bgcolor="aqua">Name</th>
+- <th align="center" bgcolor="aqua">Description</th>
+- <th align="center" bgcolor="aqua">MBean ObjectName -
Engine</th>
+- <th align="center" bgcolor="aqua">MBean ObjectName -
Host</th>
+- </tr>
+-
+- <tr>
+- <td>Cluster</td>
+- <td>The complete cluster element</td>
+- <td><code>type=Cluster</code></td>
+- <td><code>type=Cluster,host=${HOST}</code></td>
+- </tr>
+-
+- <tr>
+- <td>DeltaManager</td>
+- <td>This manager control the sessions and handle session replication
</td>
+- <td><code>type=Manager,path=${APP.CONTEXT.PATH},
host=${HOST}</code></td>
+- <td><code>type=Manager,path=${APP.CONTEXT.PATH},
host=${HOST}</code></td>
+- </tr>
+-
+- <tr>
+- <td>ReplicationValve</td>
+- <td>This valve control the replication to the backup nodes</td>
+- <td><code>type=Valve,name=ReplicationValve</code></td>
+-
<td><code>type=Valve,name=ReplicationValve,host=${HOST}</code></td>
+- </tr>
+-
+- <tr>
+- <td>JvmRouteBinderValve</td>
+- <td>This is a cluster fallback valve to change the Session ID to the current
tomcat jvmroute.</td>
+- <td><code>type=Valve,name=JvmRouteBinderValve,
+- path=${APP.CONTEXT.PATH}</code></td>
+- <td><code>type=Valve,name=JvmRouteBinderValve,host=${HOST},
+- path=${APP.CONTEXT.PATH}</code></td>
+- </tr>
+-
+-</table>
+-</p>
+-</section>
+-
+-<section name="FAQ">
+-<p>Please see <a
href="http://tomcat.apache.org/faq/cluster.html">the clustering section of
the FAQ</a>.</p>
+-</section>
+-
+-</body>
+-
+-</document>
+Index: build.sh
+===================================================================
+--- build.sh (revision 0)
++++ build.sh (revision 0)
+@@ -0,0 +1,12 @@
++#
++# Shell script to build Tomcat6
++#
++# build ouput/build.
++ant download
++ant
++# build output/dist
++ant -f dist.xml
++ant -f dist.xml dist-javadoc
++# build extras and release
++ant -f extras.xml
++ant -f dist.xml release
+Index: BUILDING.txt
+===================================================================
+--- BUILDING.txt (revision 590752)
++++ BUILDING.txt (working copy)
+@@ -91,6 +91,8 @@
+ * NOTE: Users accessing the Internet through a proxy must use a properties
+ file to indicate to Ant the proxy configuration. Read below.
+
++* NOTE: This builds output/build.
++
+ * WARNING: Running this command will download binaries to the /usr/share/java
+ directory. Make sure this is appropriate to do on your computer. On Windows,
+ this usually corresponds to the "C:\usr\share\java" directory, unless Cygwin
+@@ -128,11 +130,15 @@
+ The documentation can be easly built:
+
+ cd ${tomcat.source}
++ ant -f dist.xml
+ ant -f dist.xml dist-javadoc
+
++* NOTE: This builds output/dist.
++
+ (6) Building a release running tests:
+
+ cd ${tomcat.source}
++ ant -f extras.xml
+ ant -f dist.xml release
+
+-
++* NOTE: This builds output/extras and output/release.
+Index: build.properties.default
+===================================================================
+--- build.properties.default (revision 590752)
++++ build.properties.default (working copy)
+@@ -35,7 +35,7 @@
+ # Please note this path must be absolute, not relative,
+ # as it is referenced with different working directory
+ # contexts by the various build scripts.
+-base.path=/usr/share/java
++base.path=${basedir}/tcjava
+ #base.path=C:/path/to/the/repository
+ #base.path=/usr/local
+