This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
/*
* 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.Manager;
import org.apache.catalina.Valve;
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.Member;
/**
* A <b>CatalinaCluster</b> interface allows to plug in and out the
* different cluster implementations
*/
public interface CatalinaCluster extends Cluster {
// ----------------------------------------------------- Instance Variables
/**
* 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);
/**
* @return <code>true</code> if the cluster has members.
*/
public boolean hasMembers();
/**
* @return an array containing all the members currently participating in the cluster.
*/
public Member[] getMembers();
/**
* @return the member that represents this node.
*/
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<String,ClusterManager> getManagers();
/**
* Get Manager
* @param name The manager name
* @return The manager
*/
public Manager getManager(String name);
/**
* Get a new cluster name for a manager.
* @param name Override name (optional)
* @param manager The manager
* @return the manager name in the cluster
*/
public String getManagerName(String name, Manager manager);
public Valve[] getValves();
public void setChannel(Channel channel);
public Channel getChannel();
}

View File

@@ -0,0 +1,105 @@
/*
* 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.File;
import java.io.IOException;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.tribes.ChannelListener;
/**
* A <b>ClusterDeployer</b> interface allows to plug in and out the
* different deployment implementations
*/
public interface ClusterDeployer extends ChannelListener {
/**
* 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 Error stopping cluster deployer
*/
public void stop() throws LifecycleException;
/**
* 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 name.
* <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 contextName The context name to which this application should
* be installed (must be unique)
* @param webapp A WAR file or unpacked directory structure containing
* the web application to be installed
*
* @exception IllegalArgumentException if the specified context name
* is malformed
* @exception IllegalStateException if the specified context name
* is already attached to an existing web application
* @exception IOException if an input/output error was encountered
* during installation
*/
public void install(String contextName, File webapp) throws IOException;
/**
* Remove an existing web application, attached to the specified context
* name. 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 contextName The context name of the application to be removed
* @param undeploy boolean flag to remove web application from server
*
* @exception IllegalArgumentException if the specified context name
* is malformed
* @exception IllegalArgumentException if the specified context name does
* not identify a currently installed web application
* @exception IOException if an input/output error occurs during
* removal
*/
public void remove(String contextName, 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);
}

View File

@@ -0,0 +1,105 @@
/*
* 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.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* Receive SessionID cluster change from other backup node after primary session
* node is failed.
*
* @author Peter Rossbach
*/
public abstract class ClusterListener implements ChannelListener {
private static final Log log = LogFactory.getLog(ClusterListener.class);
//--Instance Variables--------------------------------------
/**
* The string manager for this package.
*/
protected CatalinaCluster cluster = null;
//--Constructor---------------------------------------------
public ClusterListener() {
// NO-OP
}
//--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;
}
//--Logic---------------------------------------------------
@Override
public final void messageReceived(Serializable msg, Member member) {
if ( msg instanceof ClusterMessage ) messageReceived((ClusterMessage)msg);
}
@Override
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) ;
}

View File

@@ -0,0 +1,97 @@
/*
* 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.IOException;
import org.apache.catalina.Manager;
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 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 The manager name
* @since 5.5.10
*/
public void setName(String name);
public CatalinaCluster getCluster();
public void setCluster(CatalinaCluster cluster);
/**
* Open stream and use correct ClassLoader (Container), switching thread
* context class loader.
*
* @param data The data
* @return The object input stream
* @throws IOException An error occurred
*/
public ReplicationStream getReplicationStream(byte[] data) throws IOException;
public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException;
public boolean isNotifyListenersOnReplication();
public ClusterManager cloneFromTemplate();
}

View File

@@ -0,0 +1,29 @@
/*
* 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;
public interface ClusterMessage extends Serializable {
public Member getAddress();
public void setAddress(Member member);
public String getUniqueId();
public long getTimestamp();
public void setTimestamp(long timestamp);
}

View File

@@ -0,0 +1,51 @@
/*
* 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;
public abstract class ClusterMessageBase implements ClusterMessage {
private static final long serialVersionUID = 1L;
private long timestamp;
protected transient Member address;
public ClusterMessageBase() {
// NO-OP
}
@Override
public Member getAddress() {
return address;
}
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void setAddress(Member member) {
this.address = member;
}
@Override
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -0,0 +1,221 @@
/*
* 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 Peter Rossbach
*/
@SuppressWarnings("deprecation")
public class ClusterRuleSet extends RuleSetBase {
// ----------------------------------------------------- Instance Variables
/**
* The matching pattern prefix to use for recognizing our elements.
*/
protected final String prefix;
// ------------------------------------------------------------ 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.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.
*/
@Override
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 + "Manager/SessionIdGenerator",
"org.apache.catalina.util.StandardSessionIdGenerator",
"className");
digester.addSetProperties(prefix + "Manager/SessionIdGenerator");
digester.addSetNext(prefix + "Manager/SessionIdGenerator",
"setSessionIdGenerator",
"org.apache.catalina.SessionIdGenerator");
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 + "MembershipListener",
null, // MUST be specified in the element
"className");
digester.addSetProperties(channelPrefix + "MembershipListener");
digester.addSetNext(channelPrefix + "MembershipListener",
"addMembershipListener",
"org.apache.catalina.tribes.MembershipListener");
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/LocalMember",
null, // MUST be specified in the element
"className");
digester.addSetProperties(channelPrefix + "Interceptor/LocalMember");
digester.addSetNext(channelPrefix + "Interceptor/LocalMember",
"setLocalMember",
"org.apache.catalina.tribes.Member");
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(channelPrefix + "ChannelListener",
null, // MUST be specified in the element
"className");
digester.addSetProperties(channelPrefix + "ChannelListener");
digester.addSetNext(channelPrefix + "ChannelListener",
"addChannelListener",
"org.apache.catalina.tribes.ChannelListener");
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
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 javax.servlet.http.HttpSession;
import org.apache.catalina.Session;
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);
}

View File

@@ -0,0 +1,38 @@
/*
* 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 Peter Rossbach
*/
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);
}

View File

@@ -0,0 +1,201 @@
/*
* 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.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.SessionListener;
import org.apache.catalina.authenticator.SingleSignOn;
import org.apache.catalina.authenticator.SingleSignOnEntry;
import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterValve;
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
import org.apache.catalina.tribes.tipis.ReplicatedMap;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
/**
* 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 propagated 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 implements ClusterValve, MapOwner {
private static final StringManager sm = StringManager.getManager(ClusterSingleSignOn.class);
// -------------------------------------------------------------- Properties
private CatalinaCluster cluster = null;
@Override
public CatalinaCluster getCluster() { return cluster; }
@Override
public void setCluster(CatalinaCluster cluster) {
this.cluster = cluster;
}
private long rpcTimeout = 15000;
public long getRpcTimeout() {
return rpcTimeout;
}
public void setRpcTimeout(long rpcTimeout) {
this.rpcTimeout = rpcTimeout;
}
private int mapSendOptions =
Channel.SEND_OPTIONS_SYNCHRONIZED_ACK | Channel.SEND_OPTIONS_USE_ACK;
public int getMapSendOptions() {
return mapSendOptions;
}
public void setMapSendOptions(int mapSendOptions) {
this.mapSendOptions = mapSendOptions;
}
private boolean terminateOnStartFailure = false;
public boolean getTerminateOnStartFailure() {
return terminateOnStartFailure;
}
public void setTerminateOnStartFailure(boolean terminateOnStartFailure) {
this.terminateOnStartFailure = terminateOnStartFailure;
}
private long accessTimeout = 5000;
public long getAccessTimeout() {
return accessTimeout;
}
public void setAccessTimeout(long accessTimeout) {
this.accessTimeout = accessTimeout;
}
// ---------------------------------------------------- SingleSignOn Methods
@Override
protected boolean associate(String ssoId, Session session) {
boolean result = super.associate(ssoId, session);
if (result) {
((ReplicatedMap<String,SingleSignOnEntry>) cache).replicate(ssoId, true);
}
return result;
}
@Override
protected boolean update(String ssoId, Principal principal, String authType,
String username, String password) {
boolean result = super.update(ssoId, principal, authType, username, password);
if (result) {
((ReplicatedMap<String,SingleSignOnEntry>) cache).replicate(ssoId, true);
}
return result;
}
@Override
protected SessionListener getSessionListener(String ssoId) {
return new ClusterSingleSignOnListener(ssoId);
}
// -------------------------------------------------------- MapOwner Methods
@Override
public void objectMadePrimary(Object key, Object value) {
// NO-OP
}
// ------------------------------------------------------- Lifecycle Methods
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Load the cluster component, if any
try {
if(cluster == null) {
Container host = getContainer();
if(host instanceof Host) {
if(host.getCluster() instanceof CatalinaCluster) {
setCluster((CatalinaCluster) host.getCluster());
}
}
}
if (cluster == null) {
throw new LifecycleException(sm.getString("clusterSingleSignOn.nocluster"));
}
ClassLoader[] cls = new ClassLoader[] { this.getClass().getClassLoader() };
ReplicatedMap<String,SingleSignOnEntry> cache = new ReplicatedMap<>(
this, cluster.getChannel(), rpcTimeout, cluster.getClusterName() + "-SSO-cache",
cls, terminateOnStartFailure);
cache.setChannelSendOptions(mapSendOptions);
cache.setAccessTimeout(accessTimeout);
this.cache = cache;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
throw new LifecycleException(sm.getString("clusterSingleSignOn.clusterLoad.fail"), t);
}
super.startInternal();
}
/**
* Stop this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void stopInternal() throws LifecycleException {
super.stopInternal();
if (getCluster() != null) {
((ReplicatedMap<?,?>) cache).breakdown();
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 org.apache.catalina.authenticator.SingleSignOnListener;
import org.apache.catalina.ha.session.ReplicatedSessionListener;
/**
* Cluster extension of {@link SingleSignOnListener} that simply adds the marker
* interface {@link ReplicatedSessionListener} which allows the listener to be
* replicated across the cluster along with the session.
*/
public class ClusterSingleSignOnListener extends SingleSignOnListener implements
ReplicatedSessionListener {
private static final long serialVersionUID = 1L;
public ClusterSingleSignOnListener(String ssoId) {
super(ssoId);
}
}

View File

@@ -0,0 +1,17 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=ClusterSingleSignOn exception during clusterLoad
clusterSingleSignOn.nocluster=There is no Cluster for ClusterSingleSignOn

View File

@@ -0,0 +1,16 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=ClusterSingleSignOn Fehler bei clusterLoad

View File

@@ -0,0 +1,16 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=ClusterSingleSignOn excepción durante clusterLoad

View File

@@ -0,0 +1,17 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=Exception ClusterSingleSignOn pendant clusterLoad
clusterSingleSignOn.nocluster=Il n'y a pas de cluster pour ClusterSingleSignOn

View File

@@ -0,0 +1,17 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=ClusterSingleSignOnでクラスタロード中に例外が発生しました
clusterSingleSignOn.nocluster=ClusterSingleSignOnに関連するクラスタがありません。

View File

@@ -0,0 +1,17 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=ClusterSingleSignOn에서 clusterLoad 오퍼레이션 수행 중 예외 발생
clusterSingleSignOn.nocluster=ClusterSingleSignOn을 위한 클러스터가 없습니다.

View File

@@ -0,0 +1,16 @@
# 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.
clusterSingleSignOn.clusterLoad.fail=在集群加载时, 集群单点登录异常

View File

@@ -0,0 +1,61 @@
<?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.
-->
<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.ha.authenticator.ClusterSingleSignOn">
<attribute
name="asyncSupported"
description="Does this valve support async reporting?"
is="true"
type="boolean"/>
<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="(Optional) Domain to be used by sso cookies"
type="java.lang.String"/>
<attribute
name="mapSendOptions"
description="mapSendOptions"
type="int"
writeable="false"/>
<attribute
name="rpcTimeout"
description="Timeout for RPC messages, how long we will wait for a reply"
type="long"/>
<attribute
name="terminateOnStartFailure"
description="Flag for whether to terminate this map that failed to start."
type="boolean"/>
<attribute
name="accessTimeout"
description="The timeout for a ping message in replication map."
type="long"/>
</mbean>
</mbeans-descriptors>

View File

@@ -0,0 +1,103 @@
/*
* 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.backend;
/* for MBean to read ready and busy */
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import org.apache.tomcat.util.modeler.Registry;
/*
* Listener to provider informations to mod_heartbeat.c
* *msg_format = "v=%u&ready=%u&busy=%u"; (message to send).
* send the multicast message using the format...
* what about the bind(IP. port) only IP makes sense (for the moment).
* BTW:v = version :-)
*/
public class CollectedInfo {
/* Collect info via JMX */
protected MBeanServer mBeanServer = null;
protected ObjectName objName = null;
int ready;
int busy;
int port = 0;
String host = null;
public CollectedInfo(String host, int port) throws Exception {
init(host, port);
}
public void init(String host, int port) throws Exception {
int iport = 0;
String shost = null;
mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
String onStr = "*:type=ThreadPool,*";
ObjectName objectName = new ObjectName(onStr);
Set<ObjectInstance> set = mBeanServer.queryMBeans(objectName, null);
for (ObjectInstance oi : set) {
objName = oi.getObjectName();
String name = objName.getKeyProperty("name");
/* Name are:
* http-8080
* jk-10.33.144.3-8009
* jk-jfcpc%2F10.33.144.3-8009
*/
String [] elenames = name.split("-");
String sport = elenames[elenames.length-1];
iport = Integer.parseInt(sport);
String [] shosts = elenames[1].split("%2F");
shost = shosts[0];
if (port==0 && host==null)
break; /* Take the first one */
if (host==null && iport==port)
break; /* Only port done */
if (shost.compareTo(host) == 0)
break; /* Done port and host are the expected ones */
}
if (objName == null)
throw new Exception("Can't find connector for " + host + ":" + port);
this.port = iport;
this.host = shost;
}
public void refresh() throws Exception {
if (mBeanServer == null || objName == null) {
throw new Exception("Not initialized!!!");
}
Integer imax = (Integer) mBeanServer.getAttribute(objName, "maxThreads");
// the currentThreadCount could be 0 before the threads are created...
// Integer iready = (Integer) mBeanServer.getAttribute(objName, "currentThreadCount");
Integer ibusy = (Integer) mBeanServer.getAttribute(objName, "currentThreadsBusy");
busy = ibusy.intValue();
ready = imax.intValue() - ibusy.intValue();
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.backend;
import org.apache.catalina.ContainerEvent;
import org.apache.catalina.ContainerListener;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/*
* Listener to provider informations to mod_heartbeat.c
* *msg_format = "v=%u&ready=%u&busy=%u"; (message to send).
* send the multicast message using the format...
* what about the bind(IP. port) only IP makes sense (for the moment).
* BTW:v = version :-)
*/
public class HeartbeatListener implements LifecycleListener, ContainerListener {
private static final Log log = LogFactory.getLog(HeartbeatListener.class);
/* To allow to select the connector */
private int port = 0;
private String host = null;
/* for multicasting stuff */
private final String ip = "224.0.1.105"; /* Multicast IP */
private final int multiport = 23364; /* Multicast Port */
private final int ttl = 16;
public String getHost() { return host; }
public String getGroup() { return ip; }
public int getMultiport() { return multiport; }
public int getTtl() { return ttl; }
/**
* Proxy list, format "address:port,address:port".
*/
private final String proxyList = null;
public String getProxyList() { return proxyList; }
/**
* URL prefix.
*/
private final String proxyURL = "/HeartbeatListener";
public String getProxyURL() { return proxyURL; }
private CollectedInfo coll = null;
private Sender sender = null;
@Override
public void containerEvent(ContainerEvent event) {
}
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.PERIODIC_EVENT.equals(event.getType())) {
if (sender == null) {
if (proxyList == null)
sender = new MultiCastSender();
else
sender = new TcpSender();
}
/* Read busy and ready */
if (coll == null) {
try {
coll = new CollectedInfo(host, port);
this.port = coll.port;
this.host = coll.host;
} catch (Exception ex) {
log.error("Unable to initialize info collection: " + ex);
coll = null;
return;
}
}
/* Start or restart sender */
try {
sender.init(this);
} catch (Exception ex) {
log.error("Unable to initialize Sender: " + ex);
sender = null;
return;
}
/* refresh the connector information and send it */
try {
coll.refresh();
} catch (Exception ex) {
log.error("Unable to collect load information: " + ex);
coll = null;
return;
}
String output = "v=1&ready=" + coll.ready + "&busy=" + coll.busy +
"&port=" + port;
try {
sender.send(output);
} catch (Exception ex) {
log.error("Unable to send collected load information: " + ex);
}
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.backend;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.nio.charset.StandardCharsets;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/*
* Sender to proxies using multicast socket.
*/
public class MultiCastSender
implements Sender {
private static final Log log = LogFactory.getLog(HeartbeatListener.class);
HeartbeatListener config = null;
/* for multicasting stuff */
MulticastSocket s = null;
InetAddress group = null;
@Override
public void init(HeartbeatListener config) throws Exception {
this.config = config;
}
@Override
public int send(String mess) throws Exception {
if (s == null) {
try {
group = InetAddress.getByName(config.getGroup());
if (config.getHost() != null) {
InetAddress addr = InetAddress.getByName(config.getHost());
InetSocketAddress addrs = new InetSocketAddress(addr, config.getMultiport());
s = new MulticastSocket(addrs);
} else
s = new MulticastSocket(config.getMultiport());
s.setTimeToLive(config.getTtl());
s.joinGroup(group);
} catch (Exception ex) {
log.error("Unable to use multicast: " + ex);
s = null;
return -1;
}
}
byte[] buf;
buf = mess.getBytes(StandardCharsets.US_ASCII);
DatagramPacket data = new DatagramPacket(buf, buf.length, group, config.getMultiport());
try {
s.send(data);
} catch (Exception ex) {
log.error("Unable to send collected load information: " + ex);
s.close();
s = null;
return -1;
}
return 0;
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.backend;
import java.net.InetAddress;
/*
* This class represents a front-end httpd server.
*
*/
public class Proxy {
public InetAddress address = null;
public int port = 80;
}

View File

@@ -0,0 +1,40 @@
/*
* 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.backend;
/**
* Interface to send data to proxies.
*/
public interface Sender {
/**
* Set the configuration parameters
* @param config The heartbeat listener configuration
* @throws Exception An error occurred
*/
public void init(HeartbeatListener config) throws Exception;
/**
* Send the message to the proxies
* @param mess The message that will be sent
* @return <code>0</code> if no error occurred, <code>-1</code> otherwise
* @throws Exception An error occurred
*/
public int send(String mess) throws Exception;
}

View File

@@ -0,0 +1,208 @@
/*
* 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.backend;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.StringTokenizer;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/*
* Sender to proxies using multicast socket.
*/
public class TcpSender
implements Sender {
private static final Log log = LogFactory.getLog(HeartbeatListener.class);
HeartbeatListener config = null;
/**
* Proxies.
*/
protected Proxy[] proxies = null;
/**
* Active connections.
*/
protected Socket[] connections = null;
protected BufferedReader[] connectionReaders = null;
protected BufferedWriter[] connectionWriters = null;
@Override
public void init(HeartbeatListener config) throws Exception {
this.config = config;
StringTokenizer tok = new StringTokenizer(config.getProxyList(), ",");
proxies = new Proxy[tok.countTokens()];
int i = 0;
while (tok.hasMoreTokens()) {
String token = tok.nextToken().trim();
int pos = token.indexOf(':');
if (pos <=0)
throw new Exception("bad ProxyList");
proxies[i] = new Proxy();
proxies[i].port = Integer.parseInt(token.substring(pos + 1));
try {
proxies[i].address = InetAddress.getByName(token.substring(0, pos));
} catch (Exception e) {
throw new Exception("bad ProxyList");
}
i++;
}
connections = new Socket[proxies.length];
connectionReaders = new BufferedReader[proxies.length];
connectionWriters = new BufferedWriter[proxies.length];
}
@Override
public int send(String mess) throws Exception {
if (connections == null) {
log.error("Not initialized");
return -1;
}
String requestLine = "POST " + config.getProxyURL() + " HTTP/1.0";
for (int i = 0; i < connections.length; i++) {
if (connections[i] == null) {
try {
if (config.getHost() != null) {
connections[i] = new Socket();
InetAddress addr = InetAddress.getByName(config.getHost());
InetSocketAddress addrs = new InetSocketAddress(addr, 0);
connections[i].setReuseAddress(true);
connections[i].bind(addrs);
addrs = new InetSocketAddress(proxies[i].address, proxies[i].port);
connections[i].connect(addrs);
} else
connections[i] = new Socket(proxies[i].address, proxies[i].port);
connectionReaders[i] = new BufferedReader(new InputStreamReader(connections[i].getInputStream()));
connectionWriters[i] = new BufferedWriter(new OutputStreamWriter(connections[i].getOutputStream()));
} catch (Exception ex) {
log.error("Unable to connect to proxy: " + ex);
close(i);
}
}
if (connections[i] == null)
continue; // try next proxy in the list
BufferedWriter writer = connectionWriters[i];
try {
writer.write(requestLine);
writer.write("\r\n");
writer.write("Content-Length: " + mess.length() + "\r\n");
writer.write("User-Agent: HeartbeatListener/1.0\r\n");
writer.write("Connection: Keep-Alive\r\n");
writer.write("\r\n");
writer.write(mess);
writer.write("\r\n");
writer.flush();
} catch (Exception ex) {
log.error("Unable to send collected load information to proxy: " + ex);
close(i);
}
if (connections[i] == null)
continue; // try next proxy in the list
/* Read httpd answer */
String responseStatus = connectionReaders[i].readLine();
if (responseStatus == null) {
log.error("Unable to read response from proxy");
close(i);
continue;
} else {
responseStatus = responseStatus.substring(responseStatus.indexOf(' ') + 1, responseStatus.indexOf(' ', responseStatus.indexOf(' ') + 1));
int status = Integer.parseInt(responseStatus);
if (status != 200) {
log.error("Status is " + status);
close(i);
continue;
}
// read all the headers.
String header = connectionReaders[i].readLine();
int contentLength = 0;
while (!"".equals(header)) {
int colon = header.indexOf(':');
String headerName = header.substring(0, colon).trim();
String headerValue = header.substring(colon + 1).trim();
if ("content-length".equalsIgnoreCase(headerName)) {
contentLength = Integer.parseInt(headerValue);
}
header = connectionReaders[i].readLine();
}
if (contentLength > 0) {
char[] buf = new char[512];
while (contentLength > 0) {
int thisTime = (contentLength > buf.length) ? buf.length : contentLength;
int n = connectionReaders[i].read(buf, 0, thisTime);
if (n <= 0) {
log.error("Read content failed");
close(i);
break;
} else {
contentLength -= n;
}
}
}
}
}
return 0;
}
/**
* Close connection.
* @param i The index of the connection that will be closed
*/
protected void close(int i) {
try {
if (connectionReaders[i] != null) {
connectionReaders[i].close();
}
} catch (IOException e) {
}
connectionReaders[i] = null;
try {
if (connectionWriters[i] != null) {
connectionWriters[i].close();
}
} catch (IOException e) {
}
connectionWriters[i] = null;
try {
if (connections[i] != null) {
connections[i].close();
}
} catch (IOException e) {
}
connections[i] = null;
}
}

View File

@@ -0,0 +1,19 @@
# 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.
applicationContext.setAttribute.namenull=Name cannot be null
replicatedContext.startFailed=Failed to start ReplicatedContext: [{0}]
replicatedContext.startUnable=Unable to start ReplicatedContext: [{0}]

View File

@@ -0,0 +1,19 @@
# 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.
applicationContext.setAttribute.namenull=Le nom ne peut être null
replicatedContext.startFailed=Echec de démarrage du ReplicatedContext: [{0}]
replicatedContext.startUnable=Impossible de démarrer le ReplicatedContext: [{0}]

View File

@@ -0,0 +1,19 @@
# 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.
applicationContext.setAttribute.namenull=名前はnullにできません
replicatedContext.startFailed=ReplicatedContext: [{0}]の起動に失敗しました。
replicatedContext.startUnable=ReplicatedContext[{0}]を開始できません。

View File

@@ -0,0 +1,19 @@
# 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.
applicationContext.setAttribute.namenull=이름이 널일 수 없습니다.
replicatedContext.startFailed=ReplicatedContext를 시작하지 못했습니다: [{0}]
replicatedContext.startUnable=ReplicatedContext [{0}]을(를) 시작할 수 없습니다.

View File

@@ -0,0 +1,226 @@
/*
* 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 java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Loader;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
import org.apache.catalina.tribes.tipis.ReplicatedMap;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* @version 1.0
*/
public class ReplicatedContext extends StandardContext implements MapOwner {
private int mapSendOptions = Channel.SEND_OPTIONS_DEFAULT;
private static final Log log = LogFactory.getLog(ReplicatedContext.class);
protected static final long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
private static final StringManager sm = StringManager.getManager(ReplicatedContext.class);
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
try {
CatalinaCluster catclust = (CatalinaCluster)this.getCluster();
if ( catclust != null ) {
ReplicatedMap<String,Object> map = new ReplicatedMap<>(
this, catclust.getChannel(),DEFAULT_REPL_TIMEOUT,
getName(),getClassLoaders());
map.setChannelSendOptions(mapSendOptions);
((ReplApplContext)this.context).setAttributeMap(map);
}
} catch ( Exception x ) {
log.error(sm.getString("replicatedContext.startUnable", getName()),x);
throw new LifecycleException(sm.getString("replicatedContext.startFailed", getName()),x);
}
}
/**
* Stop this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void stopInternal() throws LifecycleException {
Map<String, Object> map = ((ReplApplContext) this.context)
.getAttributeMap();
super.stopInternal();
if (map instanceof ReplicatedMap) {
((ReplicatedMap<?, ?>) map).breakdown();
}
}
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()};
}
}
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ReplApplContext(this);
if (getAltDDName() != null)
context.setAttribute(Globals.ALT_DD_ATTR,getAltDDName());
}
return ((ReplApplContext)context).getFacade();
}
protected static class ReplApplContext extends ApplicationContext {
protected final Map<String, Object> tomcatAttributes = new ConcurrentHashMap<>();
public ReplApplContext(ReplicatedContext context) {
super(context);
}
protected ReplicatedContext getParent() {
return (ReplicatedContext)getContext();
}
@Override
protected ServletContext getFacade() {
return super.getFacade();
}
public Map<String,Object> getAttributeMap() {
return this.attributes;
}
public void setAttributeMap(Map<String,Object> map) {
this.attributes = map;
}
@Override
public void removeAttribute(String name) {
tomcatAttributes.remove(name);
//do nothing
super.removeAttribute(name);
}
@Override
public void setAttribute(String name, Object value) {
if (name == null) {
throw new IllegalArgumentException(sm.getString("applicationContext.setAttribute.namenull"));
}
if (value == null) {
removeAttribute(name);
return;
}
if ( (!getParent().getState().isAvailable()) || "org.apache.jasper.runtime.JspApplicationContextImpl".equals(name) ){
tomcatAttributes.put(name,value);
} else
super.setAttribute(name,value);
}
@Override
public Object getAttribute(String name) {
Object obj = tomcatAttributes.get(name);
if (obj == null) {
return super.getAttribute(name);
} else {
return obj;
}
}
@SuppressWarnings("unchecked")
@Override
public Enumeration<String> getAttributeNames() {
Set<String> names = new HashSet<>();
names.addAll(attributes.keySet());
return new MultiEnumeration<>(new Enumeration[] {
super.getAttributeNames(),
Collections.enumeration(names) });
}
}
protected static class MultiEnumeration<T> implements Enumeration<T> {
private final Enumeration<T>[] e;
public MultiEnumeration(Enumeration<T>[] lists) {
e = lists;
}
@Override
public boolean hasMoreElements() {
for ( int i=0; i<e.length; i++ ) {
if ( e[i].hasMoreElements() ) return true;
}
return false;
}
@Override
public T nextElement() {
for ( int i=0; i<e.length; i++ ) {
if ( e[i].hasMoreElements() ) return e[i].nextElement();
}
return null;
}
}
@Override
public void objectMadePrimary(Object key, Object value) {
//noop
}
}

View File

@@ -0,0 +1,787 @@
/*
* 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.util.HashMap;
import javax.management.MBeanServer;
import javax.management.ObjectName;
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.ha.ClusterDeployer;
import org.apache.catalina.ha.ClusterListener;
import org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.util.ContextName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.res.StringManager;
/**
* <p>
* A farm war deployer is a class that is able to deploy/undeploy web
* applications in WAR from within the cluster.
* </p>
* Any host can act as the admin, and will have three directories
* <ul>
* <li>watchDir - the directory where we watch for changes</li>
* <li>deployDir - 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 Peter Rossbach
*/
public class FarmWarDeployer extends ClusterListener
implements ClusterDeployer, FileChangeListener {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(FarmWarDeployer.class);
private static final StringManager sm = StringManager.getManager(FarmWarDeployer.class);
/*--Instance Variables--------------------------------------*/
protected boolean started = false;
protected final HashMap<String, FileMessageFactory> fileFactories =
new HashMap<>();
/**
* Deployment directory.
*/
protected String deployDir;
private File deployDirFile = null;
/**
* Temporary directory.
*/
protected String tempDir;
private File tempDirFile = null;
/**
* Watch directory.
*/
protected String watchDir;
private File watchDirFile = null;
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 backgroundProcess 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;
/**
* MBean server.
*/
protected MBeanServer mBeanServer = null;
/**
* The associated deployer ObjectName.
*/
protected ObjectName oname = null;
/**
* The maximum valid time(in seconds) for FileMessageFactory.
*/
protected int maxValidTime = 5 * 60;
/*--Constructor---------------------------------------------*/
public FarmWarDeployer() {
}
/*--Logic---------------------------------------------------*/
@Override
public void start() throws Exception {
if (started)
return;
Container hcontainer = getCluster().getContainer();
if(!(hcontainer instanceof Host)) {
log.error(sm.getString("farmWarDeployer.hostOnly"));
return ;
}
host = (Host) hcontainer;
// Check to correct engine and host setup
Container econtainer = host.getParent();
if(!(econtainer instanceof Engine)) {
log.error(sm.getString("farmWarDeployer.hostParentEngine",
host.getName()));
return ;
}
Engine engine = (Engine) econtainer;
String hostname = null;
hostname = host.getName();
try {
oname = new ObjectName(engine.getName() + ":type=Deployer,host="
+ hostname);
} catch (Exception e) {
log.error(sm.getString("farmWarDeployer.mbeanNameFail",
engine.getName(), hostname),e);
return;
}
if (watchEnabled) {
watcher = new WarWatcher(this, getWatchDirFile());
if (log.isInfoEnabled()) {
log.info(sm.getString(
"farmWarDeployer.watchDir", getWatchDir()));
}
}
configBase = host.getConfigBaseFile();
// Retrieve the MBean server
mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
started = true;
count = 0;
getCluster().addClusterListener(this);
if (log.isInfoEnabled())
log.info(sm.getString("farmWarDeployer.started"));
}
/*
* stop cluster wide deployments
*
* @see org.apache.catalina.ha.ClusterDeployer#stop()
*/
@Override
public void stop() throws LifecycleException {
started = false;
getCluster().removeClusterListener(this);
count = 0;
if (watcher != null) {
watcher.clear();
watcher = null;
}
if (log.isInfoEnabled())
log.info(sm.getString("farmWarDeployer.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
*/
@Override
public void messageReceived(ClusterMessage msg) {
try {
if (msg instanceof FileMessage) {
FileMessage fmsg = (FileMessage) msg;
if (log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.msgRxDeploy",
fmsg.getContextName(), 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(getDeployDirFile(), name);
try {
String contextName = fmsg.getContextName();
if (!isServiced(contextName)) {
addServiced(contextName);
try {
remove(contextName);
if (!factory.getFile().renameTo(deployable)) {
log.error(sm.getString(
"farmWarDeployer.renameFail",
factory.getFile(), deployable));
}
check(contextName);
} finally {
removeServiced(contextName);
}
if (log.isDebugEnabled())
log.debug(sm.getString(
"farmWarDeployer.deployEnd",
contextName));
} else
log.error(sm.getString(
"farmWarDeployer.servicingDeploy",
contextName, name));
} catch (Exception ex) {
log.error(ex);
} finally {
removeFactory(fmsg);
}
}
} else if (msg instanceof UndeployMessage) {
try {
UndeployMessage umsg = (UndeployMessage) msg;
String contextName = umsg.getContextName();
if (log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.msgRxUndeploy",
contextName));
if (!isServiced(contextName)) {
addServiced(contextName);
try {
remove(contextName);
} finally {
removeServiced(contextName);
}
if (log.isDebugEnabled())
log.debug(sm.getString(
"farmWarDeployer.undeployEnd",
contextName));
} else
log.error(sm.getString(
"farmWarDeployer.servicingUndeploy",
contextName));
} catch (Exception ex) {
log.error(ex);
}
}
} catch (java.io.IOException x) {
log.error(sm.getString("farmWarDeployer.msgIoe"), x);
}
}
/**
* Create factory for all transported war files
*
* @param msg The file
* @return Factory for all app message (war files)
* @throws java.io.FileNotFoundException Missing file error
* @throws java.io.IOException Other IO error
*/
public synchronized FileMessageFactory getFactory(FileMessage msg)
throws java.io.FileNotFoundException, java.io.IOException {
File writeToFile = new File(getTempDirFile(), msg.getFileName());
FileMessageFactory factory = fileFactories.get(msg.getFileName());
if (factory == null) {
factory = FileMessageFactory.getInstance(writeToFile, true);
factory.setMaxValidTime(maxValidTime);
fileFactories.put(msg.getFileName(), factory);
}
return factory;
}
/**
* Remove file (war) from messages
*
* @param msg The file
*/
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.
*/
@Override
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 name.
* <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 contextName
* The context name to which this application should be installed
* (must be unique)
* @param webapp
* A WAR file or unpacked directory structure containing the web
* application to be installed
*
* @exception IllegalArgumentException
* if the specified context name is malformed
* @exception IllegalStateException
* if the specified context name is already deployed
* @exception IOException
* if an input/output error was encountered during
* installation
*/
@Override
public void install(String contextName, File webapp) throws IOException {
Member[] members = getCluster().getMembers();
if (members.length == 0) return;
Member localMember = getCluster().getLocalMember();
FileMessageFactory factory =
FileMessageFactory.getInstance(webapp, false);
FileMessage msg = new FileMessage(localMember, webapp.getName(),
contextName);
if(log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.sendStart", contextName,
webapp));
msg = factory.readMessage(msg);
while (msg != null) {
for (int i = 0; i < members.length; i++) {
if (log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.sendFragment",
contextName, webapp, members[i]));
getCluster().send(msg, members[i]);
}
msg = factory.readMessage(msg);
}
if(log.isDebugEnabled())
log.debug(sm.getString(
"farmWarDeployer.sendEnd", contextName, webapp));
}
/**
* Remove an existing web application, attached to the specified context
* name. 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 contextName
* The context name of the application to be removed
* @param undeploy
* boolean flag to remove web application from server
*
* @exception IllegalArgumentException
* if the specified context name is malformed
* @exception IllegalArgumentException
* if the specified context name does not identify a
* currently installed web application
* @exception IOException
* if an input/output error occurs during removal
*/
@Override
public void remove(String contextName, boolean undeploy)
throws IOException {
if (getCluster().getMembers().length > 0) {
if (log.isInfoEnabled())
log.info(sm.getString("farmWarDeployer.removeStart", contextName));
Member localMember = getCluster().getLocalMember();
UndeployMessage msg = new UndeployMessage(localMember, System
.currentTimeMillis(), "Undeploy:" + contextName + ":"
+ System.currentTimeMillis(), contextName);
if (log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.removeTxMsg", contextName));
cluster.send(msg);
}
// remove locally
if (undeploy) {
try {
if (!isServiced(contextName)) {
addServiced(contextName);
try {
remove(contextName);
} finally {
removeServiced(contextName);
}
} else
log.error(sm.getString("farmWarDeployer.removeFailRemote",
contextName));
} catch (Exception ex) {
log.error(sm.getString("farmWarDeployer.removeFailLocal",
contextName), ex);
}
}
}
/**
* Modification from watchDir war detected!
*
* @see org.apache.catalina.ha.deploy.FileChangeListener#fileModified(File)
*/
@Override
public void fileModified(File newWar) {
try {
File deployWar = new File(getDeployDirFile(), newWar.getName());
ContextName cn = new ContextName(deployWar.getName(), true);
if (deployWar.exists() && deployWar.lastModified() > newWar.lastModified()) {
if (log.isInfoEnabled())
log.info(sm.getString("farmWarDeployer.alreadyDeployed", cn.getName()));
return;
}
if (log.isInfoEnabled())
log.info(sm.getString("farmWarDeployer.modInstall",
cn.getName(), deployWar.getAbsolutePath()));
// install local
if (!isServiced(cn.getName())) {
addServiced(cn.getName());
try {
copy(newWar, deployWar);
check(cn.getName());
} finally {
removeServiced(cn.getName());
}
} else {
log.error(sm.getString("farmWarDeployer.servicingDeploy",
cn.getName(), deployWar.getName()));
}
install(cn.getName(), deployWar);
} catch (Exception x) {
log.error(sm.getString("farmWarDeployer.modInstallFail"), x);
}
}
/**
* War remove from watchDir
*
* @see org.apache.catalina.ha.deploy.FileChangeListener#fileRemoved(File)
*/
@Override
public void fileRemoved(File removeWar) {
try {
ContextName cn = new ContextName(removeWar.getName(), true);
if (log.isInfoEnabled())
log.info(sm.getString("farmWarDeployer.removeLocal",
cn.getName()));
remove(cn.getName(), true);
} catch (Exception x) {
log.error(sm.getString("farmWarDeployer.removeLocalFail"), x);
}
}
/**
* Invoke the remove method on the deployer.
* @param contextName The context to remove
* @throws Exception If an error occurs removing the context
*/
protected void remove(String contextName) throws Exception {
// TODO Handle remove also work dir content !
// Stop the context first to be nicer
Context context = (Context) host.findChild(contextName);
if (context != null) {
if(log.isDebugEnabled())
log.debug(sm.getString("farmWarDeployer.undeployLocal",
contextName));
context.stop();
String baseName = context.getBaseName();
File war = new File(host.getAppBaseFile(), baseName + ".war");
File dir = new File(host.getAppBaseFile(), baseName);
File xml = new File(configBase, baseName + ".xml");
if (war.exists()) {
if (!war.delete()) {
log.error(sm.getString("farmWarDeployer.deleteFail", war));
}
} else if (dir.exists()) {
undeployDir(dir);
} else {
if (!xml.delete()) {
log.error(sm.getString("farmWarDeployer.deleteFail", xml));
}
}
// Perform new deployment and remove internal HostConfig state
check(contextName);
}
}
/**
* 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 {
if (!file.delete()) {
log.error(sm.getString("farmWarDeployer.deleteFail", file));
}
}
}
if (!dir.delete()) {
log.error(sm.getString("farmWarDeployer.deleteFail", dir));
}
}
/**
* Call watcher to check for deploy changes
*
* @see org.apache.catalina.ha.ClusterDeployer#backgroundProcess()
*/
@Override
public void backgroundProcess() {
if (started) {
if (watchEnabled) {
count = (count + 1) % processDeployFrequency;
if (count == 0) {
watcher.check();
}
}
removeInvalidFileFactories();
}
}
/*--Deployer Operations ------------------------------------*/
/**
* Check a context for deployment operations.
* @param name The context name
* @throws Exception Error invoking the deployer
*/
protected void check(String name) throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
mBeanServer.invoke(oname, "check", params, signature);
}
/**
* Verified if a context is being services.
* @param name The context name
* @return <code>true</code> if the context is being serviced
* @throws Exception Error invoking 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();
}
/**
* Mark a context as being services.
* @param name The context name
* @throws Exception Error invoking the deployer
*/
protected void addServiced(String name) throws Exception {
String[] params = { name };
String[] signature = { "java.lang.String" };
mBeanServer.invoke(oname, "addServiced", params, signature);
}
/**
* Mark a context as no longer being serviced.
* @param name The context name
* @throws Exception Error invoking 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--------------------------------*/
@Override
public boolean equals(Object listener) {
return super.equals(listener);
}
@Override
public int hashCode() {
return super.hashCode();
}
public String getDeployDir() {
return deployDir;
}
public File getDeployDirFile() {
if (deployDirFile != null) return deployDirFile;
File dir = getAbsolutePath(getDeployDir());
this.deployDirFile = dir;
return dir;
}
public void setDeployDir(String deployDir) {
this.deployDir = deployDir;
}
public String getTempDir() {
return tempDir;
}
public File getTempDirFile() {
if (tempDirFile != null) return tempDirFile;
File dir = getAbsolutePath(getTempDir());
this.tempDirFile = dir;
return dir;
}
public void setTempDir(String tempDir) {
this.tempDir = tempDir;
}
public String getWatchDir() {
return watchDir;
}
public File getWatchDirFile() {
if (watchDirFile != null) return watchDirFile;
File dir = getAbsolutePath(getWatchDir());
this.watchDirFile = dir;
return dir;
}
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;
}
public int getMaxValidTime() {
return maxValidTime;
}
public void setMaxValidTime(int maxValidTime) {
this.maxValidTime = maxValidTime;
}
/**
* 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()) {
if (!to.createNewFile()) {
log.error(sm.getString("fileNewFail", to));
return false;
}
}
} catch (IOException e) {
log.error(sm.getString("farmWarDeployer.fileCopyFail",
from, to), e);
return false;
}
try (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);
}
} catch (IOException e) {
log.error(sm.getString("farmWarDeployer.fileCopyFail",
from, to), e);
return false;
}
return true;
}
protected void removeInvalidFileFactories() {
String[] fileNames = fileFactories.keySet().toArray(new String[0]);
for (String fileName : fileNames) {
FileMessageFactory factory = fileFactories.get(fileName);
if (!factory.isValid()) {
fileFactories.remove(fileName);
}
}
}
private File getAbsolutePath(String path) {
File dir = new File(path);
if (!dir.isAbsolute()) {
dir = new File(getCluster().getContainer().getCatalinaBase(),
dir.getPath());
}
try {
dir = dir.getCanonicalFile();
} catch (IOException e) {// ignore
}
return dir;
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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);
}

View File

@@ -0,0 +1,88 @@
/*
* 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.ClusterMessageBase;
import org.apache.catalina.tribes.Member;
/**
* Contains the data for a file being transferred over TCP, this is
* essentially a fragment of a file, read and written by the FileMessageFactory
* @version 1.0
*/
public class FileMessage extends ClusterMessageBase {
private static final long serialVersionUID = 2L;
private int messageNumber;
private byte[] data;
private int dataLength;
private long totalNrOfMsgs;
private final String fileName;
private final String contextName;
public FileMessage(Member source,
String fileName,
String contextName) {
this.address=source;
this.fileName=fileName;
this.contextName=contextName;
}
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;
}
@Override
public String getUniqueId() {
StringBuilder result = new StringBuilder(getFileName());
result.append("#-#");
result.append(getMessageNumber());
result.append("#-#");
result.append(System.currentTimeMillis());
return result.toString();
}
public String getFileName() {
return fileName;
}
public String getContextName() {
return contextName;
}
}

View File

@@ -0,0 +1,402 @@
/*
* 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.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.res.StringManager;
/**
* 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.
*
* @version 1.0
*/
public class FileMessageFactory {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(FileMessageFactory.class);
private static final StringManager sm = StringManager.getManager(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 final File file;
/**
* True means that we are writing with this factory. False means that we are
* reading with this factory
*/
protected final boolean openForWrite;
/**
* Once the factory is used, it cannot 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 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 number of the last message processed. Message IDs are 1 based.
*/
protected AtomicLong lastMessageProcessed = new AtomicLong(0);
/**
* Messages received out of order are held in the buffer until required. If
* everything is worked as expected, messages will spend very little time in
* the buffer.
*/
protected final Map<Long, FileMessage> msgBuffer = new ConcurrentHashMap<>();
/**
* The bytes that we hold the data in, not thread safe.
*/
protected byte[] data = new byte[READ_SIZE];
/**
* Flag that indicates if a thread is writing messages to disk. Access to
* this flag must be synchronised.
*/
protected boolean isWriting = false;
/**
* The time this instance was created. (in milliseconds)
*/
protected long creationTime = 0;
/**
* The maximum valid time(in seconds) from creationTime.
*/
protected int maxValidTime = -1;
/**
* 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())
if (!file.createNewFile()) {
throw new IOException(sm.getString("fileNewFail", file));
}
out = new FileOutputStream(f);
} else {
size = file.length();
totalNrOfMessages = (size / READ_SIZE) + 1;
in = new FileInputStream(f);
}//end if
creationTime = System.currentTimeMillis();
}
/**
* 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.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(sm.getString("fileMessageFactory.cannotWrite"));
}
if (log.isDebugEnabled())
log.debug("Message " + msg + " data " + HexUtils.toHexString(msg.getData())
+ " data length " + msg.getDataLength() + " out " + out);
if (msg.getMessageNumber() <= lastMessageProcessed.get()) {
// Duplicate of message already processed
log.warn(sm.getString("fileMessageFactory.duplicateMessage", msg.getContextName(), msg.getFileName(),
HexUtils.toHexString(msg.getData()), Integer.valueOf(msg.getDataLength())));
return false;
}
FileMessage previous =
msgBuffer.put(Long.valueOf(msg.getMessageNumber()), msg);
if (previous != null) {
// Duplicate of message not yet processed
log.warn(sm.getString("fileMessageFactory.duplicateMessage", msg.getContextName(), msg.getFileName(),
HexUtils.toHexString(msg.getData()), Integer.valueOf(msg.getDataLength())));
return false;
}
FileMessage next = null;
synchronized (this) {
if (!isWriting) {
next = msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1));
if (next != null) {
isWriting = true;
} else {
return false;
}
} else {
return false;
}
}
while (next != null) {
out.write(next.getData(), 0, next.getDataLength());
lastMessageProcessed.incrementAndGet();
out.flush();
if (next.getMessageNumber() == next.getTotalNrOfMsgs()) {
out.close();
cleanup();
return true;
}
synchronized(this) {
next =
msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1));
if (next == null) {
isWriting = false;
}
}
}
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 (IOException ignore) {
}
if (out != null)
try {
out.close();
} catch (IOException ignore) {
}
in = null;
out = null;
size = 0;
closed = true;
data = null;
nrOfMessagesProcessed = 0;
totalNrOfMessages = 0;
msgBuffer.clear();
lastMessageProcessed = null;
}
/**
* 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 The value to check
* @throws IllegalArgumentException if the state is not the expected one
*/
protected void checkState(boolean openForWrite)
throws IllegalArgumentException {
if (this.openForWrite != openForWrite) {
cleanup();
if (openForWrite) {
throw new IllegalArgumentException(sm.getString("fileMessageFactory.cannotWrite"));
} else {
throw new IllegalArgumentException(sm.getString("fileMessageFactory.cannotRead"));
}
}
if (this.closed) {
cleanup();
throw new IllegalArgumentException(sm.getString("fileMessageFactory.closed"));
}
}
/**
* Example usage.
*
* @param args
* String[], args[0] - read from filename, args[1] write to
* filename
* @throws Exception An error occurred
*/
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);
if (msg == null) {
System.out.println("Empty input file : " + args[0]);
return;
}
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;
}
public boolean isValid() {
if (maxValidTime > 0) {
long timeNow = System.currentTimeMillis();
int timeIdle = (int) ((timeNow - creationTime) / 1000L);
if (timeIdle > maxValidTime) {
cleanup();
if (file.exists() && !file.delete()) {
log.warn(sm.getString("fileMessageFactory.deleteFail", file));
}
return false;
}
}
return true;
}
public int getMaxValidTime() {
return maxValidTime;
}
public void setMaxValidTime(int maxValidTime) {
this.maxValidTime = maxValidTime;
}
}

View File

@@ -0,0 +1,59 @@
# 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.
farmWarDeployer.alreadyDeployed=webapp [{0}] are already deployed.
farmWarDeployer.deleteFail=Failed to delete [{0}]
farmWarDeployer.deployEnd=Deployment from [{0}] finished.
farmWarDeployer.fileCopyFail=Unable to copy from [{0}] to [{1}]
farmWarDeployer.hostOnly=FarmWarDeployer can only work as host cluster subelement!
farmWarDeployer.hostParentEngine=FarmWarDeployer can only work if parent of [{0}] is an engine!
farmWarDeployer.mbeanNameFail=Cannot construct MBean object name for engine [{0}] and host [{1}]
farmWarDeployer.modInstall=Installing webapp [{0}] from [{1}]
farmWarDeployer.modInstallFail=Unable to install WAR file
farmWarDeployer.modRemoveFail=No removal
farmWarDeployer.msgIoe=Unable to read farm deploy file message.
farmWarDeployer.msgRxDeploy=Receive cluster deployment path [{0}], war [{1}]
farmWarDeployer.msgRxUndeploy=Receive cluster undeployment from path [{0}]
farmWarDeployer.removeFailLocal=Local remove from [{0}] failed
farmWarDeployer.removeFailRemote=Local remove from [{0}] failed, other manager has app in service!
farmWarDeployer.removeLocal=Removing webapp [{0}]
farmWarDeployer.removeLocalFail=Unable to remove WAR file
farmWarDeployer.removeStart=Cluster wide remove of web app [{0}]
farmWarDeployer.removeTxMsg=Send cluster wide undeployment from [{0}]
farmWarDeployer.renameFail=Failed to rename [{0}] to [{1}]
farmWarDeployer.sendEnd=Send cluster war deployment path [{0}], war [{1}] finished.
farmWarDeployer.sendFragment=Send cluster war fragment path [{0}], war [{1}] to [{2}]
farmWarDeployer.sendStart=Send cluster war deployment path [{0}], war [{1}] started.
farmWarDeployer.servicingDeploy=Application [{0}] is being serviced. Touch war file [{1}] again!
farmWarDeployer.servicingUndeploy=Application [{0}] is being serviced and can''t be removed from backup cluster node
farmWarDeployer.started=Cluster FarmWarDeployer started.
farmWarDeployer.stopped=Cluster FarmWarDeployer stopped.
farmWarDeployer.undeployEnd=Undeployment from [{0}] finished.
farmWarDeployer.undeployLocal=Undeploy local context [{0}]
farmWarDeployer.watchDir=Cluster deployment is watching [{0}] for changes.
fileMessageFactory.cannotRead=Cannot read message, this factory is writing
fileMessageFactory.cannotWrite=Cannot write message, this factory is reading
fileMessageFactory.closed=Factory has been closed
fileMessageFactory.deleteFail=Failed to delete [{0}]
fileMessageFactory.duplicateMessage=Received duplicate message. Is the Sender timeout too low? context: [{0}] filename: [{1}] data: [{2}] data length: [{3}]
fileNewFail=Unable to create [{0}]
warWatcher.cantListWatchDir=Cannot list files in WatchDir [{0}]: check to see if it is a directory and has read permissions.
warWatcher.checkWarResult=WarInfo.check() returned [{0}] for [{1}]
warWatcher.checkingWar=Checking WAR file [{0}]
warWatcher.checkingWars=Checking WARs in [{0}]
warWatcher.listedFileDoesNotExist=[{0}] was detected in [{1}] but does not exist. Check directory permissions on [{1}]?

View File

@@ -0,0 +1,25 @@
# 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.
farmWarDeployer.modInstall=Installiere Webapplikation [{0}] von [{1}]
farmWarDeployer.modInstallFail=WAR-Datei konnte nicht installiert werden
farmWarDeployer.msgIoe=Die Farm-Deploy-Datei-Nachricht kann nicht gelesen werden.
farmWarDeployer.servicingUndeploy=Applikation [{0}] ist noch aktiv und kann nicht vom Backup-Cluster-Knoten entfernt werden
farmWarDeployer.undeployEnd=Undeployment von [{0}] beendet.
fileMessageFactory.duplicateMessage=Doppelte Nachricht empfangen. Ist der Timeout für den Sender zu niedrig? Context: [{0}] Dateiname: [{1}] Daten: [{2}] Datenlänge [{3}]
warWatcher.cantListWatchDir=Dateien in WatchDir [{0}] können nicht gelistet werdenm: Prüfen Sie dass Lesezugriff auf das Verzeichnis besteht
warWatcher.checkingWar=WAR-Datei [{0}] wird geprüft.

View File

@@ -0,0 +1,31 @@
# 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.
farmWarDeployer.hostOnly=FarmWarDeployer sólo puede operar como un subelemento de una máquina del cluster!
farmWarDeployer.modInstall=Installando webapp [{0}] desde [{1}]\n
farmWarDeployer.modInstallFail=Incapaz de instalar WAR file
farmWarDeployer.msgIoe=Incapáz de leer el archivo de despliegue de la granja
farmWarDeployer.msgRxDeploy=Recibe el camino de despliegue del cluster [{0}], war [{1}]
farmWarDeployer.removeFailLocal=Borrado local de [{0}] fallido
farmWarDeployer.removeFailRemote=El borrado local de [{0}] falló, otro manager tiene la aplicación en servicio!
farmWarDeployer.sendFragment=Fragmento war enviado al cluster con camino [{0}], war [{1}] a [{2}]\n
farmWarDeployer.servicingUndeploy=La applicación [{0}] esta en servicion y no pude ser removida del nodo de respaldo del cluster
farmWarDeployer.undeployEnd=El revertimiendo del despliegue de [{0}] ha terminado.\n
fileMessageFactory.deleteFail=Fallo al borrar [{0}]\n
warWatcher.cantListWatchDir=No se pueden listar archivos en WatchDir [{0}]: verifique si es un directorio y tiene permisos de lectura.\n
warWatcher.checkWarResult=WarInfo.check() devolvió [{0}] para [{1}]
warWatcher.checkingWar=Verificando archivo WAR [{0}]

View File

@@ -0,0 +1,58 @@
# 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.
farmWarDeployer.alreadyDeployed=l''application web [{0}] est déjà déployée
farmWarDeployer.deleteFail=Pas réussi à supprimer [{0}]
farmWarDeployer.deployEnd=Le déploiement de [{0}] est terminé
farmWarDeployer.fileCopyFail=Impossible de copier depuis [{0}] vers [{1}]
farmWarDeployer.hostOnly=Le FarmWarDeployer ne fonctionne qu'en tant que sous-élément d'un "host cluster" !
farmWarDeployer.hostParentEngine=FarmWarDeployer peut fonctionner uniquement si le parent de [{0}] est un moteur
farmWarDeployer.mbeanNameFail=Impossible de construire le nom d''objet du mbean pour le moteur [{0}] et l''hôte [{1}]
farmWarDeployer.modInstall=Installation en cours pour la webapp [{0}] depuis [{1}]
farmWarDeployer.modInstallFail=Incapable d'installer le fichier WAR
farmWarDeployer.msgIoe=Incapable de lire le message de déploiement dans la ferme
farmWarDeployer.msgRxDeploy=Recu le chemin de déploiement [{0}] du cluster, war [{1}]
farmWarDeployer.msgRxUndeploy=Réception d''un retrait de cluster ("cluster undeployment") du chemin [{0}]
farmWarDeployer.removeFailLocal=Impossible d''enlever localement de [{0}]
farmWarDeployer.removeFailRemote=La suppression locale de [{0}] a échouée, l''autre gestionnaire (manager) a l''app en fonction !
farmWarDeployer.removeLocal=Retrait de l''application web [{0}]
farmWarDeployer.removeLocalFail=Impossible d'enlever le fichier WAR
farmWarDeployer.removeStart=Retrait de l''application web [{0}] dans tout le cluster
farmWarDeployer.removeTxMsg=Envoi à tout le cluster du déploiement à partir de [{0}]
farmWarDeployer.renameFail=Echec du renommage de [{0}] en [{1}]
farmWarDeployer.sendEnd=Envoi du chemin de déploiement du war au cluster, war [{1}] terminé
farmWarDeployer.sendFragment=Envoi du chemin du fragment du war du cluster [{0}], war [{1}] vers [{2}]
farmWarDeployer.sendStart=Envoi du déploiement war en cluster chemin [{0}], war [{1}] démarré
farmWarDeployer.servicingDeploy=L''application [{0}] est en cours de maintenance, mettez de nouveau à jour la date du fichier war [{1}]
farmWarDeployer.servicingUndeploy=L''application [{0}] est en maintenance et ne peut être retirée du node backup du cluster
farmWarDeployer.started=Le FarmWarDeployer du cluster a démarré
farmWarDeployer.stopped=Le FarmWarDeployer du cluster a été arrêté
farmWarDeployer.undeployEnd=Retrait de [{0}] terminé
farmWarDeployer.undeployLocal=Le contexte local [{0}] est retiré
farmWarDeployer.watchDir=Le déploiement du cluster surveille [{0}] pour des modifications
fileMessageFactory.cannotRead=Impossible de lire un message, cette fabrique est en train d'écrire
fileMessageFactory.cannotWrite=Impossible d'écrire un message, cette fabrique est en train de lire
fileMessageFactory.closed=La fabrique a été fermée
fileMessageFactory.deleteFail=Impossible de supprimer [{0}]
fileMessageFactory.duplicateMessage=Réception de message en double, le délai d''attente maximum de l''expéditeur pourrait être trop court; contexte: [{0}] nom de fichier: [{1}] données: [{2}] longueur des données: [{3}]
fileNewFail=Impossible de créer [{0}]
warWatcher.cantListWatchDir=Incapacité de lister les fichiers dans le répertoire WatchDir [{0}]: vérifiez qu''il s''agit d''un répertoire et qu''il a la permission de lecture.
warWatcher.checkWarResult=WarInfo.check() a retourné [{0}] pour [{1}]
warWatcher.checkingWar=Vérification du fichier WAR [{0}]
warWatcher.checkingWars=Vérification des WARs dans [{0}]
warWatcher.listedFileDoesNotExist=[{0}] a été détecté dans [{1}] mais n''existe pas, les permissions du répertoire pourraient être incorrectes

View File

@@ -0,0 +1,58 @@
# 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.
farmWarDeployer.alreadyDeployed=webapp [{0}]は既にデプロイされています。
farmWarDeployer.deleteFail=[{0}] を削除できません。
farmWarDeployer.deployEnd=[{0}]からの配備が完了しました。
farmWarDeployer.fileCopyFail=[{0}] から [{1}] へコピーできません。
farmWarDeployer.hostOnly=FarmWarDeployer はHostクラスタのサブエレメントとしてのみ機能します
farmWarDeployer.hostParentEngine=FarmWarDeployer は親 [{0}] が Engine のインスタンスでなければ機能しません。
farmWarDeployer.mbeanNameFail=エンジン[{0}]とホスト[{1}]のMBeanオブジェクト名を構築できません
farmWarDeployer.modInstall=[{1}]からwebapp [{0}]をインストールしています。
farmWarDeployer.modInstallFail=WAR ファイルをインストールできませんでした。
farmWarDeployer.msgIoe=ファームデプロイファイルメッセージの読み取りに失敗しました。
farmWarDeployer.msgRxDeploy=クラスタ展開パス[{0}]、WAR[{1}]を受信しました。
farmWarDeployer.msgRxUndeploy=パス[{0}]からクラスタの展開解除を受信しました。
farmWarDeployer.removeFailLocal=[{0}]からのローカル削除に失敗しました
farmWarDeployer.removeFailRemote=ローカルの [{0}] を削除できませんでした。他のマネージャーノードでサービス中です !
farmWarDeployer.removeLocal=webapp [{0}]を削除しています
farmWarDeployer.removeLocalFail=WAR ファイルを削除できません。
farmWarDeployer.removeStart=Webアプリケーション[{0}]のクラスタ削除。
farmWarDeployer.removeTxMsg=コンテキスト [{0}] からクラスターに配置解除メッセージを送信しました。
farmWarDeployer.renameFail=[{0}] から [{1}] へ名前を変更できません。
farmWarDeployer.sendEnd=クラスタWarデプロイメント パス[{0}]を送信しました。WAR[{1}]を完了しました。
farmWarDeployer.sendFragment=コンテキスト [{0}] の war [{1}] をクラスターメンバー [{2}] へ送信します。
farmWarDeployer.sendStart=クラスタWar展開パス[{0}]を送信し、War[{1}]を開始しました。
farmWarDeployer.servicingDeploy=アプリケーション [{0}] はすでにサービスを開始しています。もう一度 WAR ファイル [{1}] を更新してください。
farmWarDeployer.servicingUndeploy=アプリケーション [{0}] はサービス中のためバックアップクラスタノードから削除できません。
farmWarDeployer.started=クラスターの FarmWarDeployer を開始しました。
farmWarDeployer.stopped=Cluster FarmWarDeployer が停止しました。
farmWarDeployer.undeployEnd=コンテキスト [{0}] の配置解除が完了しました。
farmWarDeployer.undeployLocal=ローカルコンテキスト [{0}] を配置解除します。
farmWarDeployer.watchDir=クラスタデプロイメントの監視[{0}]が変更されています。
fileMessageFactory.cannotRead=メッセージを読むことができません。このFactoryは書き込み中です。
fileMessageFactory.cannotWrite=メッセージを書き込めません、このFactoryは読み込み中です。
fileMessageFactory.closed=FileMessageFactoryはクローズされています。
fileMessageFactory.deleteFail=[{0}]を削除できませんでした
fileMessageFactory.duplicateMessage=メッセージをもう一度受信します。SenderのActTimeoutが短すぎます。 名前:[{0}] war[{1}] データ:[{2}] データ長:[{3}]
fileNewFail=[{0}]を作成できません。
warWatcher.cantListWatchDir=監視ディレクトリ [{0}] のファイル一覧を取得できません : ディレクトリの読み取り権限を確認してください。
warWatcher.checkWarResult=WAR ファイル [{1}] について WarInfo.check() は [{0}] を返しました。
warWatcher.checkingWar=WAR ファイル [{0}] をチェックしています。
warWatcher.checkingWars=[{0}]のWARを確認します
warWatcher.listedFileDoesNotExist=[{1}] にあるはずの [{0}] が存在しません。[{1}] のディレクトリ権限を確認してください。

View File

@@ -0,0 +1,58 @@
# 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.
farmWarDeployer.alreadyDeployed=webapp [{0}](들)이 이미 배치되어 있습니다.
farmWarDeployer.deleteFail=[{0}]을(를) 삭제하지 못했습니다.
farmWarDeployer.deployEnd=[{0}](으)로부터의 배치 작업이 완료됐습니다.
farmWarDeployer.fileCopyFail=[{0}](으)로부터 [{1}](으)로 복사할 수 없습니다.
farmWarDeployer.hostOnly=FarmWarDeployer는 오직 하위 엘리먼트인 호스트 클러스터 엘리먼트에서 존재해야 합니다.
farmWarDeployer.hostParentEngine=FarmWarDeployer는, 오직 [{0}]의 부모가 엔진일 때에만, 정상 동작할 수 있습니다!
farmWarDeployer.mbeanNameFail=엔진 [{0}]와(과) 호스트 [{1}]을(를) 위한 MBean 객체 이름을 구성할 수 없습니다.
farmWarDeployer.modInstall=[{1}](으)로부터 웹 애플리케이션 [{0}]을(를) 설치합니다.
farmWarDeployer.modInstallFail=WAR 파일을 설치할 수 없습니다.
farmWarDeployer.msgIoe=farm deploy 파일 메시지를 읽을 수 없습니다.
farmWarDeployer.msgRxDeploy=클러스터 배치 경로 [{0}]을(를) 받았음, war: [{1}]
farmWarDeployer.msgRxUndeploy=경로 [{0}]에 대한 클러스터 배치 제거 메시지를 수신했습니다.
farmWarDeployer.removeFailLocal=[{0}](으)로부터 로컬 삭제가 실패했습니다.
farmWarDeployer.removeFailRemote=FarmWarDeployer가 컨텍스트 [{0}]을(를) 로컬에서 제거하지 못하였습니다. 다른 Manager가 해당 애플리케이션을 서비스 중에 있습니다!
farmWarDeployer.removeLocal=웹 애플리케이션 [{0}]을(를) 제거합니다.
farmWarDeployer.removeLocalFail=WAR 파일을 제거할 수 없습니다.
farmWarDeployer.removeStart=웹 애플리케이션 [{0}]을(를) 전 클러스터에서 제거
farmWarDeployer.removeTxMsg=전 클러스터에서 [{0}]에 대한 배치를 제거 할 것을 전송합니다.
farmWarDeployer.renameFail=[{0}]을(를) [{1}](으)로 이름을 변경하지 못했습니다.
farmWarDeployer.sendEnd=클러스터 war 배치 경로 [{0}]을(를) 전송합니다. war [{1}]은(는) 완료되었습니다.
farmWarDeployer.sendFragment=클러스터 war 파일 (경로: [{0}]) [{1}]을(를) [{2}](으)로 전송합니다.
farmWarDeployer.sendStart=클러스터 war 배치 경로 [{0}]을(를) 전송합니다, war [{1}]이(가) 시작되었습니다.
farmWarDeployer.servicingDeploy=애플리케이션 [{0}]이(가) 서비스되고 있습니다. War 파일 [{1}]을(를) 다시 touch 하십시오!
farmWarDeployer.servicingUndeploy=애플리케이션 [{0}]이(가) 서비스 되고 있는 중이어서, 백업 클러스터 노드로부터 제거될 수 없습니다.
farmWarDeployer.started=클러스터 FarmWarDeployer가 시작되었습니다.
farmWarDeployer.stopped=클러스터 FarmWarDeployer가 중지되었습니다.
farmWarDeployer.undeployEnd=컨텍스트 [{0}]의 배치를 제거했습니다.
farmWarDeployer.undeployLocal=로컬 컨텍스트 [{0}]의 배치를 제거합니다.
farmWarDeployer.watchDir=클러스터 배치관리자가 변경사항들을 탐지하기 위해 [{0}]을(를) 감시합니다.
fileMessageFactory.cannotRead=팩토리가 쓰고 있는 중이라서, 메시지를 읽을 수 없습니다.
fileMessageFactory.cannotWrite=팩토리가 읽고 있는 중이라서, 메시지를 쓸 수 없습니다.
fileMessageFactory.closed=팩토리가 이미 닫혀 있습니다.
fileMessageFactory.deleteFail=[{0}]을(를) 삭제하지 못했습니다.
fileMessageFactory.duplicateMessage=중복된 메시지를 받았습니다. Sender의 제한 시간 초과 값이 너무 작게 설정되었나요? 컨텍스트: [{0}], 파일명: [{1}], 데이터: [{2}], 데이터 길이: [{3}]
fileNewFail=[{0}]을(를) 생성할 수 없습니다.
warWatcher.cantListWatchDir=WatchDir [{0}] 내의 파일 목록을 구할 수 없습니다. 해당 디렉토리가 존재하는지 그리고 읽기 권한이 있는지 점검하십시오.
warWatcher.checkWarResult=WarInfo.check()가 [{1}]을(를) 위해 [{0}]을(를) 반환했습니다.
warWatcher.checkingWar=WAR 파일 [{0}]을(를) 점검합니다.
warWatcher.checkingWars=[{0}] 내의 WAR들을 점검합니다.
warWatcher.listedFileDoesNotExist=[{0}]이(가) [{1}]에서 탐지되었으나, 존재하지 않습니다. [{1}]에 대한 디렉토리 접근 허가 설정을 점검해 보시겠습니까?

View File

@@ -0,0 +1,18 @@
# 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.
farmWarDeployer.modInstall=Установка веб приложения [{0}] из [{1}]
warWatcher.checkingWar=Проверяем WAR файл [{}]

View File

@@ -0,0 +1,38 @@
# 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.
farmWarDeployer.deleteFail=无法删除 [{0}]
farmWarDeployer.hostOnly=FarmWarDeployer 只有做为 host cluster 的子元素是才生效
farmWarDeployer.mbeanNameFail=无法为引擎[{0}]和主机[{1}]构造MBean对象名
farmWarDeployer.modInstall=从 [{1}] 安装 webapp [{0}]
farmWarDeployer.modInstallFail=无法安装 WAR 文件
farmWarDeployer.msgIoe=无法读取服务器场部署文件消息。
farmWarDeployer.msgRxDeploy=接收集群部署路径[{0}],战争[{1}]
farmWarDeployer.msgRxUndeploy=从路径[{0}]接收未部署群集
farmWarDeployer.removeFailLocal=从[{0}]本地移除失败
farmWarDeployer.removeFailRemote=本地从[{0}]删除失败其他经理有app在服务
farmWarDeployer.removeLocalFail=无法移除WAR文件
farmWarDeployer.renameFail=将 [{0}] 重命名为 [{1}] 失败
farmWarDeployer.sendFragment=将群集战争片段路径[{0}],战争[{1}]发送到[{2}]
farmWarDeployer.servicingDeploy=应用程序[{0}]正在服务。再次触摸WAR文件[{1}]
farmWarDeployer.servicingUndeploy=正在处理应用程序[{0}],无法从备份群集节点中删除它
farmWarDeployer.undeployEnd=从[{0}]取消部署完成。
farmWarDeployer.undeployLocal=不能部署本地上下文[{0}]
fileMessageFactory.deleteFail=无法删除 [{0}]
warWatcher.cantListWatchDir=无法列出WatchDir文件夹 [{0}] 中的文件:检查该路径是否为目录且应用具有读取权限。
warWatcher.checkWarResult=WarInfo.check() 为[{1}]返回[{0}]
warWatcher.checkingWar=检查 WAR 文件 [{0}]

View File

@@ -0,0 +1,69 @@
/*
* 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;
public class UndeployMessage implements ClusterMessage {
private static final long serialVersionUID = 2L;
private Member address;
private long timestamp;
private String uniqueId;
private final String contextName;
public UndeployMessage(Member address,
long timestamp,
String uniqueId,
String contextName) {
this.address = address;
this.timestamp= timestamp;
this.uniqueId = uniqueId;
this.contextName = contextName;
}
@Override
public Member getAddress() {
return address;
}
@Override
public void setAddress(Member address) {
this.address = address;
}
@Override
public long getTimestamp() {
return timestamp;
}
@Override
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@Override
public String getUniqueId() {
return uniqueId;
}
public String getContextName() {
return contextName;
}
}

View File

@@ -0,0 +1,225 @@
/*
* 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.Iterator;
import java.util.Map;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* The <b>WarWatcher </b> watches the deployDir for changes made to the
* directory (adding new WAR files-&gt;deploy or remove WAR files-&gt;undeploy)
* and notifies a listener of the changes made.
*
* @author Peter Rossbach
* @version 1.1
*/
public class WarWatcher {
/*--Static Variables----------------------------------------*/
private static final Log log = LogFactory.getLog(WarWatcher.class);
private static final StringManager sm = StringManager.getManager(WarWatcher.class);
/*--Instance Variables--------------------------------------*/
/**
* Directory to watch for war files
*/
protected final File watchDir;
/**
* Parent to be notified of changes
*/
protected final FileChangeListener listener;
/**
* Currently deployed files
*/
protected final Map<String, WarInfo> currentStatus = new HashMap<>();
/*--Constructor---------------------------------------------*/
public WarWatcher(FileChangeListener listener, File watchDir) {
this.listener = listener;
this.watchDir = watchDir;
}
/*--Logic---------------------------------------------------*/
/**
* check for modification and send notification to listener
*/
public void check() {
if (log.isDebugEnabled())
log.debug(sm.getString("warWatcher.checkingWars", watchDir));
File[] list = watchDir.listFiles(new WarFilter());
if (list == null) {
log.warn(sm.getString("warWatcher.cantListWatchDir",
watchDir));
list = new File[0];
}
//first make sure all the files are listed in our current status
for (int i = 0; i < list.length; i++) {
if(!list[i].exists())
log.warn(sm.getString("warWatcher.listedFileDoesNotExist",
list[i], watchDir));
addWarInfo(list[i]);
}
// Check all the status codes and update the FarmDeployer
for (Iterator<Map.Entry<String,WarInfo>> i =
currentStatus.entrySet().iterator(); i.hasNext();) {
Map.Entry<String,WarInfo> entry = i.next();
WarInfo info = entry.getValue();
if(log.isTraceEnabled())
log.trace(sm.getString("warWatcher.checkingWar",
info.getWar()));
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
i.remove();
}
if(log.isTraceEnabled())
log.trace(sm.getString("warWatcher.checkWarResult",
Integer.valueOf(check),
info.getWar()));
}
}
/**
* add cluster war to the watcher state
* @param warfile The WAR to add
*/
protected void addWarInfo(File warfile) {
WarInfo info = 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();
}
/*--Inner classes-------------------------------------------*/
/**
* File name filter for war files
*/
protected static class WarFilter implements java.io.FilenameFilter {
@Override
public boolean accept(File path, String name) {
if (name == null)
return false;
return name.endsWith(".war");
}
}
/**
* File information on existing WAR files
*/
protected static class WarInfo {
protected final File war;
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;
}
@Override
public int hashCode() {
return war.getAbsolutePath().hashCode();
}
@Override
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;
}
}
}

View File

@@ -0,0 +1,51 @@
<?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.
-->
<mbeans-descriptors>
<mbean
name="FarmWarDeployer"
className="org.apache.catalina.mbeans.ClassNameMBean"
description="Farm Deployer"
domain="Catalina"
group="Cluster"
type="org.apache.catalina.ha.deploy.FarmWarDeployer">
<attribute
name="deployDir"
description="Deployment directory."
type="java.lang.String"/>
<attribute
name="tempDir"
description="The temporaryDirectory to store binary data when downloading a war from the cluster"
type="java.lang.String"/>
<attribute
name="watchDir"
description="The directory where we watch for changes"
type="java.lang.String"/>
<attribute
name="watchEnabled"
description="Is watching enabled?"
type="boolean"/>
<attribute
name="processDeployFrequency"
description="Frequency of the Farm watchDir check."
type="int"/>
<attribute
name="maxValidTime"
description="The maximum valid time of FileMessageFactory."
type="int"/>
</mbean>
</mbeans-descriptors>

View File

@@ -0,0 +1,23 @@
<!--
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>

View File

@@ -0,0 +1,265 @@
/*
* 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.HashSet;
import java.util.Set;
import org.apache.catalina.DistributedManager;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.ClusterMessage;
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapOwner;
import org.apache.catalina.tribes.tipis.LazyReplicatedMap;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
*@version 1.0
*/
public class BackupManager extends ClusterManagerBase
implements MapOwner, DistributedManager {
private final Log log = LogFactory.getLog(BackupManager.class); // must not be static
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(BackupManager.class);
protected static final long DEFAULT_REPL_TIMEOUT = 15000;//15 seconds
/**
* The name of this manager
*/
protected String name;
/**
* Flag for how this map sends messages.
*/
private int mapSendOptions = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK|Channel.SEND_OPTIONS_USE_ACK;
/**
* Timeout for RPC messages.
*/
private long rpcTimeout = DEFAULT_REPL_TIMEOUT;
/**
* Flag for whether to terminate this map that failed to start.
*/
private boolean terminateOnStartFailure = false;
/**
* The timeout for a ping message in replication map.
*/
private long accessTimeout = 5000;
/**
* Constructor, just calls super()
*
*/
public BackupManager() {
super();
}
//******************************************************************************/
// ClusterManager Interface
//******************************************************************************/
@Override
public void messageDataReceived(ClusterMessage msg) {
}
@Override
public ClusterMessage requestCompleted(String sessionId) {
if (!getState().isAvailable()) return null;
LazyReplicatedMap<String,Session> map =
(LazyReplicatedMap<String,Session>)sessions;
map.replicate(sessionId,false);
return null;
}
//=========================================================================
// OVERRIDE THESE METHODS TO IMPLEMENT THE REPLICATION
//=========================================================================
@Override
public void objectMadePrimary(Object key, Object value) {
if (value instanceof DeltaSession) {
DeltaSession session = (DeltaSession)value;
synchronized (session) {
session.access();
session.setPrimarySession(true);
session.endAccess();
}
}
}
@Override
public Session createEmptySession() {
return new DeltaSession(this);
}
@Override
public String getName() {
return this.name;
}
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* 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 LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
super.startInternal();
try {
if (cluster == null) throw new LifecycleException(sm.getString("backupManager.noCluster", getName()));
LazyReplicatedMap<String,Session> map = new LazyReplicatedMap<>(
this, cluster.getChannel(), rpcTimeout, getMapName(),
getClassLoaders(), terminateOnStartFailure);
map.setChannelSendOptions(mapSendOptions);
map.setAccessTimeout(accessTimeout);
this.sessions = map;
} catch ( Exception x ) {
log.error(sm.getString("backupManager.startUnable", getName()),x);
throw new LifecycleException(sm.getString("backupManager.startFailed", getName()),x);
}
setState(LifecycleState.STARTING);
}
public String getMapName() {
String name = cluster.getManagerName(getName(),this)+"-"+"map";
if ( log.isDebugEnabled() ) log.debug("Backup manager, Setting map name to:"+name);
return name;
}
/**
* Stop this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* This will disconnect the cluster communication channel and stop the
* listener thread.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void stopInternal() throws LifecycleException {
if (log.isDebugEnabled())
log.debug(sm.getString("backupManager.stopped", getName()));
setState(LifecycleState.STOPPING);
if (sessions instanceof LazyReplicatedMap) {
LazyReplicatedMap<String,Session> map =
(LazyReplicatedMap<String,Session>)sessions;
map.breakdown();
}
super.stopInternal();
}
@Override
public void setName(String name) {
this.name = name;
}
public void setMapSendOptions(int mapSendOptions) {
this.mapSendOptions = mapSendOptions;
}
public int getMapSendOptions() {
return mapSendOptions;
}
public void setRpcTimeout(long rpcTimeout) {
this.rpcTimeout = rpcTimeout;
}
public long getRpcTimeout() {
return rpcTimeout;
}
public void setTerminateOnStartFailure(boolean terminateOnStartFailure) {
this.terminateOnStartFailure = terminateOnStartFailure;
}
public boolean isTerminateOnStartFailure() {
return terminateOnStartFailure;
}
public long getAccessTimeout() {
return accessTimeout;
}
public void setAccessTimeout(long accessTimeout) {
this.accessTimeout = accessTimeout;
}
@Override
public String[] getInvalidatedSessions() {
return new String[0];
}
@Override
public ClusterManager cloneFromTemplate() {
BackupManager result = new BackupManager();
clone(result);
result.mapSendOptions = mapSendOptions;
result.rpcTimeout = rpcTimeout;
result.terminateOnStartFailure = terminateOnStartFailure;
result.accessTimeout = accessTimeout;
return result;
}
@Override
public int getActiveSessionsFull() {
LazyReplicatedMap<String,Session> map =
(LazyReplicatedMap<String,Session>)sessions;
return map.sizeFull();
}
@Override
public Set<String> getSessionIdsFull() {
Set<String> sessionIds = new HashSet<>();
LazyReplicatedMap<String,Session> map =
(LazyReplicatedMap<String,Session>)sessions;
for (String id : map.keySetFull()) {
sessionIds.add(id);
}
return sessionIds;
}
}

View File

@@ -0,0 +1,220 @@
/*
* 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 org.apache.catalina.Cluster;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Loader;
import org.apache.catalina.SessionIdGenerator;
import org.apache.catalina.Valve;
import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.tcp.ReplicationValve;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.tribes.io.ReplicationStream;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.collections.SynchronizedStack;
public abstract class ClusterManagerBase extends ManagerBase implements ClusterManager {
private final Log log = LogFactory.getLog(ClusterManagerBase.class); // must not be static
/**
* A reference to the cluster
*/
protected CatalinaCluster cluster = null;
/**
* Should listeners be notified?
*/
private boolean notifyListenersOnReplication = true;
/**
* cached replication valve cluster container!
*/
private volatile ReplicationValve replicationValve = null ;
/**
* send all actions of session attributes.
*/
private boolean recordAllActions = false;
private SynchronizedStack<DeltaRequest> deltaRequestPool = new SynchronizedStack<>();
protected SynchronizedStack<DeltaRequest> getDeltaRequestPool() {
return deltaRequestPool;
}
@Override
public CatalinaCluster getCluster() {
return cluster;
}
@Override
public void setCluster(CatalinaCluster cluster) {
this.cluster = cluster;
}
@Override
public boolean isNotifyListenersOnReplication() {
return notifyListenersOnReplication;
}
public void setNotifyListenersOnReplication(boolean notifyListenersOnReplication) {
this.notifyListenersOnReplication = notifyListenersOnReplication;
}
public boolean isRecordAllActions() {
return recordAllActions;
}
public void setRecordAllActions(boolean recordAllActions) {
this.recordAllActions = recordAllActions;
}
public static ClassLoader[] getClassLoaders(Context context) {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
Loader loader = context.getLoader();
ClassLoader classLoader = null;
if (loader != null) {
classLoader = loader.getClassLoader();
}
if (classLoader == null) {
classLoader = tccl;
}
if (classLoader == tccl) {
return new ClassLoader[] {classLoader};
} else {
return new ClassLoader[] {classLoader, tccl};
}
}
public ClassLoader[] getClassLoaders() {
return getClassLoaders(getContext());
}
@Override
public ReplicationStream getReplicationStream(byte[] data) throws IOException {
return getReplicationStream(data,0,data.length);
}
@Override
public ReplicationStream getReplicationStream(byte[] data, int offset, int length) throws IOException {
ByteArrayInputStream fis = new ByteArrayInputStream(data, offset, length);
return new ReplicationStream(fis, getClassLoaders());
}
// ---------------------------------------------------- persistence handler
/**
* {@link org.apache.catalina.Manager} implementations that also implement
* {@link ClusterManager} do not support local session persistence.
*/
@Override
public void load() {
// NOOP
}
/**
* {@link org.apache.catalina.Manager} implementations that also implement
* {@link ClusterManager} do not support local session persistence.
*/
@Override
public void unload() {
// NOOP
}
protected void clone(ClusterManagerBase copy) {
copy.setName("Clone-from-" + getName());
copy.setMaxActiveSessions(getMaxActiveSessions());
copy.setProcessExpiresFrequency(getProcessExpiresFrequency());
copy.setNotifyListenersOnReplication(isNotifyListenersOnReplication());
copy.setSessionAttributeNameFilter(getSessionAttributeNameFilter());
copy.setSessionAttributeValueClassNameFilter(getSessionAttributeValueClassNameFilter());
copy.setWarnOnSessionAttributeFilterFailure(getWarnOnSessionAttributeFilterFailure());
copy.setSecureRandomClass(getSecureRandomClass());
copy.setSecureRandomProvider(getSecureRandomProvider());
copy.setSecureRandomAlgorithm(getSecureRandomAlgorithm());
if (getSessionIdGenerator() != null) {
try {
SessionIdGenerator copyIdGenerator = sessionIdGeneratorClass.getConstructor().newInstance();
copyIdGenerator.setSessionIdLength(getSessionIdGenerator().getSessionIdLength());
copyIdGenerator.setJvmRoute(getSessionIdGenerator().getJvmRoute());
copy.setSessionIdGenerator(copyIdGenerator);
} catch (ReflectiveOperationException e) {
// Ignore
}
}
copy.setRecordAllActions(isRecordAllActions());
}
/**
* Register cross context session at replication valve thread local
* @param session cross context session
*/
protected void registerSessionAtReplicationValve(DeltaSession session) {
if(replicationValve == null) {
CatalinaCluster cluster = getCluster() ;
if(cluster != null) {
Valve[] valves = 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
if(replicationValve != null) {
replicationValve.registerReplicationSession(session);
}
}
@Override
protected void startInternal() throws LifecycleException {
super.startInternal();
if (getCluster() == null) {
Cluster cluster = getContext().getCluster();
if (cluster instanceof CatalinaCluster) {
setCluster((CatalinaCluster)cluster);
}
}
if (cluster != null) cluster.registerManager(this);
}
@Override
protected void stopInternal() throws LifecycleException {
if (cluster != null) cluster.removeManager(this);
replicationValve = null;
super.stopInternal();
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.ClusterListener;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.ClusterMessage;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* Receive replicated SessionMessage form other cluster node.
* @author Peter Rossbach
*/
public class ClusterSessionListener extends ClusterListener {
private static final Log log =
LogFactory.getLog(ClusterSessionListener.class);
private static final StringManager sm = StringManager.getManager(ClusterSessionListener.class);
//--Constructor---------------------------------------------
public ClusterSessionListener() {
// NO-OP
}
//--Logic---------------------------------------------------
/**
* 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
*/
@Override
public void messageReceived(ClusterMessage myobj) {
if (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<String,ClusterManager> managers = cluster.getManagers() ;
if (ctxname == null) {
for (Map.Entry<String, ClusterManager> entry :
managers.entrySet()) {
if (entry.getValue() != null)
entry.getValue().messageDataReceived(msg);
else {
//this happens a lot before the system has started
// up
if (log.isDebugEnabled())
log.debug(sm.getString("clusterSessionListener.noManager", entry.getKey()));
}
}
} else {
ClusterManager mgr = managers.get(ctxname);
if (mgr != null) {
mgr.messageDataReceived(msg);
} else {
if (log.isWarnEnabled())
log.warn(sm.getString("clusterSessionListener.noManager", ctxname));
// A no context manager message is replied in order to avoid
// timeout of GET_ALL_SESSIONS sync phase.
if (msg.getEventType() == SessionMessage.EVT_GET_ALL_SESSIONS) {
SessionMessage replymsg = new SessionMessageImpl(ctxname,
SessionMessage.EVT_ALL_SESSION_NOCONTEXTMANAGER,
null, "NO-CONTEXT-MANAGER","NO-CONTEXT-MANAGER-" + ctxname);
cluster.send(replymsg, msg.getAddress());
}
}
}
}
}
/**
* 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.
*/
@Override
public boolean accept(ClusterMessage msg) {
return msg instanceof SessionMessage;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
/*
* 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 invocations of methods
* on the actual session.
* This class is NOT thread safe. One DeltaRequest per session
* @version 1.0
*/
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.Principal;
import java.util.LinkedList;
import org.apache.catalina.SessionListener;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
public class DeltaRequest implements Externalizable {
public static final Log log = LogFactory.getLog(DeltaRequest.class);
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(DeltaRequest.class);
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 TYPE_AUTHTYPE = 4;
public static final int TYPE_LISTENER = 5;
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__";
public static final String NAME_AUTHTYPE = "__SET__AUTHTYPE__";
public static final String NAME_LISTENER = "__SET__LISTENER__";
private String sessionId;
private LinkedList<AttributeInfo> actions = new LinkedList<>();
private final LinkedList<AttributeInfo> 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) {
addAction(TYPE_ATTRIBUTE, ACTION_REMOVE, name, null);
}
public void setMaxInactiveInterval(int interval) {
addAction(TYPE_MAXINTERVAL, ACTION_SET, NAME_MAXINTERVAL, Integer.valueOf(interval));
}
/**
* 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;
GenericPrincipal gp = null;
if (p != null) {
if (p instanceof GenericPrincipal) {
gp = (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, gp);
}
public void setNew(boolean n) {
int action = ACTION_SET;
addAction(TYPE_ISNEW,action,NAME_ISNEW,Boolean.valueOf(n));
}
public void setAuthType(String authType) {
int action = (authType==null)?ACTION_REMOVE:ACTION_SET;
addAction(TYPE_AUTHTYPE,action,NAME_AUTHTYPE, authType);
}
public void addSessionListener(SessionListener listener) {
addAction(TYPE_LISTENER, ACTION_SET, NAME_LISTENER ,listener);
}
public void removeSessionListener(SessionListener listener) {
addAction(TYPE_LISTENER, ACTION_REMOVE, NAME_LISTENER ,listener);
}
protected void addAction(int type,
int action,
String name,
Object value) {
AttributeInfo info = null;
if ( this.actionPool.size() > 0 ) {
try {
info = actionPool.removeFirst();
}catch ( Exception x ) {
log.error(sm.getString("deltaRequest.removeUnable"),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, boolean notifyListeners) {
if ( !this.sessionId.equals( session.getId() ) )
throw new java.lang.IllegalArgumentException(sm.getString("deltaRequest.ssid.mismatch"));
session.access();
for ( int i=0; i<actions.size(); i++ ) {
AttributeInfo info = 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 TYPE_ISNEW:
if ( log.isTraceEnabled() ) log.trace("Session.setNew('"+info.getValue()+"')");
session.setNew(((Boolean)info.getValue()).booleanValue(),false);
break;
case TYPE_MAXINTERVAL:
if ( log.isTraceEnabled() ) log.trace("Session.setMaxInactiveInterval('"+info.getValue()+"')");
session.setMaxInactiveInterval(((Integer)info.getValue()).intValue(),false);
break;
case TYPE_PRINCIPAL:
Principal p = null;
if (info.getAction() == ACTION_SET) {
p = (Principal) info.getValue();
}
session.setPrincipal(p,false);
break;
case TYPE_AUTHTYPE:
String authType = null;
if ( info.getAction() == ACTION_SET ) {
authType = (String)info.getValue();
}
session.setAuthType(authType,false);
break;
case TYPE_LISTENER:
SessionListener listener = (SessionListener) info.getValue();
if (info.getAction() == ACTION_SET) {
session.addSessionListener(listener,false);
} else {
session.removeSessionListener(listener,false);
}
break;
default :
throw new java.lang.IllegalArgumentException(sm.getString("deltaRequest.invalidAttributeInfoType", info));
}//switch
}//for
session.endAccess();
reset();
}
public void reset() {
while ( actions.size() > 0 ) {
try {
AttributeInfo info = actions.removeFirst();
info.recycle();
actionPool.addLast(info);
}catch ( Exception x ) {
log.error(sm.getString("deltaRequest.removeUnable"),x);
}
}
actions.clear();
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
if ( sessionId == null ) {
new Exception(sm.getString("deltaRequest.ssid.null")).fillInStackTrace().printStackTrace();
}
}
public int getSize() {
return actions.size();
}
public void clear() {
actions.clear();
actionPool.clear();
}
@Override
public 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 = actionPool.removeFirst();
} catch ( Exception x ) {
log.error(sm.getString("deltaRequest.removeUnable"),x);
info = new AttributeInfo();
}
}
else {
info = new AttributeInfo();
}
info.readExternal(in);
actions.addLast(info);
}//for
}
@Override
public 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 = actions.get(i);
info.writeExternal(out);
}
}
/**
* serialize DeltaRequest
* @see DeltaRequest#writeExternal(java.io.ObjectOutput)
*
* @return serialized delta request
* @throws IOException IO error serializing
*/
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() {
this(-1, -1, null, null);
}
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;
}
@Override
public int hashCode() {
return name.hashCode();
}
public String getName() {
return name;
}
public void recycle() {
name = null;
value = null;
type=-1;
action=-1;
}
@Override
public boolean equals(Object o) {
if ( ! (o instanceof AttributeInfo ) ) return false;
AttributeInfo other = (AttributeInfo)o;
return other.getName().equals(this.getName());
}
@Override
public 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();
}
@Override
public 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());
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder("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();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,408 @@
/*
* 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 org.apache.catalina.Cluster;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterManager;
import org.apache.catalina.ha.ClusterValve;
import org.apache.catalina.session.ManagerBase;
import org.apache.catalina.session.PersistentManager;
import org.apache.catalina.valves.ValveBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* Valve to handle Tomcat jvmRoute takeover using mod_jk module after node
* failure. After a node crashes, subsequent requests go to other cluster nodes.
* That incurs a drop in performance. When this Valve is enabled on a backup
* node and sees a request, which was intended for another (thus failed) node,
* it will rewrite the cookie jsessionid information to use the route to this
* backup cluster node, that answered the request. After the response is
* delivered to the client, all subsequent client requests will go directly to
* the backup node. The change of sessionid is also sent to all other cluster
* nodes. After all that, the session stickiness will work directly to the
* backup node and the traffic will not go back to the failed node after it is
* restarted!
*
* <p>
* Add this Valve to your cluster definition at conf/server.xml .
*
* <pre>
* &lt;Cluster&gt;
* &lt;Valve className=&quot;org.apache.catalina.ha.session.JvmRouteBinderValve&quot; /&gt;
* &lt;/Cluster&gt;
* </pre>
*
* <em>A Trick:</em><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 sessions are migrated.
*
* @author Peter Rossbach
*/
public class JvmRouteBinderValve extends ValveBase implements ClusterValve {
/*--Static Variables----------------------------------------*/
public static final Log log = LogFactory.getLog(JvmRouteBinderValve.class);
//------------------------------------------------------ Constructor
public JvmRouteBinderValve() {
super(true);
}
/*--Instance Variables--------------------------------------*/
/**
* the cluster
*/
protected CatalinaCluster cluster;
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(JvmRouteBinderValve.class);
/**
* enabled this component
*/
protected boolean enabled = true;
/**
* number of session that no at this tomcat instance hosted
*/
protected long numberOfSessions = 0;
protected String sessionIdAttribute = "org.apache.catalina.ha.session.JvmRouteOrignalSessionID";
/*--Logic---------------------------------------------------*/
/**
* set session id attribute to failed node for request.
*
* @return Returns the sessionIdAttribute.
*/
public String getSessionIdAttribute() {
return sessionIdAttribute;
}
/**
* get name of failed request 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
*/
@Override
public void invoke(Request request, Response response) throws IOException,
ServletException {
if (getEnabled() &&
request.getContext() != null &&
request.getContext().getDistributable() &&
!request.isAsyncDispatching()) {
// 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() != null
&& getCluster().getManager(((ClusterManager)manager).getName()) != null)
||
(manager instanceof PersistentManager))) {
handlePossibleTurnover(request);
}
}
// Pass this request on to the next valve in our pipeline
getNext().invoke(request, response);
}
/**
* handle possible session turn over.
*
* @see JvmRouteBinderValve#handleJvmRoute(Request, String, String)
* @param request current request
*/
protected void handlePossibleTurnover(Request request) {
String sessionID = request.getRequestedSessionId() ;
if (sessionID != null) {
long t1 = System.currentTimeMillis();
String jvmRoute = getLocalJvmRoute(request);
if (jvmRoute == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("jvmRoute.missingJvmRouteAttribute"));
}
return;
}
handleJvmRoute( request, sessionID, jvmRoute);
if (log.isDebugEnabled()) {
long t2 = System.currentTimeMillis();
long time = t2 - t1;
log.debug(sm.getString("jvmRoute.turnoverInfo", Long.valueOf(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 ClusterManager
*
* @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", request.getContext().getName()));
}
}
return manager;
}
/**
* @return Returns the cluster.
*/
@Override
public CatalinaCluster getCluster() {
return cluster;
}
/**
* @param cluster The cluster to set.
*/
@Override
public void setCluster(CatalinaCluster cluster) {
this.cluster = cluster;
}
/**
* Handle jvmRoute stickiness after tomcat instance failed. After this
* correction a new Cookie send to client with new jvmRoute and the
* SessionID change propagate to the other cluster nodes.
*
* @param request current request
* @param sessionId
* request SessionID from Cookie
* @param localJvmRoute
* local jvmRoute
*/
protected void handleJvmRoute(
Request request, 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));
}
Session catalinaSession = null;
try {
catalinaSession = getManager(request).findSession(sessionId);
} catch (IOException e) {
// Hups!
}
String id = sessionId.substring(0, index);
String newSessionID = id + "." + localJvmRoute;
// OK - turnover the session and inform other cluster nodes
if (catalinaSession != null) {
changeSessionID(request, sessionId, newSessionID,
catalinaSession);
numberOfSessions++;
} else {
try {
catalinaSession = getManager(request).findSession(newSessionID);
} catch (IOException e) {
// Hups!
}
if (catalinaSession != null) {
// session is rewrite at other request, rewrite this also
changeRequestSessionID(request, sessionId, newSessionID);
} 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 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, String sessionId,
String newSessionID, Session catalinaSession) {
fireLifecycleEvent("Before session migration", catalinaSession);
catalinaSession.getManager().changeSessionId(catalinaSession, newSessionID);
changeRequestSessionID(request, sessionId, newSessionID);
fireLifecycleEvent("After session migration", catalinaSession);
if (log.isDebugEnabled()) {
log.debug(sm.getString("jvmRoute.changeSession", sessionId,
newSessionID));
}
}
/**
* Change Request Session id
* @param request current request
* @param sessionId
* original session id
* @param newSessionID
* new session id for node migration
*/
protected void changeRequestSessionID(Request request, String sessionId, String newSessionID) {
request.changeSessionId(newSessionID);
// set original 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);
}
}
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
if (cluster == null) {
Cluster containerCluster = getContainer().getCluster();
if (containerCluster instanceof CatalinaCluster) {
setCluster((CatalinaCluster)containerCluster);
}
}
if (log.isInfoEnabled()) {
log.info(sm.getString("jvmRoute.valve.started"));
if (cluster == null) {
log.info(sm.getString("jvmRoute.noCluster"));
}
}
super.startInternal();
}
/**
* Stop this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void stopInternal() throws LifecycleException {
super.stopInternal();
cluster = null;
numberOfSessions = 0;
if (log.isInfoEnabled()) {
log.info(sm.getString("jvmRoute.valve.stopped"));
}
}
}

View File

@@ -0,0 +1,90 @@
# 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.
backupManager.noCluster=no cluster associated with this context: [{0}]
backupManager.startFailed=Failed to start BackupManager: [{0}]
backupManager.startUnable=Unable to start BackupManager: [{0}]
backupManager.stopped=Manager [{0}] is stopping
clusterSessionListener.noManager=Context manager doesn''t exist:[{0}]
deltaManager.createMessage.access=Manager [{0}]: create session access message for session [{1}]
deltaManager.createMessage.accessChangePrimary=Manager [{0}]: create change primary node message for session [{1}]
deltaManager.createMessage.allSessionData=Manager [{0}] sent all session data.
deltaManager.createMessage.allSessionTransfered=Manager [{0}] sent all session data transferred
deltaManager.createMessage.delta=Manager [{0}]: create delta request message for session [{1}]
deltaManager.createMessage.expire=Manager [{0}]: create session expire message for session [{1}]
deltaManager.createMessage.unableCreateDeltaRequest=Unable to serialize delta request for sessionid [{0}]
deltaManager.createSession.newSession=Created a new DeltaSession with Id [{0}] Total count=[{1}]
deltaManager.dropMessage=Manager [{0}]: Drop message [{1}] inside GET_ALL_SESSIONS sync phase start date [{2}] message date [{3}]
deltaManager.expireSessions=Manager [{0}] expiring sessions upon shutdown
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.managerLoad=Exception loading sessions from persistent storage
deltaManager.noCluster=Starting... no cluster associated with this context: [{0}]
deltaManager.noContextManager=Manager [{0}]: In reply to the ''Get all session data'' message sent at [{1}], a ''No matching context manager'' message was received after [{2}] ms.
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 sent at [{1}] received, timing out after [{2}] ms.
deltaManager.receiveMessage.accessed=Manager [{0}]: received session accessed message for session [{1}]
deltaManager.receiveMessage.allSessionDataAfter=Manager [{0}]: all session state deserialized
deltaManager.receiveMessage.allSessionDataBegin=Manager [{0}]: received all session state data
deltaManager.receiveMessage.createNewSession=Manager [{0}]: received session created message for session [{1}]
deltaManager.receiveMessage.delta=Manager [{0}]: received session delta message for session [{1}]
deltaManager.receiveMessage.delta.unknown=Manager [{0}]: received session delta for unknown session [{1}]
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 expired message for session [{1}]
deltaManager.receiveMessage.noContextManager=Manager [{0}] received from node [{1}:{2}] no context manager.
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.registerCluster=Register manager [{0}] to cluster element [{1}] with name [{2}]
deltaManager.sendMessage.newSession=Manager [{0}] send new session [{1}]
deltaManager.sessionReceived=Manager [{0}]; session state sent at [{1}] received in [{2}] ms.
deltaManager.startClustering=Starting clustering manager at [{0}]
deltaManager.stopped=Manager [{0}] is stopping
deltaManager.unableSerializeSessionID=Unable to serialize sessionID [{0}]
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 [{2}] seconds.
deltaRequest.invalidAttributeInfoType=Invalid attribute info type=[{0}]
deltaRequest.removeUnable=Unable to remove element:
deltaRequest.showPrincipal=Principal [{0}] is set to session [{1}]
deltaRequest.ssid.mismatch=Session id mismatch, not executing the delta request
deltaRequest.ssid.null=Session Id is null for setSessionId
deltaRequest.wrongPrincipalClass=ClusterManager only support GenericPrincipal. Your realm used principal class [{0}].
deltaSession.notifying=Notifying cluster of session expiration: primary=[{0}], sessionId [{1}]
deltaSession.readSession=readObject() loading session [{0}]
deltaSession.writeSession=writeObject() storing session [{0}]
jvmRoute.cannotFindSession=Cannot find session [{0}]
jvmRoute.changeSession=Changed session from [{0}] to [{1}]
jvmRoute.failover=Detected a failover with different jvmRoute - orginal route: [{0}] new one: [{1}] at session id [{2}]
jvmRoute.foundManager=Found Cluster Manager [{0}] at [{1}]
jvmRoute.missingJvmRouteAttribute=No engine jvmRoute attribute configured!
jvmRoute.noCluster=The JvmRouterBinderValve is configured, but clustering is not being used. Fail over will still work, providing a PersistentManager is used.
jvmRoute.notFoundManager=Not found Cluster Manager at [{0}]
jvmRoute.set.orignalsessionid=Set Orginal Session id at request attribute [{0}] value: [{1}]
jvmRoute.turnoverInfo=Turnover Check time [{0}] msec
jvmRoute.valve.started=JvmRouteBinderValve started
jvmRoute.valve.stopped=JvmRouteBinderValve stopped
standardSession.notSerializable=Cannot serialize session attribute [{0}] for session [{1}]
standardSession.removeAttribute.ise=removeAttribute: Session already invalidated
standardSession.setAttribute.namenull=setAttribute: name parameter cannot be null

View File

@@ -0,0 +1,26 @@
# 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.foundMasterMember=Für den Kontext [{0}] wurde der Replikationsmaster [{1}] gefunden
deltaManager.noContextManager=Manager [{0}]: Als Antwort auf eine um [{1}] gesendete "Hole alle Sitzungsdaten"-Nachricht, wurde nach [{2}] ms eine "Kein passender Context-Manager"-Nachricht empfangen.
deltaManager.receiveMessage.allSessionDataBegin=Manager [{0}]: alle Sitzungsdaten empfangen
deltaManager.receiveMessage.delta.unknown=Manager [{0}]: Habe Session-Delta für unbekannte Session [{1}] empfangen
deltaManager.receiveMessage.unloadingBegin=Manager [{0}]: Beginne die Sessions zu entladen
deltaManager.unloading.ioe=IOExceptio während des Speichers der Persisted Sessions: [{0}]
deltaRequest.removeUnable=Kann Element nicht entfernen:
deltaRequest.wrongPrincipalClass=Der ClusterManager unterstützt nur GenericPrincipal. Ihr Realm benutzt die Principal Klasse [{0}].
jvmRoute.valve.started=JvmRouteBinderValve gestartet

View File

@@ -0,0 +1,81 @@
# 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.
backupManager.startUnable=Imposible de iniciar BackupManager: [{0}]\n
deltaManager.createMessage.access=Gestor [{0}]: creado mensaje de sesión [{1}] acceso.
deltaManager.createMessage.accessChangePrimary=Gestor [{0}]: creado mensaje de sesión [{1}] acceso para cambiar el primario.
deltaManager.createMessage.allSessionData=Gestor [{0}] envía todos los datos de sesión.
deltaManager.createMessage.allSessionTransfered=Gestor [{0}] envía todos los datos de sesión transferidos
deltaManager.createMessage.delta=Gestor [{0}]: crea mensaje de sesión [{1}] de requerimiento delta.
deltaManager.createMessage.expire=Gestor [{0}]: crea mensaje de sesión [{1}] de expiración.
deltaManager.createMessage.unableCreateDeltaRequest=No puedo serializar requerimiento delta para la id de sesión [{0}]
deltaManager.createSession.newSession=Creada una DeltaSession con Id [{0}] Total contador=[{1}]
deltaManager.dropMessage=Gestor [{0}]: Quita mensaje [{1}] dentro de fase sincronizada GET_ALL_SESSIONS fecha inicio [{2}] fecha mensaje [{3}]
deltaManager.expireSessions=Gestor [{0}] expirando sesiones al apagar
deltaManager.foundMasterMember=Hallado para contexto [{0}] el miembro maestro de réplica [{1}]
deltaManager.loading.cnfe=ClassNotFoundException al cargar sesiones persistentes: [{0}]
deltaManager.loading.existing.session=sobrecarga en sesión existente [{0}]
deltaManager.loading.ioe=IOException al cargar sesiones persistentes: [{0}]
deltaManager.managerLoad=Excepción cargando sesiones desde almacenaje persistente
deltaManager.noCluster=Arrancando... no hay clúster asociado con este contexto: [{0}]
deltaManager.noContextManager=Manejador [{0}]: En respuesta al mensaje ''Tomar todos los datos de sesión (Get all session data)'' enviado a [{1}], recibión el mensaje ''El manejador no machea con ningún contexto (No matching context manager)'' luego de [{2}] ms.\n
deltaManager.noMasterMember=Arrancando... sin otro miembro para el contexto [{0}] en dominio [{1}]
deltaManager.noMembers=Gestor [{0}]: saltando estado de transferencia. No hay miembros activos en grupo de clúster.
deltaManager.noSessionState=Gestor [{0}]: No se ha recibido estado de sesión a las [{1}], agotando tiempo tras [{2}] ms.
deltaManager.receiveMessage.accessed=Gestor [{0}]: accedida sesión [{1}] recibida.
deltaManager.receiveMessage.allSessionDataAfter=Gestor [{0}]: estado de sesión deserializado
deltaManager.receiveMessage.allSessionDataBegin=Gestor [{0}]: recibidos datos de estado de sesión
deltaManager.receiveMessage.createNewSession=Gestor [{0}]: creada sesión [{1}] recibida.
deltaManager.receiveMessage.delta=Gestor [{0}]: delta sesión [{1}] recibida.
deltaManager.receiveMessage.delta.unknown=Manejador [{0}]: recibió una diferencia de sesión para una sesión desconocida [{1}]
deltaManager.receiveMessage.error=Gestor [{0}]: No puedo recibir mensaje a través del canal TCP
deltaManager.receiveMessage.eventType=Gestor [{0}]: recibido SessionMessage de tipo=[{1}] desde [{2}]
deltaManager.receiveMessage.expired=Gestor [{0}]: expirada sesión [{1}] recibida.
deltaManager.receiveMessage.transfercomplete=Gestor [{0}] recibido desde nodo [{1}:{2}] estado de sesión transferido.
deltaManager.receiveMessage.unloadingAfter=Gestor [{0}]: completada la descarga de sesiones
deltaManager.receiveMessage.unloadingBegin=Gestor [{0}]: iniciada descarga de sesiones
deltaManager.registerCluster=Registrar gestor [{0}] a elemento de clúster [{1}] con nombre [{2}]
deltaManager.sendMessage.newSession=El gestor [{0}] envía nueva sesión [{1}]
deltaManager.sessionReceived=Gestor [{0}]; estado de sesión enviado a las [{1}] recibido en [{2}] ms.
deltaManager.startClustering=Iniciando gestor de clúster a las [{0}]
deltaManager.stopped=El gestor [{0}] se está parando
deltaManager.unableSerializeSessionID=No puedo seriallizar la ID de sesión [{0}]
deltaManager.unloading.ioe=IOException al grabar sesiones persistentes: [{0}]
deltaManager.waitForSessionState=Gestor [{0}], requiriendo estado de sesión desde [{1}]. Esta operación se agotará si no se recibe estado de sesión dentro de [{2}] segundos.
deltaRequest.removeUnable=Imposible eliminar elemento:
deltaRequest.showPrincipal=El Principal [{0}] está puesto a sesión [{1}]
deltaRequest.wrongPrincipalClass=DeltaManager sólo soporta GenericPrincipal. Tu reino utilizó clase principal [{0}].
deltaSession.notifying=Notificando clúster de expiración primaria=[{0}] sessionId [{1}]
deltaSession.readSession=readObject() cargando sesión [{0}]
deltaSession.writeSession=writeObject() guardando sesión [{0}]
jvmRoute.cannotFindSession=No puedo hallar sesión [{0}]
jvmRoute.changeSession=Cambiada sesión desde [{0}] a [{1}]
jvmRoute.failover=Detectada una caída con diferente jvmRoute - ruta original: [{0}] nueva: [{1}] en id de sesión [{2}]
jvmRoute.foundManager=Hallado Clúster DeltaManager [{0}] en [{1}]
jvmRoute.missingJvmRouteAttribute=¡No se ha configurado atributo de motor jvmRoute!
jvmRoute.noCluster=La válvula JvmRouterBinderValve se encuentra configurada, pero no se usa el clúster. Aún funcionará la tolerancia a fallos, siempre que se esté usando PersistentManager.
jvmRoute.notFoundManager=No hallado Clúster DeltaManager [{0}] en [{1}]
jvmRoute.set.orignalsessionid=Puesta id Orginal de Sesión en atributo de requerimiento [{0}] valor: [{1}]
jvmRoute.turnoverInfo=Ajustado tiempo de Chequeo a [{0}] mseg
jvmRoute.valve.started=JvmRouteBinderValve arrancada
jvmRoute.valve.stopped=JvmRouteBinderValve parada
standardSession.notSerializable=No puedo serializar atributo de sesión [{0}] para sesión [{1}]
standardSession.removeAttribute.ise=removeAttribute: Sesión ya invalidada
standardSession.setAttribute.namenull=setAttribute: parámetro de nombre no puede ser nulo

View File

@@ -0,0 +1,90 @@
# 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.
backupManager.noCluster=pas de groupe (cluster) associé à ce contexte: [{0}]
backupManager.startFailed=Impossible de démarrer le BackupManager: [{0}]
backupManager.startUnable=Impossible de démarrer le BackupManager: [{0}]
backupManager.stopped=Le gestionnaire de session [{0}] s''est arrêté
clusterSessionListener.noManager=Le gestionnaire de session du contexte n''existe pas: [{0}]
deltaManager.createMessage.access=Gestionnaire de session [{0}]: création du message de session [{1}] d''accès
deltaManager.createMessage.accessChangePrimary=Gestionnaire de session [{0}] : création du message de session [{1}] accès pour changer le primaire
deltaManager.createMessage.allSessionData=Gestionnaire de session [{0}] envoyé toutes les données de session
deltaManager.createMessage.allSessionTransfered=Gestionnaire de session [{0}] envoi du message signalant le transfert de toutes les données de session
deltaManager.createMessage.delta=Gestionnaire de session [{0}]: création du message [{0}] de requête delta
deltaManager.createMessage.expire=Gestionnaire de session [{0}]: création du message [{1}] d''expiration de session
deltaManager.createMessage.unableCreateDeltaRequest=Impossible de sérialiser la requête delta pour l''id de session [{0}]
deltaManager.createSession.newSession=Crée une DeltaSession avec Id [{0}] Nombre total=[{1}]
deltaManager.dropMessage=Gestionnaire de session [{0}] : Abandon du message [{1}] dans GET_ALL_SESSIONS début de la phase de sync [{2}] date du message [{3}]
deltaManager.expireSessions=Gestionnaire de session [{0}] expiration des sessions lors de l''arrêt
deltaManager.foundMasterMember=Le membre maître [{1}] a été trouvé pour la réplication du contexte [{0}]
deltaManager.loading.cnfe=Exception ClassNotFoundException lors du chargement des sessions persistantes : [{0}]
deltaManager.loading.existing.session=la session existante [{0}] est surchargée
deltaManager.loading.ioe=IOException lors du chargement des session persistées: [{0}]
deltaManager.managerLoad=Exception lors du chargement des sessions depuis le stockage persistant
deltaManager.noCluster=Démarrage, pas de cluster associé à ce contexte [{0}]
deltaManager.noContextManager=Gestionnaire de session [{0}]: En réponse à l''envoi d''un message demandant toutes les données des sessions à [{0}], un message indiquant l''absence d''un gestionnaire de sessions correspondant à été reçu au bout de [{2}] ms
deltaManager.noMasterMember=Démarrage sans autre membre pour le contexte [{0}] du domaine [{1}]
deltaManager.noMembers=Gestionnaire de session [{0}] : pas de transfert d''état, il n''y a pas de membres actifs dans le cluster
deltaManager.noSessionState=Gestionnaire de session [{0}] : pas de statut de session envoyé à [{1}] reçu, délai d''attente maximum de [{2}] ms.
deltaManager.receiveMessage.accessed=Gestionnaire de session [{0}] : reçu un accès à la session [{1}]
deltaManager.receiveMessage.allSessionDataAfter=Gestionnaire de session [{0}] : l''état de la session a été désérialisé
deltaManager.receiveMessage.allSessionDataBegin=Gestionnaire de session [{0}] : : reçu les données d''état des sessions
deltaManager.receiveMessage.createNewSession=Gestionnaire de session [{0}] : reçu la création de la session [{1}]
deltaManager.receiveMessage.delta=Gestionnaire de session [{0}] : reçu le delta de session [{1}]
deltaManager.receiveMessage.delta.unknown=Gestionnaire de session [{0}] : reçu un delta pour une session inconnue [{1}]
deltaManager.receiveMessage.error=Gestionnaire de session [{0}] : impossible de recevoir un message par le canal TCP
deltaManager.receiveMessage.eventType=Gestionnaire de session [{0}] : recu un SessionMessage de type=[{1}] de [{2}]
deltaManager.receiveMessage.expired=Gestionnaire de session [{0}] : reçu l''expiration de la session [{1}]
deltaManager.receiveMessage.noContextManager=Gestionnaire de session [{0}] a reçu d''un nœud [{1}:{2}] sans gestionnaire de contexte
deltaManager.receiveMessage.transfercomplete=Gestionnaire de session [{0}] reçu du nœud [{1}:{2}] l''état de la session a été transféré
deltaManager.receiveMessage.unloadingAfter=Gestionnaire de session [{0}] : fin du déchargement des sessions
deltaManager.receiveMessage.unloadingBegin=Gestionnaire de session [{0}] : début du déchargement des sessions
deltaManager.registerCluster=Enregistrement du gestionnaire [{0}] dans l''élément du cluster [{1}] avec le nom [{2}]
deltaManager.sendMessage.newSession=Gestionnaire de session [{0}] : envoi de la nouvelle session [{1}]
deltaManager.sessionReceived=Gestionnaire de session [{0}]: l''état de session envoyé à [{0}] a été reçu en [{2}] ms
deltaManager.startClustering=Démarrage du gestionnaire du cluster à [{0}]
deltaManager.stopped=Le gestionnaire de session [{0}] s''arrête
deltaManager.unableSerializeSessionID=Impossible de sérialiser le sessionID [{0}]
deltaManager.unloading.ioe=IOException lors de la sauvegarde des sessions persistantes: [{0}]
deltaManager.waitForSessionState=Gestionnaire de session [{0}], demande de l''état de session depuis [{1}], cette opération fera un timeout si l''état de la session n''a pas été reçu en moins de [{2}] secondes
deltaRequest.invalidAttributeInfoType=Info d''attribut invalide = [{0}]
deltaRequest.removeUnable=N'a pas pu enlever l'élément:
deltaRequest.showPrincipal=Le principal [{0}] est associé à la session [{1}]
deltaRequest.ssid.mismatch=L'id de session ne correspond pas, la requête delta ne sera pas exécutée
deltaRequest.ssid.null=L'id de session est null pour setSessionId
deltaRequest.wrongPrincipalClass=Un ClusterManager n''accepte que des GenericPrincipal. Votre realm a utilisé la classe de "principal" [{0}]
deltaSession.notifying=Notification du cluster de l''expiration de la session: primaire=[{0}] sessionId [{1}]
deltaSession.readSession=readObject() charge la session [{0}]
deltaSession.writeSession=writeObject() stocke la session [{0}]
jvmRoute.cannotFindSession=Impossible de trouver la session [{0}]
jvmRoute.changeSession=Changé la session de [{0}] vers [{1}]
jvmRoute.failover=Un changement de serveur a été détecté avec une jvmRoute différente, route originale: [{0}] nouvelle: [{1}] pour l''id de session [{2}]
jvmRoute.foundManager=Trouvé le gestionnaire de session du cluster [{0}] à [{1}]
jvmRoute.missingJvmRouteAttribute=Pas d'attribut jvmRoute configuré sur le moteur
jvmRoute.noCluster=La JvmRouterBinderValve est configurée mais le cluster n'est pas activé, la bascule vers un autre serveur fonctionnera tout de même à condition qu'un PersistentManager soit utilisé
jvmRoute.notFoundManager=Gestionnaire de cluster ("Cluster Manager") non trouvé à [{0}]
jvmRoute.set.orignalsessionid=Fixe l''id de session d''origine dans l''attribut de requête [{0}] valeur: [{1}]
jvmRoute.turnoverInfo=Temps de vérification de turnover [{0}] ms
jvmRoute.valve.started=La JvmRouteBinderValve a démarrée
jvmRoute.valve.stopped=JvmRouteBinderValve s'est arrêté
standardSession.notSerializable=Impossible de sérialiser l''attribut de session [{0}] pour la session [{1}]
standardSession.removeAttribute.ise=removeAttribute : session déjà invalidée
standardSession.setAttribute.namenull=setAttribute: le paramètre nom ne peut pas être null

View File

@@ -0,0 +1,90 @@
# 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.
backupManager.noCluster=コンテキスト: [{0}]にクラスターが関連付けられていません。
backupManager.startFailed=BackupManager: [{0}]の起動に失敗しました。
backupManager.startUnable=BackupManager を開始できません。: [{0}]
backupManager.stopped=Manager [{0}] を停止します。
clusterSessionListener.noManager=Context Managerが存在しません。
deltaManager.createMessage.access=Manager [{0}]: セッション [{1}] へのアクセスメッセージを作成します。
deltaManager.createMessage.accessChangePrimary=Manager[{0}]: セッション [{1}] でプライマリノード変更メッセージを作成しました。
deltaManager.createMessage.allSessionData=Manager [{0}] はすべてのセッションデータを送信しました。
deltaManager.createMessage.allSessionTransfered=Manager [{0}]はすべてのセッションデータ転送を送信しました。
deltaManager.createMessage.delta=Manager [{0}]:セッション[{1}]のデルタリクエストメッセージを作成します
deltaManager.createMessage.expire=Manager [{0}]:セッション[{1}]のセッションの期限切れメッセージを作成します。
deltaManager.createMessage.unableCreateDeltaRequest=セッションID [{0}]のDeltaRequestシリアライズできません。
deltaManager.createSession.newSession=DeltaSession (ID は [{0}]) を作成しました。総数は [{1}] です。
deltaManager.dropMessage=Manager [{0}]GET_ALL_SESSIONS同期フェーズの開始日[{2}] メッセージの日付[{3}]内のメッセージ[{1}]をドロップします。
deltaManager.expireSessions=シャットダウン時にManager[{0}]はセッションを満了します。
deltaManager.foundMasterMember=コンテキスト [{0}] のレプリケーションマスターメンバー [{1}] を発見しました。
deltaManager.loading.cnfe=永続セッションのロード中にClassNotFoundExceptionが発生しました[{0}]
deltaManager.loading.existing.session=既存セッション[{0}]のオーバーロード
deltaManager.loading.ioe=永続化セッションの読み込み中に IOException が発生しました: [{0}]
deltaManager.managerLoad=永続化ストレージからセッションの読み込み中に例外が発生しました。
deltaManager.noCluster=Starting ...このコンテキスト[{0}]に関連付けられたクラスタはありません。
deltaManager.noContextManager=マネージャ[{0}][{2}] msで[{1}]でNo Context Managerの送信が受信されました
deltaManager.noMasterMember=ドメイン[{1}]のコンテキスト[{0}]に他のメンバーがいない状態で開始しています。
deltaManager.noMembers=Manager [{0}]:状態転送をスキップします。 クラスタグループ内でアクティブなメンバーはいません。
deltaManager.noSessionState=Manager [{0}][{1}]で送信されたセッション状態はありませんでした。[{2}] ms後にタイムアウトしました。
deltaManager.receiveMessage.accessed=Manager [{0}]:セッション[{1}]のセッションアクセスメッセージを受信しました。
deltaManager.receiveMessage.allSessionDataAfter=Manager[{0}]: 全てのセッション状態をデシリアライズしました。
deltaManager.receiveMessage.allSessionDataBegin=Manager[{0}]:すべてのセッション状態データを受信しました。
deltaManager.receiveMessage.createNewSession=Manager [{0}]:セッション[{1}]のセッション作成メッセージを受信しました。
deltaManager.receiveMessage.delta=Manager [{0}]:セッション[{1}]のセッションデルタメッセージを受信しました。
deltaManager.receiveMessage.delta.unknown=マネージャ[{0}]:未知のセッション[{1}]デルタを受信しました。
deltaManager.receiveMessage.error=Manager [{0}]: TCP チャンネルからメッセージを受信できません。
deltaManager.receiveMessage.eventType=Manager[{0}]: [{2}] からセッションメッセージ [{1}] を受信しました。
deltaManager.receiveMessage.expired=Manager[{0}]:セッション[{1}]のセッションの期限切れメッセージを受信しました。
deltaManager.receiveMessage.noContextManager=Manager [{0}]はノード[{1}{2}]からコンテキストマネージャ無しメッセージを受信しました。
deltaManager.receiveMessage.transfercomplete=Manager [{0}]はノード[{1}{2}]からセッション状態が転送を受信しました。
deltaManager.receiveMessage.unloadingAfter=Manager[{0}]:セッションのアンロードが完了しました。
deltaManager.receiveMessage.unloadingBegin=Manager [{0}]:セッションのアンロードを開始します
deltaManager.registerCluster=マネージャー [{0}] をクラスターの構成要素 [{1}] に名前 [{2}] で登録しました。
deltaManager.sendMessage.newSession=Manager[{0}]が新しいセッションを送信します。[{1}]
deltaManager.sessionReceived=Manager [{0}]; [{2}] msで受信した[{1}]で送信されたセッション状態。
deltaManager.startClustering=[{0}]でクラスタリングマネージャを開始しています。
deltaManager.stopped=マネージャ[{0}]が停止しています
deltaManager.unableSerializeSessionID=セッション ID [{0}] をシリアライズできません。
deltaManager.unloading.ioe=永続セッションを保存中のIOException[{0}]
deltaManager.waitForSessionState=Manager[{0}]、[{1}]からのセッション状態を要求しています。 [{2}]秒以内にセッション状態が受信されなかった場合、この操作はタイムアウトになります。
deltaRequest.invalidAttributeInfoType=無効な属性情報タイプ= [{0}]
deltaRequest.removeUnable=要素を削除できません。
deltaRequest.showPrincipal=プリンシパル [{0}]はセッション [{1}]に設定されています
deltaRequest.ssid.mismatch=セッションIDが一致しません。デルタリクエストが実行されていません。
deltaRequest.ssid.null=setSessionId に指定したセッション ID が null です。
deltaRequest.wrongPrincipalClass=ClusterManagerはGenericPrincipalのみをサポートします。 あなたのRealmはプリンシパルクラス[{0}]を使用しました。
deltaSession.notifying=クラスタにセッションの有効期限を通知するprimary = [{0}]、sessionId [{1}]
deltaSession.readSession=readObject() はセッション [{0}] を読み込みました。
deltaSession.writeSession=writeObject() によりセッション [{0}] を格納しました。
jvmRoute.cannotFindSession=セッション [{0}] がありません。
jvmRoute.changeSession=セッションを [{0}] から [{1}] へ変更しました。
jvmRoute.failover=他の jvmRoute へのフェールオーバーを検出しました。元のルートは [{0}]、新しいルートは [{1}]、セッション ID は [{2}] です。
jvmRoute.foundManager=コンテキスト [{1}] のCluster Manager [{0}] を発見しました。
jvmRoute.missingJvmRouteAttribute=jvmRoute 属性にエンジンが指定されていません。
jvmRoute.noCluster=JvmRouterBinderValveは設定されていますが、クラスタリングは使用されていません。 PersistentManagerが使用されている場合、フェールオーバーは引き続き機能します。
jvmRoute.notFoundManager=[{0}]でCluster Managerが見つかりません。
jvmRoute.set.orignalsessionid=オリジナルSession idをリクエスト属性[{0}]の値:[{1}]で設定します。
jvmRoute.turnoverInfo=折り返しの所要時間は [{0}] ミリ秒でした。
jvmRoute.valve.started=JvmRouteBinderValve が起動しました。
jvmRoute.valve.stopped=JvmRouteBinderValve が停止しました。
standardSession.notSerializable=セッション[{1}]のセッション属性[{0}]をシリアライズできません。
standardSession.removeAttribute.ise=removeAttribute: Session already invalidated
standardSession.setAttribute.namenull=setAttributenameパラメータをnullにすることはできません。

View File

@@ -0,0 +1,90 @@
# 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.
backupManager.noCluster=이 컨텍스트 [{0}]와(과) 연관된 클러스터가 없습니다.
backupManager.startFailed=백업매니저 [{0}]을(를) 시작하지 못했습니다.
backupManager.startUnable=BackupManager를 시작할 수 없습니다: [{0}]
backupManager.stopped=매니저 [{0}]이(가) 중지되고 있습니다.
clusterSessionListener.noManager=컨텍스트 매니저가 존재하지 않습니다: [{0}]
deltaManager.createMessage.access=매니저 [{0}]: 세션(ID: [{1}])을 위한 세션 접근 메시지를 생성합니다.
deltaManager.createMessage.accessChangePrimary=매니저 [{0}]: 세션(ID: [{1}])을 위해 Primary 노드 변경 메시지를 생성합니다.
deltaManager.createMessage.allSessionData=매니저 [{0}]이(가) 모든 세션 데이터를 전송했습니다.
deltaManager.createMessage.allSessionTransfered=매니저 [{0}]이(가), 모든 세션 데이터 전송 완료 메시지를 보냈습니다.
deltaManager.createMessage.delta=매니저 [{0}]: 세션(ID: [{1}])을 위한 델타 요청 메시지를 생성합니다.
deltaManager.createMessage.expire=매니저 [{0}]: 세션(ID: [{1}])을 위한 세션 만료 메시지를 생성합니다.
deltaManager.createMessage.unableCreateDeltaRequest=세션 ID [{0}]을(를) 위한 델타 요청을 직렬화할 수 없습니다.
deltaManager.createSession.newSession=ID가 [{0}]인 DeltaSession을 생성했습니다. 총 개수=[{1}]
deltaManager.dropMessage=매니저 [{0}]: GET_ALL_SESSIONS 동기화 국면 내에서, 메시지 [{1}]을(를) 무시합니다. 시작 시간: [{2}], 메시지의 타임스탬프: [{3}]
deltaManager.expireSessions=매니저 [{0}]이(가) 셧다운 시에 세션들을 만료시킵니다.
deltaManager.foundMasterMember=컨텍스트 [{0}]을(를) 위한 복제 마스터 멤버 [{1}]을(를) 찾았습니다.
deltaManager.loading.cnfe=저장된 세션들을 로드하는 중 ClassNotFoundException 발생: [{0}]
deltaManager.loading.existing.session=기존 세션 [{0}]을(를) 오버로드합니다.
deltaManager.loading.ioe=저장된 세션들을 로드하는 중 IOException 발생: [{0}]
deltaManager.managerLoad=저장소로부터 세션들을 로드하는 중 예외 발생
deltaManager.noCluster=시작 중... 이 컨텍스트와 연관된 클러스터가 없습니다: [{0}]
deltaManager.noContextManager=매니저 [{0}]: [{1}]에서 전송되었던 ''Get all session data'' 메시지에 응답하여, ''No matching context manager'' 메시지를 [{2}] 밀리초 후에 받았습니다.
deltaManager.noMasterMember=시작 중... 도메인 [{1}]에 컨텍스트 [{0}]을(를) 위한 다른 멤버들은 없는 상태입니다.
deltaManager.noMembers=매니저 [{0}]: 상태 이전 작업을 건너뜁니다. 클러스터 그룹 내에 활성화된 멤버가 없습니다.
deltaManager.noSessionState=매니저 [{0}]: [{1}]에서 보낸 세션 상태 메시지를 받지 못했습니다. [{2}] 밀리초 이후 제한 시간 초과되었습니다.
deltaManager.receiveMessage.accessed=매니저 [{0}]: 세션(ID: [{1}])을 위한 세션 접근 메시지를 받았습니다.
deltaManager.receiveMessage.allSessionDataAfter=매니저 [{0}]: 모든 세션 상태가 역직렬화되었습니다.
deltaManager.receiveMessage.allSessionDataBegin=매니저 [{0}]: 모든 세션 상태 데이터를 받았습니다.
deltaManager.receiveMessage.createNewSession=매니저 [{0}]: 세션 [{1}]을(를) 위한 세션 생성됨 메시지를 수신했습니다.
deltaManager.receiveMessage.delta=매니저 [{0}]: 세션(ID: [{1}])을 위한 세션 델타 메시지를 받았습니다.
deltaManager.receiveMessage.delta.unknown=매니저 [{0}]: 알 수 없는 세션 [{1}]을(를) 위한 세션 델타를 받았습니다.
deltaManager.receiveMessage.error=매니저 [{0}]: TCP 채널을 통해 메시지를 받을 수 없습니다.
deltaManager.receiveMessage.eventType=매니저 [{0}]: 타입이 [{1}]인 SessionMessage를 [{2}](으)로부터 받았습니다.
deltaManager.receiveMessage.expired=매니저 [{0}]: 세션 [{1}]을(를) 위한 세션 만료 메시지를 받았습니다.
deltaManager.receiveMessage.noContextManager=매니저 [{0}]이(가) 노드 [{1}:{2}](으)로부터 no context manager 메시지를 받았습니다.
deltaManager.receiveMessage.transfercomplete=매니저 [{0}]이(가) 노드 [{1}:{2}](으)로부터, 세션 상태 이전 완료 메시지를 받았습니다.
deltaManager.receiveMessage.unloadingAfter=매니저 [{0}]: 세션들을 언로드하는 작업이 완료되었습니다.
deltaManager.receiveMessage.unloadingBegin=매니저 [{0}]: 세션들에 대해 언로드를 시작합니다.
deltaManager.registerCluster=매니저 [{0}]을(를), [{2}](이)라는 이름의 클러스터 엘리먼트 [{1}](으)로 등록합니다.
deltaManager.sendMessage.newSession=매니저 [{0}]이(가) 새로운 세션 [{1}]을(를) 전송합니다.
deltaManager.sessionReceived=매니저 [{0}]; [{1}]에서 전송된 세션 상태를 [{2}] 밀리초 이내에 받음
deltaManager.startClustering=[{0}]에서 클러스터 매니저를 시작합니다.
deltaManager.stopped=매니저 [{0}]이(가) 중지됩니다.
deltaManager.unableSerializeSessionID=세션 ID [{0}]을(를) 직렬화할 수 없습니다.
deltaManager.unloading.ioe=세션들을 저장하는 중 IOException 발생: [{0}]
deltaManager.waitForSessionState=매니저 [{0}]: [{1}](으)로부터 세션 상태를 요청합니다. 만일 [{2}]초 이내에 세션 상태를 받지 못하면, 이 오퍼레이션은 제한 시간 초과 처리될 것입니다.
deltaRequest.invalidAttributeInfoType=유효하지 않은 AttributeInfo 타입=[{0}]
deltaRequest.removeUnable=클러스터 엘리먼트를 제거할 수 없습니다:
deltaRequest.showPrincipal=Principal [{0}]이(가) 세션 [{1}]에 설정되었습니다.
deltaRequest.ssid.mismatch=세션 ID가 일치하지 않아, 델타 요청을 실행하지 않습니다.
deltaRequest.ssid.null=setSessionId를 위한 세션 ID가 널입니다.
deltaRequest.wrongPrincipalClass=ClusterManager는 오직 GenericPrincipal만을 지원합니다. 사용된 realm은 principal 클래스 [{0}]을(를) 사용했습니다.
deltaSession.notifying=클러스터에 세션 만료를 통지합니다: primary여부: [{0}], 세션ID: [{1}]
deltaSession.readSession=readObject()가 세션 [{0}]을(를) 로드합니다.
deltaSession.writeSession=writeObject()가 세션 [{0}]을(를) 저장합니다.
jvmRoute.cannotFindSession=세션 [{0}]을(를) 찾을 수 없습니다.
jvmRoute.changeSession=세션을 [{0}]에서 [{1}](으)로 변경했습니다.
jvmRoute.failover=다른 jvmRoute로 Failover를 탐지했습니다. 원래의 라우트: [{0}], 새로운 라우트: [{1}]. 세션 ID: [{2}]
jvmRoute.foundManager=[{1}]에서 클러스터 매니저 [{0}]을(를) 찾았습니다.
jvmRoute.missingJvmRouteAttribute=엔진의 jvmRoute 속성이 설정되지 않았습니다!
jvmRoute.noCluster=JvmRouterBinderValve가 설정되었지만, 클러스터링이 사용되고 있지 않습니다. PersistentManager가 사용되는 경우, Fail over는 여전히 정상 동작할 것입니다.
jvmRoute.notFoundManager=[{0}]에서 클러스터 매니저를 찾을 수 없습니다.
jvmRoute.set.orignalsessionid=요청의 속성 [{0}]에 원래의 세션 ID를 설정합니다: [{1}]
jvmRoute.turnoverInfo=Failover를 위한 jvmRoute 교체 수행 시간: [{0}] 밀리초
jvmRoute.valve.started=JvmRouteBinderValve가 시작됐습니다.
jvmRoute.valve.stopped=JvmRouteBinderValve가 중지되었습니다.
standardSession.notSerializable=세션 [{1}]을 위한 세션 속성 [{0}]을(를) 직렬화할 수 없습니다.
standardSession.removeAttribute.ise=removeAttribute: 세션이 이미 무효화되었습니다.
standardSession.setAttribute.namenull=setAttribute: name 파라미터는 널일 수 없습니다.

View File

@@ -0,0 +1,55 @@
# 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.
backupManager.noCluster=没有与此上下文关联的集群:[{0}]
backupManager.startFailed=启动BackupManager: [{0}]失败
backupManager.startUnable=无法启动BackupManager: [{0}]
backupManager.stopped=管理者[{0}]正在停止。
deltaManager.createMessage.access=管理器[{0}]:创建会话为会话[{1}]存取消息
deltaManager.createMessage.delta=管理器[{0}] ):为会话[{1}]创建增量请求消息
deltaManager.createMessage.expire=管理器[{0}] (:为会话[{1}]创建会话过期消息
deltaManager.createSession.newSession=用id[{0}]创建一个扩展会话(DeltaSession),总数为 [{1}]
deltaManager.foundMasterMember=复制主master 成员在上下文中被发现.\n
deltaManager.loading.cnfe=加载持久化会话 [{0}] 时出现ClassNotFoundException
deltaManager.loading.ioe=加载持久 session 时出现 IOException[{0}]
deltaManager.managerLoad=从永久存储加载会话时发生异常
deltaManager.noContextManager=管理器[{0}]:回复[{1}]发送的“获取所有会话数据”消息,在[{2}] ms后收到“无匹配的上下文管理器”消息
deltaManager.noSessionState=管理者[{0}]:没有收到[{1}]发送的会话状态,在[{2}]毫秒之后超时。
deltaManager.receiveMessage.accessed=管理器[{0}]:接收会话为会话[{1}]存取消息
deltaManager.receiveMessage.allSessionDataAfter=Manager [{0}]: session 状态反序列化
deltaManager.receiveMessage.allSessionDataBegin=管理者[{0}]:接收到所有会话数据状态
deltaManager.receiveMessage.delta.unknown=管理器[{0}]:未知会话的接收会话增量[{1}]
deltaManager.receiveMessage.expired=管理器[{0}]: 接收到的会话 [{1}] 已过期。
deltaManager.receiveMessage.unloadingBegin=管理器[{0}]: 开始卸载会话
deltaManager.registerCluster=将管理器[{0}]注册到名为[{2}]的集群元素[{1}]
deltaManager.sendMessage.newSession=\ 管理器 [{0}] 发送新的会话 [{1}]
deltaManager.sessionReceived=管理器[{0}];在[{1}]发送的会话状态在[{2}]毫秒内收到。
deltaManager.unableSerializeSessionID=无法序列化会话ID [{0}]
deltaManager.unloading.ioe=当保存永久回话:[{0}] 时,抛出 IOException
deltaRequest.removeUnable=不能移除元素
deltaRequest.showPrincipal=Principal [{0}] 和session [{1}]产生关联。
deltaRequest.wrongPrincipalClass=ClusterManager仅支持GenericPrincipal。 你的Realm使用的Principal类为[{0}]。
deltaSession.writeSession=writeObject()存储会话[{0}]
jvmRoute.changeSession=会话从[{0}]切换到[{1}]
jvmRoute.missingJvmRouteAttribute=没有配置引擎jvmRoute属性
jvmRoute.notFoundManager=没有在 [{0}] 找到Cluster Manager
jvmRoute.set.orignalsessionid=在请求属性[{0}]值:[{1}]处设置原始会话ID
jvmRoute.valve.started=JvmRouteBinderValve 启动
standardSession.setAttribute.namenull=setAttribute:名称属性不能为空

View File

@@ -0,0 +1,29 @@
/*
* 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.Serializable;
import org.apache.catalina.SessionListener;
/**
* This is a marker interface used to indicate an implementation of
* {@link SessionListener} that should be replicated with the session across the
* cluster.
*/
public interface ReplicatedSessionListener extends SessionListener, Serializable {
}

View File

@@ -0,0 +1,110 @@
/*
* 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_EXPIRED</pre><li>
* <li><pre>public static final int EVT_SESSION_ACCESSED</pre><li>
* <li><pre>public static final int EVT_GET_ALL_SESSIONS</pre><li>
* <li><pre>public static final int EVT_SESSION_DELTA</pre><li>
* <li><pre>public static final int EVT_ALL_SESSION_DATA</pre><li>
* <li><pre>public static final int EVT_ALL_SESSION_TRANSFERCOMPLETE</pre><li>
* <li><pre>public static final int EVT_CHANGE_SESSION_ID</pre><li>
* <li><pre>public static final int EVT_ALL_SESSION_NOCONTEXTMANAGER</pre><li>
* </ul>
*
*/
public interface SessionMessage extends ClusterMessage {
/**
* 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;
/**
* Event type used when a sessionID has been changed.
*/
public static final int EVT_CHANGE_SESSION_ID = 15;
/**
* Event type used when context manager doesn't exist.
* This is used when the manager which send a session state does not exist.
*/
public static final int EVT_ALL_SESSION_NOCONTEXTMANAGER = 16;
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

View File

@@ -0,0 +1,168 @@
/*
* 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 Peter Rossbach
*/
public class SessionMessageImpl extends ClusterMessageBase implements SessionMessage {
private static final long serialVersionUID = 2L;
/*
* Private serializable variables to keep the messages state
*/
private final int mEvtType;
private final byte[] mSession;
private final String mSessionID;
private final 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_GET_ALL_SESSIONS</B><BR>
* get all sessions from from one of the nodes.<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 transferred
* after GET_ALL_SESSION received from this sender.<BR>
* <B>EVT_CHANGE_SESSION_ID</B><BR>
* send original sessionID and new sessionID.<BR>
* <B>EVT_ALL_SESSION_NOCONTEXTMANAGER</B><BR>
* send that context manager does not exist
* 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
*/
@Override
public int getEventType() { return mEvtType; }
/**
* @return the serialized data for the session
*/
@Override
public byte[] getSession() { return mSession;}
/**
* @return the session ID for the session
*/
@Override
public String getSessionID(){ return mSessionID; }
/**
* set message send time but only the first setting works (one shot)
*/
@Override
public void setTimestamp(long time) {
synchronized(this) {
if(!timestampSet) {
serializationTimestamp=time;
timestampSet = true ;
}
}
}
@Override
public long getTimestamp() { return serializationTimestamp;}
/**
* clear text event type name (for logging purpose only)
* @return the event type in a string representation, useful for debugging
*/
@Override
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-TRANSFERRED";
case EVT_CHANGE_SESSION_ID : return "SESSION-ID-CHANGED";
case EVT_ALL_SESSION_NOCONTEXTMANAGER : return "NO-CONTEXT-MANAGER";
default : return "UNKNOWN-EVENT-TYPE";
}
}
@Override
public String getContextName() {
return mContextName;
}
@Override
public String getUniqueId() {
return uniqueId;
}
@Override
public String toString() {
return getEventTypeString() + "#" + getContextName() + "#" + getSessionID() ;
}
}

View File

@@ -0,0 +1,629 @@
<?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="asyncSupported"
description="Does this valve support async reporting? "
is="true"
type="boolean"/>
<attribute
name="className"
description="Fully qualified class name of the managed object"
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"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<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="DeltaManager"
description="Cluster Manager implementation of the Manager interface"
domain="Catalina"
group="Manager"
type="org.apache.catalina.ha.session.DeltaManager">
<attribute
name="activeSessions"
description="Number of active sessions at this moment"
type="int"
writeable="false"/>
<attribute
name="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute
name="counterNoStateTransfered"
description="Count the failed session transfers noStateTransfered"
type="int"
writeable="false"/>
<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="counterReceive_EVT_CHANGE_SESSION_ID"
description="Count receive EVT_CHANGE_SESSION_ID messages"
type="long"
writeable="false"/>
<attribute
name="counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER"
description="Count receive EVT_ALL_SESSION_NOCONTEXTMANAGER 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="counterSend_EVT_CHANGE_SESSION_ID"
description="Count send EVT_CHANGE_SESSION_ID messages"
type="long"
writeable="false"/>
<attribute
name="duplicates"
description="Number of duplicated session ids generated"
type="int"/>
<attribute
name="expiredSessions"
description="Number of sessions that expired ( doesn't include explicit invalidations )"
type="long"/>
<attribute
name="expireSessionsOnShutdown"
is="true"
description="expire all sessions cluster wide as one node goes down"
type="boolean"/>
<attribute
name="invalidatedSessions"
description="describe version"
type="[Ljava.lang.String;"
writeable="false"/>
<attribute
name="maxActive"
description="Maximum number of active sessions so far"
type="int"/>
<attribute
name="maxActiveSessions"
description="The maximum number of active Sessions allowed, or -1 for no limit"
type="int"/>
<attribute
name="name"
description="The descriptive name of this Manager implementation (for logging)"
type="java.lang.String"
writeable="false"/>
<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="notifyContainerListenersOnReplication"
is="true"
description="Send container events on backup nodes"
type="boolean"/>
<attribute
name="processExpiresFrequency"
description="The frequency of the manager checks (expiration and passivation)"
type="int"/>
<attribute
name="processingTime"
description="Time spent doing housekeeping and expiration"
type="long"/>
<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"/>
<attribute
name="sessionAverageAliveTime"
description="Average time an expired session had been alive"
type="int"/>
<attribute
name="sessionCounter"
description="Total number of sessions created by this manager"
type="long"/>
<attribute
name="sessionMaxAliveTime"
description="Longest time an expired session had been alive"
type="int"/>
<attribute
name="sessionReplaceCounter"
description="Total number of replaced sessions that load from external nodes"
type="long"
writeable="false"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute
name="stateTransfered"
description="Is session state transferred complete? "
type="boolean"/>
<attribute
name="stateTransferTimeout"
description="state transfer timeout in sec"
type="int"/>
<attribute
name="receivedQueueSize"
description="length of receive queue size when session received from other node"
type="int"
writeable="false"/>
<attribute
name="rejectedSessions"
description="Number of sessions we rejected due to maxActive being reached"
type="int"
writeable="false"/>
<attribute
name="noContextManagerReceived"
is="true"
description="Is no context manager message received? "
type="boolean"/>
<attribute
name="secureRandomAlgorithm"
description="The secure random number generator algorithm name"
type="java.lang.String"/>
<attribute
name="secureRandomClass"
description="The secure random number generator class name"
type="java.lang.String"/>
<attribute
name="secureRandomProvider"
description="The secure random number generator provider name"
type="java.lang.String"/>
<attribute
name="stateTimestampDrop"
is="true"
description="All session messages before state transfer message creation are dropped."
type="boolean"/>
<attribute
name="recordAllActions"
is="true"
description="Flag whether send all actions for session across Tomcat cluster nodes."
type="boolean"/>
<attribute
name="sessionAttributeNameFilter"
description="The string pattern used for including session attributes in replication. Null means all attributes are included."
type="java.lang.String"/>
<attribute
name="sessionAttributeValueClassNameFilter"
description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name."
type="java.lang.String"/>
<attribute
name="warnOnSessionAttributeFilterFailure"
description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
type="boolean"/>
<operation
name="expireSession"
description="Expired the given session"
impact="ACTION"
returnType="void">
<parameter
name="sessionId"
description="The session id for the session to be expired"
type="java.lang.String"/>
</operation>
<operation
name="expireAllLocalSessions"
description="expire all active local sessions and replicate the invalid sessions"
impact="ACTION"
returnType="void"/>
<operation
name="findSession"
description="Return the active Session, associated with this Manager, with the specified session id (if any)"
impact="ACTION"
returnType="org.apache.catalina.Session">
<parameter
name="id"
description="The session id for the session to be returned"
type="java.lang.String"/>
</operation>
<operation
name="findSessions"
description="Return the set of active Sessions associated with this Manager."
impact="ACTION"
returnType="[Lorg.apache.catalina.Session;">
</operation>
<operation
name="getAllClusterSessions"
description="send to oldest cluster member that this node need all cluster sessions (resync member)"
impact="ACTION"
returnType="void"/>
<operation
name="getCreationTime"
description="Return the creation time for this session"
impact="ACTION"
returnType="java.lang.String">
<parameter
name="sessionId"
description="The session id for the session "
type="java.lang.String"/>
</operation>
<operation
name="getLastAccessedTime"
description="Get the last access time. This one gets updated whenever a request finishes. "
impact="ACTION"
returnType="java.lang.String">
<parameter
name="sessionId"
description="Id of the session"
type="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="getThisAccessedTime"
description="Get the last access time. This one gets updated whenever a request starts. "
impact="ACTION"
returnType="java.lang.String">
<parameter
name="sessionId"
description="Id of the session"
type="java.lang.String"/>
</operation>
<operation
name="listSessionIds"
description="Return the list of active primary session ids"
impact="ACTION"
returnType="java.lang.String"/>
<operation
name="processExpires"
description="Invalidate all sessions that have expired.s"
impact="ACTION"
returnType="void"/>
<operation
name="resetStatistics"
description="Reset all statistics"
impact="ACTION"
returnType="void"/>
</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="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="className"
description="Fully qualified class name of the managed object"
type="java.lang.String"
writeable="false"/>
<attribute
name="duplicates"
description="Number of duplicated session ids generated"
type="int"/>
<attribute
name="expiredSessions"
description="Number of sessions that expired ( doesn't include explicit invalidations )"
type="long"/>
<attribute
name="invalidatedSessions"
description="Get the list of invalidated session."
type="[Ljava.lang.String;"/>
<attribute
name="mapName"
description="mapName"
type="java.lang.String"
writeable="false"/>
<attribute
name="mapSendOptions"
description="mapSendOptions"
type="int"
writeable="false"/>
<attribute
name="maxActive"
description="Maximum number of active sessions so far"
type="int"/>
<attribute
name="maxActiveSessions"
description="The maximum number of active Sessions allowed, or -1 for no limit"
type="int"/>
<attribute
name="name"
description="The name of component. "
type="java.lang.String"/>
<attribute
name="notifyListenersOnReplication"
is="true"
description="Send session attribute change events on backup nodes"
type="boolean"/>
<attribute
name="processExpiresFrequency"
description="The frequency of the manager checks (expiration and passivation)"
type="int"/>
<attribute
name="processingTime"
description="Time spent doing housekeeping and expiration"
type="long"/>
<attribute
name="sessionAverageAliveTime"
description="Average time an expired session had been alive"
type="int"/>
<attribute
name="sessionCounter"
description="Total number of sessions created by this manager"
type="long"/>
<attribute
name="sessionMaxAliveTime"
description="Longest time an expired session had been alive"
type="int"/>
<attribute name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute
name="rejectedSessions"
description="Number of sessions we rejected due to maxActive being reached"
type="int"/>
<attribute
name="rpcTimeout"
description="Timeout for RPC messages, how long we will wait for a reply"
type="long"/>
<attribute
name="terminateOnStartFailure"
description="Flag for whether to terminate this map that failed to start."
is="true"
type="boolean"/>
<attribute
name="secureRandomAlgorithm"
description="The secure random number generator algorithm name"
type="java.lang.String"/>
<attribute
name="secureRandomClass"
description="The secure random number generator class name"
type="java.lang.String"/>
<attribute
name="secureRandomProvider"
description="The secure random number generator provider name"
type="java.lang.String"/>
<attribute
name="recordAllActions"
is="true"
description="Flag whether send all actions for session across Tomcat cluster nodes."
type="boolean"/>
<attribute
name="sessionAttributeNameFilter"
description="The string pattern used for including session attributes in replication. Null means all attributes are included."
type="java.lang.String"/>
<attribute
name="sessionAttributeValueClassNameFilter"
description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name."
type="java.lang.String"/>
<attribute
name="warnOnSessionAttributeFilterFailure"
description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
type="boolean"/>
<attribute
name="accessTimeout"
description="The timeout for a ping message in replication map."
type="long"/>
<operation
name="expireSession"
description="Expired the given session"
impact="ACTION"
returnType="void">
<parameter
name="sessionId"
description="The session id for the session to be expired"
type="java.lang.String"/>
</operation>
<operation
name="findSession"
description="Return the active Session, associated with this Manager, with the specified session id (if any)"
impact="ACTION"
returnType="org.apache.catalina.Session">
<parameter
name="id"
description="The session id for the session to be returned"
type="java.lang.String"/>
</operation>
<operation
name="findSessions"
description="Return the set of active Sessions associated with this Manager."
impact="ACTION"
returnType="[Lorg.apache.catalina.Session;">
</operation>
<operation
name="getCreationTime"
description="Return the creation time for this session"
impact="ACTION"
returnType="java.lang.String">
<parameter
name="sessionId"
description="The session id for the session "
type="java.lang.String"/>
</operation>
<operation
name="getLastAccessedTime"
description="Get the last access time. This one gets updated whenever a request finishes. "
impact="ACTION"
returnType="java.lang.String">
<parameter
name="sessionId"
description="Id of the session"
type="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="getThisAccessedTime"
description="Get the last access time. This one gets updated whenever a request starts. "
impact="ACTION"
returnType="java.lang.String">
<parameter
name="sessionId"
description="Id of the session"
type="java.lang.String"/>
</operation>
<operation
name="listSessionIds"
description="Return the list of active primary session ids"
impact="ACTION"
returnType="java.lang.String"/>
<operation
name="getSessionIdsFull"
description="Returns the list of all sessions IDS (primary, backup and proxy)."
impact="ACTION"
returnType="java.util.Set"/>
<operation
name="processExpires"
description="Invalidate all sessions that have expired.s"
impact="ACTION"
returnType="void"/>
</mbean>
</mbeans-descriptors>

View File

@@ -0,0 +1,31 @@
/*
* 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
*/
public class Constants {
public static final String Package = "org.apache.catalina.ha.tcp";
}

View File

@@ -0,0 +1,42 @@
# 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.
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.failure=Unable to compile filter=[{0}]
ReplicationValve.filter.loading=Loading request 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 [{1}] in request attribute [{2}] is [{3}].
ReplicationValve.session.invalid=Context [{0}]: Requested session [{1}] is invalid, removed or not replicated at this node.
ReplicationValve.stats=Average request time=[{0}] ms with cluster overhead time=[{1}] ms for [{2}] requests, [{3}] send requests, [{4}] cross context requests, and [{5}] filter requests (Total request=[{6}] ms, total cluster request=[{7}] ms).
simpleTcpCluster.clustermanager.cloneFailed=Unable to clone cluster manager, defaulting to org.apache.catalina.ha.session.DeltaManager
simpleTcpCluster.clustermanager.notImplement=Manager [{0}] does not implement ClusterManager, addition to cluster has been aborted.
simpleTcpCluster.member.addFailed=Unable to connect to replication system.
simpleTcpCluster.member.added=Replication member added:[{0}]
simpleTcpCluster.member.disappeared=Received member disappeared:[{0}]
simpleTcpCluster.member.removeFailed=Unable remove cluster node from replication system.
simpleTcpCluster.sendFailed=Unable to send message through cluster sender.
simpleTcpCluster.start=Cluster is about to start
simpleTcpCluster.startUnable=Unable to start cluster.
simpleTcpCluster.stopUnable=Unable to stop cluster.
simpleTcpCluster.unableSend.localMember=Unable to send message to local member [{0}]

View File

@@ -0,0 +1,20 @@
# 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.
ReplicationValve.filter.failure=Kann Filter [{0}] nicht kompilieren
ReplicationValve.session.indicator=Context [{0}]: Primärität der Session [{1}] in Request Attribut [{2}] ist [{3}].
simpleTcpCluster.clustermanager.notImplement=Manager [{0}] implementiert nicht ClusterManager. Das Hinzufügen dieses Managers zum Cluster wurde daher abgebrochen.
simpleTcpCluster.stopUnable=Cluster kann nicht gestoppt werden.

View File

@@ -0,0 +1,35 @@
# 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.
ReplicationValve.crossContext.add=añadir contenedor de réplica de sesión de Contexto Cruzado a replicationValve threadlocal
ReplicationValve.crossContext.registerSession=retistrar id de sesión de Contexto Cruzado=[{0}] desde contexto [{1}]
ReplicationValve.crossContext.remove=quitar contenedor de réplica de sesión de Contexto Cruzado a replicationValve threadlocal
ReplicationValve.crossContext.sendDelta=enviar delta de sesión de Contexto Cruzado desde contexto [{0}].
ReplicationValve.filter.failure=No puedo compilar filtror=[{0}]
ReplicationValve.filter.loading=Cargando filtros de requerimiento=[{0}]
ReplicationValve.invoke.uri=Invocando requerimiento de réplica en [{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.clustermanager.notImplement=Manejador [{0}] no implementa ClusterManager, la adición al cluster ha sido abortada.\n
simpleTcpCluster.member.addFailed=Incapaz de conectar con el sistema de replicación
simpleTcpCluster.member.removeFailed=Imposible remover el nodo del sistema de replicación
simpleTcpCluster.stopUnable=Inmposible deterner el cluster

View File

@@ -0,0 +1,42 @@
# 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.
ReplicationValve.crossContext.add=Ajout du conteneur de réplication de la session multi contexte au ThreadLocal de replicationValve
ReplicationValve.crossContext.registerSession=enregistrement de la session multi contexte id=[{0}] du contexte [{1}]
ReplicationValve.crossContext.remove=Retrait du conteneur de réplication de la session multi contexte au ThreadLocal de replicationValve
ReplicationValve.crossContext.sendDelta=Envoi du delta de la session multi contexte du contexte [{0}]
ReplicationValve.filter.failure=Incapacité de compiler le filtre=[{0}]
ReplicationValve.filter.loading=Chargement du filtre de requête [{0}]
ReplicationValve.invoke.uri=Invocation de la requête de réplication sur [{0}]
ReplicationValve.nocluster=Aucun cluster de configuré pour cette requête
ReplicationValve.resetDeltaRequest=Le cluster se suffit à lui-même: réinitialisation du delta de la requête de session [{0}]
ReplicationValve.send.failure=Impossible d'effectuer la requête de réplication
ReplicationValve.send.invalid.failure=Incapable d'envoyer le message invalide de la session [id={0}] sur le cluster
ReplicationValve.session.found=Le Contexte [{0}] a touvé la session [{1}] mais ce n''est pas une ClusterSession.
ReplicationValve.session.indicator=Contexte [{0}] : la primarité de la session [{1}] dans l''attribut de requête [{2}] est [{3}].
ReplicationValve.session.invalid=Contexte [{0}]: la session demandée [{1}] est invalide, non répliquée, ou enlevée sur ce nœud
ReplicationValve.stats=Temps de requête moyen= [{0}] ms pour le Cluster le temps ajouté est de=[{1}] ms pour [{2}] requêtes [{3}] requêtes d''envoi [{4}] requêtes multi contextes et [{5}] requêtes fitrées (Total requêtes=[{6}] ms total requêtes du cluster=[{7}] ms)
simpleTcpCluster.clustermanager.cloneFailed=Impossible de cloner le gestionnaire du cluster, le org.apache.catalina.ha.session.DeltaManager par défaut sera utilisé
simpleTcpCluster.clustermanager.notImplement=Le gestionnaire ("Manager") [{0}] n''implémente pas ClusterManager. Son ajout au cluster a été abandonné.
simpleTcpCluster.member.addFailed=Impossible de se connecter au système de réplication
simpleTcpCluster.member.added=Membre de réplication ajouté : [{0}]
simpleTcpCluster.member.disappeared=Le membre recu a disparu: [{0}]
simpleTcpCluster.member.removeFailed=Impossible d'enlever un nœud du cluster du système de réplication
simpleTcpCluster.sendFailed=Impossible d'envoyer un message à travers l'expéditeur du cluster
simpleTcpCluster.start=Le cluster va démarrer
simpleTcpCluster.startUnable=Impossible de démarre le cluster
simpleTcpCluster.stopUnable=Incapable d'arrêter le cluster
simpleTcpCluster.unableSend.localMember=Impossible d''envoyer un message au membre local [{0}]

View File

@@ -0,0 +1,42 @@
# 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.
ReplicationValve.crossContext.add=クロスコンテキストセッションレプリケーションコンテナをreplicationValveスレッドローカルに追加
ReplicationValve.crossContext.registerSession=コンテキスト[{1}]からクロスコンテキストセッションID = [{0}]を登録する
ReplicationValve.crossContext.remove=replication Contextセッションレプリケーションコンテナをスレッドローカルから削除します。
ReplicationValve.crossContext.sendDelta=コンテキスト[{0}]からのクロスコンテキストセッションデルタを送信します。
ReplicationValve.filter.failure=フィルター文字列=[{0}] がコンパイルできません。
ReplicationValve.filter.loading=リクエストフィルタ= [{0}]のロード
ReplicationValve.invoke.uri=[{0}]のレプリケーションリクエストを呼び出します。
ReplicationValve.nocluster=このリクエストに対して構成されたクラスタはありません。
ReplicationValve.resetDeltaRequest=クラスタはスタンドアロンである:コンテキスト[{0}]でセッションのデルタリクエストをリセットします。
ReplicationValve.send.failure=レプリケーションリクエストを実行できません。
ReplicationValve.send.invalid.failure=セッション[id = {0}]無効メッセージをクラスタに送信できません。
ReplicationValve.session.found=コンテキスト [{0}]: セッション [{1}] は ClusterSession ではありません。
ReplicationValve.session.indicator=Context [{0}]:リクエスト属性[{2}]のセッション[{1}]のプライマリは[{3}]です。
ReplicationValve.session.invalid=コンテキスト [{0}]: 不正なセッション [{1}] が要求されました。消去された、あるいは、このノードに複製されなかった可能性があります。
ReplicationValve.stats=[{2}]リクエストの平均要求時間= [{0}] ms、クラスタオーバーヘッド時間= [{1}] ms、[{3}]リクエストの送信、[{4}]クロスコンテキストリクエスト、[{5} }]フィルタリクエスト(合計リクエスト= [{6}] ms、クラスタ全体リクエスト= [{7}] ms
simpleTcpCluster.clustermanager.cloneFailed=クラスタマネージャをクローンできません。デフォルトはorg.apache.catalina.ha.session.DeltaManagerです。
simpleTcpCluster.clustermanager.notImplement=クラス [{0}] は ClusterManager を実装していません。それにクラスターはすでに停止しています。
simpleTcpCluster.member.addFailed=レプリケーションシステムに接続できません。
simpleTcpCluster.member.added=レプリケーションメンバーを追加しました: [{0}]
simpleTcpCluster.member.disappeared=メッセージ消失を受信しました: [{0}]
simpleTcpCluster.member.removeFailed=レプリケーションシステムからクラスターノードを削除できませんでした。
simpleTcpCluster.sendFailed=クラスタセンダ経由でメッセージを送信できませんでした。
simpleTcpCluster.start=Clusterを起動します。
simpleTcpCluster.startUnable=クラスタを起動出来ません。
simpleTcpCluster.stopUnable=クラスタを停止できません。
simpleTcpCluster.unableSend.localMember=ローカルメンバー [{0}] にメッセージを送信できません。

View File

@@ -0,0 +1,42 @@
# 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.
ReplicationValve.crossContext.add=교차 컨텍스트 세션 복제 컨테이너를 replicationValve의 threadlocal에 추가합니다.
ReplicationValve.crossContext.registerSession=컨텍스트 [{1}](으)로부터 세션 ID가 [{0}]인 교차 컨텍스트 세션을 등록합니다.
ReplicationValve.crossContext.remove=replicationValve의 threadlocal로부터, 교차 컨텍스트 세션 복제 컨테이너를 제거합니다.
ReplicationValve.crossContext.sendDelta=컨텍스트 [{0}](으)로부터 교차 컨텍스트 세션 델타를 보냅니다.
ReplicationValve.filter.failure=필터 컴파일을 할 수 없습니다. filter=[{0}]
ReplicationValve.filter.loading=요청 필터를 로드합니다: [{0}]
ReplicationValve.invoke.uri=[{0}]에 복제 요청을 호출합니다.
ReplicationValve.nocluster=이 요청을 위해 설정된 클러스터가 없습니다.
ReplicationValve.resetDeltaRequest=클러스터가 독립형(standalone)입니다: 컨텍스트 [{0}]에서 세션 요청 델타를 재설정(reset)합니다.
ReplicationValve.send.failure=복제 요청을 수행 할 수 없습니다.
ReplicationValve.send.invalid.failure=세션 [id={0}] 유효하지 않음 메시지를 클러스터에 전송할 수 없습니다.
ReplicationValve.session.found=컨텍스트 [{0}]에서 세션 [{1}]을(를) 발견했으나, 이는 ClusterSession이 아닙니다.
ReplicationValve.session.indicator=컨텍스트 [{0}]: 요청 속성 [{2}]에 있는 세션 [{1}]의 Primary 여부: [{3}]
ReplicationValve.session.invalid=컨텍스트 [{0}]: 요청된 세션 [{1}]이(가), 유효하지 않거나, 제거되었거나, 또는 이 클러스터 노드로 복제되지 않았습니다.
ReplicationValve.stats=[{2}]개의 요청들, [{3}]개의 전송 요청들, [{4}]개의 교차 컨텍스트 요청들, 그리고 [{5}]개의 필터 요청들을 처리하는 동안, 평균 요청 시간=[{0}] 밀리초, 클러스터 오버헤드 시간=[{1}] 밀리초가 소요되었습니다. (총 요청 처리 시간=[{6}] 밀리초, 총 클러스터 요청 처리 시간=[{7}] 밀리초)
simpleTcpCluster.clustermanager.cloneFailed=클러스터 매니저를 복제할 수 없습니다. 기본 값인 org.apache.catalina.ha.session.DeltaManager를 사용합니다.
simpleTcpCluster.clustermanager.notImplement=매니저 [{0}]이(가) ClusterManager 인터페이스를 구현하지 않습니다. 클러스터에 추가하려는 시도는 중단됩니다.
simpleTcpCluster.member.addFailed=복제 시스템에 연결할 수 없습니다.
simpleTcpCluster.member.added=복제 멤버가 추가됨: [{0}]
simpleTcpCluster.member.disappeared=멤버 사라짐 메시지를 수신했습니다: [{0}]
simpleTcpCluster.member.removeFailed=복제 시스템으로부터 클러스터 노드를 제거할 수 없습니다.
simpleTcpCluster.sendFailed=클러스터 sender를 통해 메시지를 보낼 수 없습니다.
simpleTcpCluster.start=클러스터가 막 시작하려 합니다.
simpleTcpCluster.startUnable=클러스터를 시작할 수 없습니다.
simpleTcpCluster.stopUnable=클러스터를 중지시킬 수 없습니다.
simpleTcpCluster.unableSend.localMember=로컬 멤버 [{0}]에게 메시지를 보낼 수 없습니다.

View File

@@ -0,0 +1,24 @@
# 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.
ReplicationValve.filter.failure=无法编译 filter = [{0}]
ReplicationValve.session.found=上下文[{0}]:找到会话[{1}]但它不是ClusterSession。
ReplicationValve.session.invalid=上下文[{0}]:请求的会话[{1}]在此节点上无效,已删除或未复制。
simpleTcpCluster.clustermanager.notImplement=连接器 [{0}] 不能继承 ClusterManager除非集群被停止。
simpleTcpCluster.member.addFailed=无法连接到复制系统。
simpleTcpCluster.member.disappeared=收到成员消失:[{0}]
simpleTcpCluster.member.removeFailed=无法从复制系统中移除集群节点
simpleTcpCluster.stopUnable=无法停止集群

View File

@@ -0,0 +1,631 @@
/*
* 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.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.servlet.ServletException;
import org.apache.catalina.Cluster;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
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.valves.ValveBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* <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 Peter Rossbach
*/
public class ReplicationValve
extends ValveBase implements ClusterValve {
private static final Log log = LogFactory.getLog(ReplicationValve.class);
// ----------------------------------------------------- Instance Variables
/**
* The StringManager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
private CatalinaCluster cluster = null ;
/**
* Filter expression
*/
protected Pattern filter = null;
/**
* crossContext session container
*/
protected final ThreadLocal<ArrayList<DeltaSession>> crossContextSessions =
new ThreadLocal<>() ;
/**
* doProcessingStats (default = off)
*/
protected boolean doProcessingStats = false;
/*
* Note: The statistics are volatile to ensure the concurrent updates do not
* corrupt them but it is still possible that:
* - some updates may be lost;
* - the individual statistics may not be consistent which each other.
* This is a deliberate design choice to reduce the requirement for
* synchronization.
*/
protected volatile long totalRequestTime = 0;
protected volatile long totalSendTime = 0;
protected volatile long nrOfRequests = 0;
protected volatile long lastSendTime = 0;
protected volatile long nrOfFilterRequests = 0;
protected volatile long nrOfSendRequests = 0;
protected volatile 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() {
super(true);
}
/**
* @return the cluster.
*/
@Override
public CatalinaCluster getCluster() {
return cluster;
}
/**
* @param cluster The cluster to set.
*/
@Override
public void setCluster(CatalinaCluster cluster) {
this.cluster = cluster;
}
/**
* @return the filter
*/
public String getFilter() {
if (filter == null) {
return null;
}
return filter.toString();
}
/**
* compile filter string to regular expression
* @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));
}
if (filter == null || filter.length() == 0) {
this.filter = null;
} else {
try {
this.filter = Pattern.compile(filter);
} catch (PatternSyntaxException pse) {
log.error(sm.getString("ReplicationValve.filter.failure",
filter), pse);
}
}
}
/**
* @return the primaryIndicator.
*/
public boolean isPrimaryIndicator() {
return primaryIndicator;
}
/**
* @param primaryIndicator The primaryIndicator to set.
*/
public void setPrimaryIndicator(boolean primaryIndicator) {
this.primaryIndicator = primaryIndicator;
}
/**
* @return the primaryIndicatorName.
*/
public String getPrimaryIndicatorName() {
return primaryIndicatorName;
}
/**
* @param primaryIndicatorName The primaryIndicatorName to set.
*/
public void setPrimaryIndicatorName(String primaryIndicatorName) {
this.primaryIndicatorName = primaryIndicatorName;
}
/**
* Calc processing stats
* @return <code>true</code> if statistics are enabled
*/
public boolean doStatistics() {
return doProcessingStats;
}
/**
* Set Calc processing stats
*
* @param doProcessingStats New flag value
* @see #resetStatistics()
*/
public void setStatistics(boolean doProcessingStats) {
this.doProcessingStats = doProcessingStats;
}
/**
* @return the lastSendTime.
*/
public long getLastSendTime() {
return lastSendTime;
}
/**
* @return the nrOfRequests.
*/
public long getNrOfRequests() {
return nrOfRequests;
}
/**
* @return the nrOfFilterRequests.
*/
public long getNrOfFilterRequests() {
return nrOfFilterRequests;
}
/**
* @return the nrOfCrossContextSendRequests.
*/
public long getNrOfCrossContextSendRequests() {
return nrOfCrossContextSendRequests;
}
/**
* @return the nrOfSendRequests.
*/
public long getNrOfSendRequests() {
return nrOfSendRequests;
}
/**
* @return the totalRequestTime.
*/
public long getTotalRequestTime() {
return totalRequestTime;
}
/**
* @return the totalSendTime.
*/
public long getTotalSendTime() {
return totalSendTime;
}
// --------------------------------------------------------- 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<DeltaSession> sessions = crossContextSessions.get();
if(sessions != null) {
if(!sessions.contains(session)) {
if(log.isDebugEnabled()) {
log.debug(sm.getString("ReplicationValve.crossContext.registerSession",
session.getIdInternal(),
session.getManager().getContext().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
*/
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException
{
long totalstart = 0;
//this happens before the request
if(doStatistics()) {
totalstart = System.currentTimeMillis();
}
if (primaryIndicator) {
createPrimaryIndicator(request) ;
}
Context context = request.getContext();
boolean isCrossContext = context != null
&& context instanceof StandardContext
&& ((StandardContext) context).getCrossContext();
try {
if(isCrossContext) {
if(log.isDebugEnabled()) {
log.debug(sm.getString("ReplicationValve.crossContext.add"));
}
//FIXME add Pool of Arraylists
crossContextSessions.set(new ArrayList<DeltaSession>());
}
getNext().invoke(request, response);
if(context != null && cluster != null
&& context.getManager() instanceof ClusterManager) {
ClusterManager clusterManager = (ClusterManager) context.getManager();
// valve cluster can access manager - other cluster handle replication
// at host level - hopefully!
if(cluster.getManager(clusterManager.getName()) == null) {
return ;
}
if(cluster.hasMembers()) {
sendReplicationMessage(request, totalstart, isCrossContext, clusterManager);
} 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 statistics
*/
public void resetStatistics() {
totalRequestTime = 0;
totalSendTime = 0;
lastSendTime = 0;
nrOfFilterRequests = 0;
nrOfRequests = 0;
nrOfSendRequests = 0;
nrOfCrossContextSendRequests = 0;
}
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
if (cluster == null) {
Cluster containerCluster = getContainer().getCluster();
if (containerCluster instanceof CatalinaCluster) {
setCluster((CatalinaCluster)containerCluster);
} else {
if (log.isWarnEnabled()) {
log.warn(sm.getString("ReplicationValve.nocluster"));
}
}
}
super.startInternal();
}
// --------------------------------------------------------- Protected Methods
protected void sendReplicationMessage(Request request, long totalstart, boolean isCrossContext, ClusterManager clusterManager) {
//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);
}
// send replication
sendSessionReplicationMessage(request, clusterManager);
if(isCrossContext) {
sendCrossContextSession();
}
} 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
*/
protected void sendCrossContextSession() {
List<DeltaSession> sessions = crossContextSessions.get();
if(sessions != null && sessions.size() >0) {
for (DeltaSession session : sessions) {
if(log.isDebugEnabled()) {
log.debug(sm.getString("ReplicationValve.crossContext.sendDelta",
session.getManager().getContext().getName() ));
}
sendMessage(session,(ClusterManager)session.getManager());
if(doStatistics()) {
nrOfCrossContextSendRequests++;
}
}
}
}
/**
* Fix memory leak for long sessions with many changes, when no backup member exists!
* @param request current request after response is generated
* @param isCrossContext check crosscontext threadlocal
*/
protected void resetReplicationRequest(Request request, boolean isCrossContext) {
Session contextSession = request.getSessionInternal(false);
if(contextSession instanceof DeltaSession){
resetDeltaRequest(contextSession);
((DeltaSession)contextSession).setPrimarySession(true);
}
if(isCrossContext) {
List<DeltaSession> sessions = crossContextSessions.get();
if(sessions != null && sessions.size() >0) {
Iterator<DeltaSession> iter = sessions.iterator();
for(; iter.hasNext() ;) {
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().getContext().getName() ));
}
((DeltaSession)session).resetDeltaRequest();
}
/**
* Send Cluster Replication Request
* @param request current request
* @param manager session manager
*/
protected void sendSessionReplicationMessage(Request request,
ClusterManager manager) {
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);
} else
if(doStatistics()) {
nrOfFilterRequests++;
}
}
}
/**
* Send message delta message from request session
* @param session current session
* @param manager session manager
*/
protected void sendMessage(Session session,
ClusterManager manager) {
String id = session.getIdInternal();
if (id != null) {
send(manager, id);
}
}
/**
* send manager requestCompleted message to cluster
* @param manager SessionManager
* @param sessionId sessionid from the manager
* @see DeltaManager#requestCompleted(String)
* @see SimpleTcpCluster#send(ClusterMessage)
*/
protected void send(ClusterManager manager, String sessionId) {
ClusterMessage msg = manager.requestCompleted(sessionId);
if (msg != null && cluster != null) {
cluster.send(msg);
if(doStatistics()) {
nrOfSendRequests++;
}
}
}
/**
* check for session invalidations
* @param manager Associated manager
*/
protected void sendInvalidSessions(ClusterManager manager) {
String[] invalidIds=manager.getInvalidatedSessions();
if ( invalidIds.length > 0 ) {
for ( int i=0;i<invalidIds.length; i++ ) {
try {
send(manager,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) {
Pattern f = filter;
return f != null && f.matcher(uri).matches();
}
/**
* Protocol cluster replications stats
* @param requestTime Request time
* @param clusterTime Cluster time
*/
protected void updateStats(long requestTime, long clusterTime) {
// TODO: Async requests may trigger multiple replication requests. How,
// if at all, should the stats handle this?
long currentTime = System.currentTimeMillis();
lastSendTime = currentTime;
totalSendTime += currentTime - clusterTime;
totalRequestTime += currentTime - requestTime;
nrOfRequests++;
if(log.isInfoEnabled()) {
if ( (nrOfRequests % 100) == 0 ) {
log.info(sm.getString("ReplicationValve.stats",
new Object[]{
Long.valueOf(totalRequestTime/nrOfRequests),
Long.valueOf(totalSendTime/nrOfRequests),
Long.valueOf(nrOfRequests),
Long.valueOf(nrOfSendRequests),
Long.valueOf(nrOfCrossContextSendRequests),
Long.valueOf(nrOfFilterRequests),
Long.valueOf(totalRequestTime),
Long.valueOf(totalSendTime)}));
}
}
}
/**
* Mark Request that processed at primary node with attribute
* primaryIndicatorName
*
* @param request The Servlet request
* @throws IOException IO error finding session
*/
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 (log.isDebugEnabled()) {
log.debug(sm.getString(
"ReplicationValve.session.indicator", request.getContext().getName(),id,
primaryIndicatorName,
Boolean.valueOf(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));
}
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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
*/
public class SendMessageData {
private Object message ;
private Member destination ;
private Exception exception ;
/**
* @param message The message to send
* @param destination Member destination
* @param exception Associated error
*/
public SendMessageData(Object message, Member destination,
Exception exception) {
super();
this.message = message;
this.destination = destination;
this.exception = exception;
}
/**
* @return the destination.
*/
public Member getDestination() {
return destination;
}
/**
* @return the exception.
*/
public Exception getException() {
return exception;
}
/**
* @return the message.
*/
public Object getMessage() {
return message;
}
}

View File

@@ -0,0 +1,853 @@
/*
* 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 java.util.concurrent.ConcurrentHashMap;
import javax.management.ObjectName;
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.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Manager;
import org.apache.catalina.Valve;
import org.apache.catalina.ha.CatalinaCluster;
import org.apache.catalina.ha.ClusterDeployer;
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.ClusterSessionListener;
import org.apache.catalina.ha.session.DeltaManager;
import org.apache.catalina.ha.session.JvmRouteBinderValve;
import org.apache.catalina.ha.session.SessionMessage;
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.tribes.group.interceptors.MessageDispatchInterceptor;
import org.apache.catalina.tribes.group.interceptors.TcpFailureDetector;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* A <b>Cluster </b> implementation using simple multicast. Responsible for
* setting up a cluster and provides callers with a valid multicast
* receiver/sender.
*
* FIXME wrote testcases
*
* @author Remy Maucherat
* @author Peter Rossbach
*/
public class SimpleTcpCluster extends LifecycleMBeanBase
implements CatalinaCluster, MembershipListener, ChannelListener{
public static final Log log = LogFactory.getLog(SimpleTcpCluster.class);
// ----------------------------------------------------- Instance Variables
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();
/**
* The string manager for this package.
*/
protected static final 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 property change support for this component.
*/
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
/**
* The context name &lt;-&gt; manager association for distributed contexts.
*/
protected final Map<String, ClusterManager> managers = new HashMap<>();
protected ClusterManager managerTemplate = new DeltaManager();
private final List<Valve> valves = new ArrayList<>();
private ClusterDeployer clusterDeployer;
private ObjectName onameClusterDeployer;
/**
* Listeners of messages
*/
protected final List<ClusterListener> clusterListeners = new ArrayList<>();
/**
* Comment for <code>notifyLifecycleListenerOnFailure</code>
*/
private boolean notifyLifecycleListenerOnFailure = false;
private int channelSendOptions = Channel.SEND_OPTIONS_ASYNCHRONOUS;
private int channelStartOptions = Channel.DEFAULT;
private final Map<Member,ObjectName> memberOnameMap = new ConcurrentHashMap<>();
// ------------------------------------------------------------- Properties
public SimpleTcpCluster() {
// NO-OP
}
/**
* 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
*/
@Override
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
*/
@Override
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
*/
@Override
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
*/
@Override
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);
}
/**
* Add cluster valve
* Cluster Valves are only add to container when cluster is started!
* @param valve The new cluster Valve.
*/
@Override
public void addValve(Valve valve) {
if (valve instanceof ClusterValve && (!valves.contains(valve)))
valves.add(valve);
}
/**
* get all cluster valves
* @return current cluster valves
*/
@Override
public Valve[] getValves() {
return 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.
* @return the listener array
*/
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.
*
* @param listener The new listener
* @see org.apache.catalina.ha.CatalinaCluster#addClusterListener(org.apache.catalina.ha.ClusterListener)
*/
@Override
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.
*
* @param listener The listener to remove
* @see org.apache.catalina.ha.CatalinaCluster#removeClusterListener(org.apache.catalina.ha.ClusterListener)
*/
@Override
public void removeClusterListener(ClusterListener listener) {
if (listener != null) {
clusterListeners.remove(listener);
listener.setCluster(null);
}
}
/**
* @return the current Deployer
*/
@Override
public ClusterDeployer getClusterDeployer() {
return clusterDeployer;
}
/**
* set a new Deployer, must be set before cluster started!
* @param clusterDeployer The associated deployer
*/
@Override
public void setClusterDeployer(ClusterDeployer clusterDeployer) {
this.clusterDeployer = clusterDeployer;
}
@Override
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;
@Override
public boolean hasMembers() {
return hasMembers;
}
/**
* Get all current cluster members
* @return all members or empty array
*/
@Override
public Member[] getMembers() {
return channel.getMembers();
}
/**
* Return the member that represents this node.
*
* @return Member
*/
@Override
public Member getLocalMember() {
return channel.getLocalMember(true);
}
// --------------------------------------------------------- Public Methods
/**
* @return Returns the managers.
*/
@Override
public Map<String, ClusterManager> getManagers() {
return managers;
}
@Override
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 DeltaManager#start()
*/
@Override
public synchronized Manager createManager(String name) {
if (log.isDebugEnabled()) {
log.debug("Creating ClusterManager for context " + name +
" using class " + getManagerTemplate().getClass().getName());
}
ClusterManager manager = null;
try {
manager = managerTemplate.cloneFromTemplate();
manager.setName(name);
} catch (Exception x) {
log.error(sm.getString("simpleTcpCluster.clustermanager.cloneFailed"), x);
manager = new org.apache.catalina.ha.session.DeltaManager();
} finally {
if ( manager != null) manager.setCluster(this);
}
return manager;
}
@Override
public void registerManager(Manager manager) {
if (! (manager instanceof ClusterManager)) {
log.warn(sm.getString("simpleTcpCluster.clustermanager.notImplement", manager));
return;
}
ClusterManager cmanager = (ClusterManager) manager;
// Notify our interested LifecycleListeners
fireLifecycleEvent(BEFORE_MANAGERREGISTER_EVENT, manager);
String clusterName = getManagerName(cmanager.getName(), manager);
cmanager.setName(clusterName);
cmanager.setCluster(this);
managers.put(clusterName, cmanager);
// Notify our interested LifecycleListeners
fireLifecycleEvent(AFTER_MANAGERREGISTER_EVENT, manager);
}
/**
* Remove an application from cluster replication bus.
*
* @param manager The manager
* @see org.apache.catalina.Cluster#removeManager(Manager)
*/
@Override
public void removeManager(Manager manager) {
if (manager instanceof ClusterManager) {
ClusterManager cmgr = (ClusterManager) manager;
// Notify our interested LifecycleListeners
fireLifecycleEvent(BEFORE_MANAGERUNREGISTER_EVENT,manager);
managers.remove(getManagerName(cmgr.getName(),manager));
cmgr.setCluster(null);
// Notify our interested LifecycleListeners
fireLifecycleEvent(AFTER_MANAGERUNREGISTER_EVENT, manager);
}
}
@Override
public String getManagerName(String name, Manager manager) {
String clusterName = name ;
if (clusterName == null) clusterName = manager.getContext().getName();
if (getContainer() instanceof Engine) {
Context context = manager.getContext();
Container host = context.getParent();
if (host instanceof Host && clusterName != null &&
!(clusterName.startsWith(host.getName() +"#"))) {
clusterName = host.getName() +"#" + clusterName ;
}
}
return clusterName;
}
@Override
public Manager getManager(String name) {
return 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()
*
*/
@Override
public void backgroundProcess() {
if (clusterDeployer != null) clusterDeployer.backgroundProcess();
//send a heartbeat through the channel
if ( isHeartbeatBackgroundEnabled() && channel !=null ) channel.heartbeat();
// periodic event
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
}
// ------------------------------------------------------ public
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (clusterDeployer != null) {
StringBuilder name = new StringBuilder("type=Cluster");
Container container = getContainer();
if (container != null) {
name.append(container.getMBeanKeyProperties());
}
name.append(",component=Deployer");
onameClusterDeployer = register(clusterDeployer, name.toString());
}
}
/**
* Start Cluster and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
if (log.isInfoEnabled()) log.info(sm.getString("simpleTcpCluster.start"));
try {
checkDefaults();
registerClusterValve();
channel.addMembershipListener(this);
channel.addChannelListener(this);
channel.setName(getClusterName() + "-Channel");
channel.start(channelStartOptions);
if (clusterDeployer != null) clusterDeployer.start();
registerMember(channel.getLocalMember(false));
} catch (Exception x) {
log.error(sm.getString("simpleTcpCluster.startUnable"), x);
throw new LifecycleException(x);
}
setState(LifecycleState.STARTING);
}
protected void checkDefaults() {
if ( clusterListeners.size() == 0 && managerTemplate instanceof DeltaManager ) {
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 MessageDispatchInterceptor());
channel.addInterceptor(new TcpFailureDetector());
}
if (heartbeatBackgroundEnabled) channel.setHeartbeat(false);
}
/**
* register all cluster valve to host or engine
*/
protected void registerClusterValve() {
if(container != null ) {
for (Iterator<Valve> 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) {
container.getPipeline().addValve(valve);
valve.setCluster(this);
}
}
}
}
/**
* unregister all cluster valve to host or engine
*/
protected void unregisterClusterValve() {
for (Iterator<Valve> 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) {
container.getPipeline().removeValve(valve);
valve.setCluster(null);
}
}
}
/**
* Stop Cluster and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
unregisterMember(channel.getLocalMember(false));
if (clusterDeployer != null) clusterDeployer.stop();
this.managers.clear();
try {
if ( clusterDeployer != null ) clusterDeployer.setCluster(null);
channel.stop(channelStartOptions);
channel.removeChannelListener(this);
channel.removeMembershipListener(this);
this.unregisterClusterValve();
} catch (Exception x) {
log.error(sm.getString("simpleTcpCluster.stopUnable"), x);
}
}
@Override
protected void destroyInternal() throws LifecycleException {
if (onameClusterDeployer != null) {
unregister(onameClusterDeployer);
onameClusterDeployer = null;
}
super.destroyInternal();
}
/**
* Return a String rendering of this object.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(this.getClass().getName());
sb.append('[');
if (container == null) {
sb.append("Container is null");
} else {
sb.append(container.getName());
}
sb.append(']');
return sb.toString();
}
/**
* send message to all cluster members
* @param msg message to transfer
*
* @see org.apache.catalina.ha.CatalinaCluster#send(org.apache.catalina.ha.ClusterMessage)
*/
@Override
public void send(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.tribes.Member)
*/
@Override
public void send(ClusterMessage msg, Member dest) {
try {
msg.setAddress(getLocalMember());
int sendOptions = channelSendOptions;
if (msg instanceof SessionMessage
&& ((SessionMessage)msg).getEventType() == SessionMessage.EVT_ALL_SESSION_DATA) {
sendOptions = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK|Channel.SEND_OPTIONS_USE_ACK;
}
if (dest != null) {
if (!getLocalMember().equals(dest)) {
channel.send(new Member[] {dest}, msg, sendOptions);
} else
log.error(sm.getString("simpleTcpCluster.unableSend.localMember", msg));
} else {
Member[] destmembers = channel.getMembers();
if (destmembers.length>0)
channel.send(destmembers,msg, sendOptions);
else if (log.isDebugEnabled())
log.debug("No members in cluster, ignoring message:"+msg);
}
} catch (Exception x) {
log.error(sm.getString("simpleTcpCluster.sendFailed"), x);
}
}
/**
* New cluster member is registered
*
* @see org.apache.catalina.tribes.MembershipListener#memberAdded(org.apache.catalina.tribes.Member)
*/
@Override
public void memberAdded(Member member) {
try {
hasMembers = channel.hasMembers();
if (log.isInfoEnabled()) log.info(sm.getString("simpleTcpCluster.member.added", member));
// Notify our interested LifecycleListeners
fireLifecycleEvent(BEFORE_MEMBERREGISTER_EVENT, member);
registerMember(member);
// Notify our interested LifecycleListeners
fireLifecycleEvent(AFTER_MEMBERREGISTER_EVENT, member);
} catch (Exception x) {
log.error(sm.getString("simpleTcpCluster.member.addFailed"), x);
}
}
/**
* Cluster member is gone
*
* @see org.apache.catalina.tribes.MembershipListener#memberDisappeared(org.apache.catalina.tribes.Member)
*/
@Override
public void memberDisappeared(Member member) {
try {
hasMembers = channel.hasMembers();
if (log.isInfoEnabled()) log.info(sm.getString("simpleTcpCluster.member.disappeared", member));
// Notify our interested LifecycleListeners
fireLifecycleEvent(BEFORE_MEMBERUNREGISTER_EVENT, member);
unregisterMember(member);
// Notify our interested LifecycleListeners
fireLifecycleEvent(AFTER_MEMBERUNREGISTER_EVENT, member);
} catch (Exception x) {
log.error(sm.getString("simpleTcpCluster.member.removeFailed"), x);
}
}
// --------------------------------------------------------- receiver
// messages
/**
* notify all listeners from receiving a new message is not ClusterMessage
* emit Failure Event to LifecycleListener
*
* @param msg
* received Message
*/
@Override
public boolean accept(Serializable msg, Member sender) {
return (msg instanceof ClusterMessage);
}
@Override
public void messageReceived(Serializable message, Member sender) {
ClusterMessage fwd = (ClusterMessage)message;
fwd.setAddress(sender);
messageReceived(fwd);
}
public void messageReceived(ClusterMessage message) {
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<ClusterListener> iter = clusterListeners.iterator();
iter.hasNext();) {
ClusterListener listener = iter.next();
if (listener.accept(message)) {
accepted = true;
listener.messageReceived(message);
}
}
if (!accepted && notifyLifecycleListenerOnFailure) {
Member dest = message.getAddress();
// Notify our interested LifecycleListeners
fireLifecycleEvent(RECEIVE_MESSAGE_FAILURE_EVENT,
new SendMessageData(message, dest, null));
if (log.isDebugEnabled()) {
log.debug("Message " + message.toString() + " from type "
+ message.getClass().getName()
+ " transferred but no listener registered");
}
}
}
}
public int getChannelStartOptions() {
return channelStartOptions;
}
public void setChannelStartOptions(int channelStartOptions) {
this.channelStartOptions = channelStartOptions;
}
// --------------------------------------------------------------------- JMX
@Override
protected String getDomainInternal() {
Container container = getContainer();
if (container == null) {
return null;
}
return container.getDomain();
}
@Override
protected String getObjectNameKeyProperties() {
StringBuilder name = new StringBuilder("type=Cluster");
Container container = getContainer();
if (container != null) {
name.append(container.getMBeanKeyProperties());
}
return name.toString();
}
private void registerMember(Member member) {
// JMX registration
StringBuilder name = new StringBuilder("type=Cluster");
Container container = getContainer();
if (container != null) {
name.append(container.getMBeanKeyProperties());
}
name.append(",component=Member,name=");
name.append(ObjectName.quote(member.getName()));
ObjectName oname = register(member, name.toString());
memberOnameMap.put(member, oname);
}
private void unregisterMember(Member member) {
if (member == null) return;
ObjectName oname = memberOnameMap.remove(member);
if (oname != null) {
unregister(oname);
}
}
}

View File

@@ -0,0 +1,152 @@
<?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="channelSendOptions"
description="This sets channel behaviour on sent messages."
type="int"/>
<attribute
name="channelStartOptions"
description="This sets channel start behaviour."
type="java.lang.String"/>
<attribute
name="clusterName"
description="name of cluster"
type="java.lang.String"/>
<attribute
name="heartbeatBackgroundEnabled"
description="enable that container background thread call channel heartbeat, default is that channel manage heartbeat itself."
is="true"
type="boolean"/>
<attribute
name="notifyLifecycleListenerOnFailure"
description="notify lifecycleListener from message transfer failure"
is="true"
type="boolean"/>
<attribute
name="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<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="start"
description="Start the cluster"
impact="ACTION"
returnType="void"/>
<operation
name="stop"
description="Stop the cluster"
impact="ACTION"
returnType="void"/>
</mbean>
<mbean
name="ReplicationValve"
description="Valve for simple tcp replication"
domain="Catalina"
group="Valve"
type="org.apache.catalina.ha.tcp.ReplicationValve">
<attribute
name="asyncSupported"
description="Does this valve support async reporting?"
is="true"
type="boolean"/>
<attribute
name="doProcessingStats"
getMethod="doStatistics"
setMethod="setStatistics"
description="active statistics counting"
type="boolean"/>
<attribute
name="filter"
description="resource filter to disable session replication check"
type="java.lang.String"/>
<attribute
name="lastSendTime"
description="last replicated request time"
type="long"
writeable="false"/>
<attribute
name="nrOfCrossContextSendRequests"
description="number of send cross context session 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="nrOfRequests"
description="number of replicated requests"
type="long"
writeable="false"/>
<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="stateName"
description="The name of the LifecycleState that this component is currently in"
type="java.lang.String"
writeable="false"/>
<attribute
name="totalSendTime"
description="total replicated send time"
type="long"
writeable="false"/>
<attribute
name="totalRequestTime"
description="total replicated request time"
type="long"
writeable="false"/>
<operation
name="resetStatistics"
description="Reset all statistics"
impact="ACTION"
returnType="void"/>
</mbean>
</mbeans-descriptors>