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,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>