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,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import org.apache.catalina.tribes.util.Arrays;
/**
* Manifest constants for the <code>org.apache.catalina.tribes.membership</code>
* package.
*
* @author Peter Rossbach
*/
public class Constants {
public static final String Package = "org.apache.catalina.tribes.membership";
public static void main(String[] args) throws Exception {
System.out.println(Arrays.toString("TRIBES-B".getBytes()));
System.out.println(Arrays.toString("TRIBES-E".getBytes()));
}
}

View File

@@ -0,0 +1,56 @@
# 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.
McastService.domain=Unable to send domain update
McastService.parseSoTimeout=Unable to parse SoTimeout: [{0}]
McastService.parseTTL=Unable to parse TTL: [{0}]
McastService.payload=Unable to send payload update
McastService.stopFail=Unable to stop the mcast service, level: [{0}]
mcastService.exceed.maxPacketSize=Packet length[{0}] exceeds max packet size of [{1}] bytes.
mcastService.missing.property=McastService:Required property [{0}] is missing.
mcastService.noStart=Multicast send is not started or enabled.
mcastServiceImpl.bind=Attempting to bind the multicast socket to [{0}:{1}]
mcastServiceImpl.bind.failed=Binding to multicast address, failed. Binding to port only.
mcastServiceImpl.error.receiving=Error receiving mcast package. Sleeping 500ms
mcastServiceImpl.invalid.startLevel=Invalid start level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ
mcastServiceImpl.invalid.stopLevel=Invalid stop level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ
mcastServiceImpl.memberDisappeared.failed=Unable to process member disappeared message.
mcastServiceImpl.packet.tooLong=Multicast packet received was too long, dropping package:[{0}]
mcastServiceImpl.receive.running=McastService.receive already running.
mcastServiceImpl.recovery=Tribes membership, running recovery thread, multicasting is not functional.
mcastServiceImpl.recovery.failed=Recovery attempt number [{0}] failed, trying again in [{1}] seconds
mcastServiceImpl.recovery.startFailed=Recovery thread failed to start membership service.
mcastServiceImpl.recovery.stopFailed=Recovery thread failed to stop membership service.
mcastServiceImpl.recovery.successful=Membership recovery was successful.
mcastServiceImpl.send.failed=Unable to send mcast message.
mcastServiceImpl.send.running=McastService.send already running.
mcastServiceImpl.setInterface=Setting multihome multicast interface to:[{0}]
mcastServiceImpl.setSoTimeout=Setting cluster mcast soTimeout to [{0}]
mcastServiceImpl.setTTL=Setting cluster mcast TTL to [{0}]
mcastServiceImpl.unable.join=Unable to join multicast group, make sure your system has multicasting enabled.
mcastServiceImpl.unableReceive.broadcastMessage=Unable to receive broadcast message.
mcastServiceImpl.waitForMembers.done=Done sleeping, membership established, start level:[{0}]
mcastServiceImpl.waitForMembers.start=Sleeping for [{0}] milliseconds to establish cluster membership, start level:[{1}]
memberImpl.invalid.package.begin=Invalid package, should start with:[{0}]
memberImpl.invalid.package.end=Invalid package, should end with:[{0}]
memberImpl.large.payload=Payload is too large for tribes to handle.
memberImpl.notEnough.bytes=Not enough bytes in member package.
memberImpl.package.small=Member package too small to validate.
memberImpl.unableParse.hostname=Unable to parse hostname.
staticMember.invalid.uuidLength=UUID must be exactly 16 bytes, not:[{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.
mcastServiceImpl.bind=Versuche Multicast Socket an [{0}:{1}] zu binden
mcastServiceImpl.recovery.failed=Wiederherstellungsversuch Nummer [{0}] ist fehlgeschlagen, versuche es in [{1}] Sekunden erneut
mcastServiceImpl.send.failed=Eine mcast-Nachricht kann nicht gesendet werden.
mcastServiceImpl.send.running=McastService.send läuft bereits
mcastServiceImpl.setSoTimeout=Setze Cluster mcast soTimeout auf [{0}]
mcastServiceImpl.setTTL=Setze Cluster mcast TTL auf [{0}]
mcastServiceImpl.unableReceive.broadcastMessage=Konnte Broadcast-Nachricht nicht empfangen.
memberImpl.notEnough.bytes=Nicht genug bytes im Mitgliederpaket

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.
mcastServiceImpl.bind=Tratando de atar el socket multicast a [{0}:{1}]\n
mcastServiceImpl.recovery=Membresía Tribes ejecutando hilo de recuperación, multicasting no esta operativo.
mcastServiceImpl.send.running=McastService.send aún esta corriendo.
mcastServiceImpl.setInterface=Fijando interfaz multicase multihme a:[{0}]\n
mcastServiceImpl.setSoTimeout=Fijando cluster mcast para tiempo de espera gotado en [{0}]
mcastServiceImpl.setTTL=Fijando cluster cluster mcast TTL a [{0}]\n
mcastServiceImpl.unableReceive.broadcastMessage=Incapaz de recibir el mensaje de broadcast
memberImpl.notEnough.bytes=No hay suficientes bytes en el paquete miembro

View File

@@ -0,0 +1,56 @@
# 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.
McastService.domain=Impossible d'envoyer la mise à jour du domaine
McastService.parseSoTimeout=Impossible de lire SoTimeout: [{0}]
McastService.parseTTL=Impossible d''analyser le TTL: [{0}]
McastService.payload=Impossible d'envoyer les données de mise à jour
McastService.stopFail=Impossible d''arrêter le service mcast, niveau [{0}]
mcastService.exceed.maxPacketSize=La taille du paquet [{0}] excède la taille maximale qui est de [{1}] octets
mcastService.missing.property=McastService:La propriété obligatoire [{0}] manque.
mcastService.noStart=L'envoi multicast n'est pas démarré ou activé
mcastServiceImpl.bind=Tentive d'associer le socket multicast à [{0}:{1}]
mcastServiceImpl.bind.failed=Echec de l'association à ladresse multicast, association uniquement sur le port
mcastServiceImpl.error.receiving=Erreur en recevant un paquet multicast, attente de 500ms
mcastServiceImpl.invalid.startLevel=Niveau de départ invalide. Les seuls niveaux acceptables sont Channel.MBR_RX_SEQ et Channel.MBR_TX_SEQ
mcastServiceImpl.invalid.stopLevel=Niveau de stop invalide, les seuls niveaux valides sont Channel.MBR_RX_SEQ et Channel.MBR_TX_SEQ
mcastServiceImpl.memberDisappeared.failed=Impossible de traiter le message indiquant un membre disparu
mcastServiceImpl.packet.tooLong=Le paquet multicast reçu est trop long, il est abandonné: [{0}]
mcastServiceImpl.receive.running=McastService.receive est déjà en cours d'exécution
mcastServiceImpl.recovery=Le multicast est non fonctionnel, le registre de membres de Tribes exécute le processus de récupération
mcastServiceImpl.recovery.failed=La tentative de récupération numéro [{0}] échouée, nouvel essai dans [{1}] secondes
mcastServiceImpl.recovery.startFailed=Le thread de récupération n'a pas pu démarrer le registre de membres
mcastServiceImpl.recovery.stopFailed=Le thread de récupération n'a pu arrêter le registre de membres
mcastServiceImpl.recovery.successful=Succès de récupération du registre de membres
mcastServiceImpl.send.failed=Impossible d'envoyer le message mcast.
mcastServiceImpl.send.running=McastService.send est déjà en cours d'exécution.
mcastServiceImpl.setInterface=Définition de l''interface multicast multi réseaux comme [{0}]
mcastServiceImpl.setSoTimeout=Réglage du mcast soTimeout du cluster à [{0}]
mcastServiceImpl.setTTL=Le multicast TTL du cluster est fixé à [{0}]
mcastServiceImpl.unable.join=Incapable de rejoindre le le groupe de multidiffusion ("multicast"). Assurez-vous que la multidiffusion est activée sur votre système.
mcastServiceImpl.unableReceive.broadcastMessage=N'a pas pu recevoir de message général (broadcast)
mcastServiceImpl.waitForMembers.done=Fin de l''attente, le registre de membres est établi, démarrage de niveau: [{0}]
mcastServiceImpl.waitForMembers.start=Attente de [{0}] millisecondes pour établir le registre de membres du cluster, démarrage au niveau [{1}]
memberImpl.invalid.package.begin=Le paquet est invalide, il devrait démarrer par [{0}]
memberImpl.invalid.package.end=le paquet est invalide, il devrait se terminer par: [{0}]
memberImpl.large.payload=Le contenu est trop gros pour être géré par Tribes
memberImpl.notEnough.bytes=Pas assez d'octets dans le paquet membre
memberImpl.package.small=Le paquet du membre est trop petit pour être validé
memberImpl.unableParse.hostname=Incapable d'analyser le nom d'hôte (hostname)
staticMember.invalid.uuidLength=Un UUID doit faire exactement 16 octets et non [{0}]

View File

@@ -0,0 +1,56 @@
# 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.
McastService.domain=ドメイン更新の送信が出来ません。
McastService.parseSoTimeout=SoTimeoutを解析できません[{0}]
McastService.parseTTL=TTL 値を解釈できません: [{0}]
McastService.payload=Payload更新の送信が出来ません。
McastService.stopFail=マルチキャストサービスを停止できません。レベル:[{0}]
mcastService.exceed.maxPacketSize=パケット長[{0}]は[{1}]バイトの最大パケットサイズを超えています。
mcastService.missing.property=McastService: 必須プロパティ [{0}] がありません。
mcastService.noStart=マルチキャスト送信が開始または有効になっていません。
mcastServiceImpl.bind=[{0}:{1}] にマルチキャストソケットを束縛します。
mcastServiceImpl.bind.failed=マルチキャストアドレスへのバインドに失敗しました。 ポートのみにバインドします。
mcastServiceImpl.error.receiving=マルチキャストパッケージを受信中にエラーが発生しました。 500msスリープします。
mcastServiceImpl.invalid.startLevel=不正な開始レベルです。受け付けられるのは Channel.MBR_RX_SEQ と Channel.MBR_TX_SEQ です。
mcastServiceImpl.invalid.stopLevel=無効な停止レベルです。 受け入れ可能なレベルはChannel.MBR_RX_SEQとChannel.MBR_TX_SEQのみです。
mcastServiceImpl.memberDisappeared.failed=メンバー消失メッセージを処理できません。
mcastServiceImpl.packet.tooLong=受信したマルチキャストパケットが大きすぎるためパッケージを破棄します。受信したパケットサイズは [{0}] です。
mcastServiceImpl.receive.running=McastService.receive は既に実行中です。
mcastServiceImpl.recovery=リカバリスレッドがメンバーシップを復旧するため、マルチキャストできなくなります。
mcastServiceImpl.recovery.failed=リカバリの試み[{0}]が失敗しました。[{1}]秒後に再試行します。
mcastServiceImpl.recovery.startFailed=リカバリースレッドはメンバーシップサービスを開始できません。
mcastServiceImpl.recovery.stopFailed=リカバリースレッドはメンバーシップサービスを停止できません。
mcastServiceImpl.recovery.successful=メンバーシップの回復に成功しました。
mcastServiceImpl.send.failed=マルチキャストメッセージを送信出来ません。
mcastServiceImpl.send.running=McastService.sendはすでに実行中です。
mcastServiceImpl.setInterface=マルチホームマルチキャストインターフェイスを構成しました。 : [{0}]
mcastServiceImpl.setSoTimeout=クラスタ マルチキャスト soTimeoutを[{0}]に設定します
mcastServiceImpl.setTTL=クラスターのマルチキャスト TTL を [{0}] に設定しました。
mcastServiceImpl.unable.join=マルチキャストグループに参加できません、システムでマルチキャストが有効になっていることを確認してください。
mcastServiceImpl.unableReceive.broadcastMessage=ブロードキャストメッセージを受信できません。
mcastServiceImpl.waitForMembers.done=sleep完了、メンバーシップを確立し、開始レベル[{0}]
mcastServiceImpl.waitForMembers.start=クラスタメンバシップを確立するために[{0}]ミリ秒スリープ、開始レベル:[{1}]
memberImpl.invalid.package.begin=パッケージが不正です。[{0}] から開始しなければなりません。
memberImpl.invalid.package.end=不正なパッケージです。[{0}] で終端しなければなりません。
memberImpl.large.payload=ペイロードはtribes が処理するには大きすぎます。
memberImpl.notEnough.bytes=メンバーパッケージのバイト長が不足しています。
memberImpl.package.small=メンバーパッケージが小さすぎて検証できません。
memberImpl.unableParse.hostname=ホスト名を解析できません。
staticMember.invalid.uuidLength=UUIDは正確に16バイトでなければなりません。[{0}]

View File

@@ -0,0 +1,56 @@
# 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.
McastService.domain=도메인 변경 메시지를 보낼 수 없습니다.
McastService.parseSoTimeout=SoTimeout을 파싱할 수 없습니다: [{0}]
McastService.parseTTL=TTL을 파싱할 수 없습니다: [{0}]
McastService.payload=Payload 변경 메시지를 보낼 수 없습니다.
McastService.stopFail=멀티캐스트 서비스를 중단시킬 수 없습니다. 레벨: [{0}]
mcastService.exceed.maxPacketSize=패킷 길이 [{0}]이(가), 최대 패킷 크기인 [{1}] 바이트를 초과합니다.
mcastService.missing.property=McastService: 필수 프로퍼티 [{0}]이(가) 없습니다.
mcastService.noStart=멀티캐스트 전송이 시작되지 않았거나 사용가능 상태가 아닙니다.
mcastServiceImpl.bind=멀티캐스트 소켓을 [{0}:{1}](으)로 바인딩하려 시도 중
mcastServiceImpl.bind.failed=멀티캐스트 주소로 바인딩하지 못했습니다. 포트로만 바인딩합니다.
mcastServiceImpl.error.receiving=멀티캐스트 패키지를 받는 중 오류 발생. 500 밀리초 동안 잠에 들겠습니다.
mcastServiceImpl.invalid.startLevel=유효하지 않은 시작 레벨. 허용되는 레벨들은 Channel.MBR_RX_SEQ와 Channel.MBR_TX_SEQ 뿐입니다.
mcastServiceImpl.invalid.stopLevel=유효하지 않은 중지 레벨. 허용 레벨들은 Channel.MBR_RX_SEQ와 Channel.MBR_TX_SEQ입니다.
mcastServiceImpl.memberDisappeared.failed=멤버 사라짐 메시지를 처리할 수 없습니다.
mcastServiceImpl.packet.tooLong=수신된 멀티캐스트 패킷이 너무 깁니다. 패키지를 무시합니다: [{0}]
mcastServiceImpl.receive.running=McastService.receive가 이미 실행 중입니다.
mcastServiceImpl.recovery=Tribes 멤버십을 복구하려고, 별도의 쓰레드에서 멀티캐스팅을 시도하고 있습니다.
mcastServiceImpl.recovery.failed=복구 시도가 [{0}]회 실패했습니다. [{1}] 초 후에 다시 시도 하겠습니다.
mcastServiceImpl.recovery.startFailed=복구 쓰레드가 멤버십 서비스를 시작하지 못했습니다.
mcastServiceImpl.recovery.stopFailed=복구 쓰레드가 멤버십 서비스를 중지시키지 못했습니다.
mcastServiceImpl.recovery.successful=멤버십 복구가 성공적이었습니다.
mcastServiceImpl.send.failed=멀티캐스트 메시지를 보낼 수 없습니다.
mcastServiceImpl.send.running=McastService.send가 이미 실행 중입니다.
mcastServiceImpl.setInterface=멀티홈 멀티캐스트 인터페이스를 [{0}]에 설정합니다.
mcastServiceImpl.setSoTimeout=클러스터 멀티캐스트의 soTimeout을 [{0}](으)로 설정합니다.
mcastServiceImpl.setTTL=클러스터 멀티캐스트 TTL을 [{0}](으)로 설정합니다.
mcastServiceImpl.unable.join=멀티캐스트 그룹에 참가할 수 없습니다. 귀하의 시스템이 멀티캐스팅을 사용 가능하도록 설정되었는지 확인하십시오.
mcastServiceImpl.unableReceive.broadcastMessage=브로드캐스트 메시지를 수신할 수 없습니다.
mcastServiceImpl.waitForMembers.done=잠들기가 끝났습니다. 멤버십이 확립되었고, 시작 레벨은 [{0}]입니다.
mcastServiceImpl.waitForMembers.start=클러스터 멤버십을 확립하기 위해, [{0}] 밀리초 동안 잠에 듭니다. 시작 레벨: [{1}]
memberImpl.invalid.package.begin=유효하지 않은 패키지입니다. [{0}](으)로 시작해야 합니다.
memberImpl.invalid.package.end=유효하지 않은 패키지. [{0}](으)로 끝나야 합니다.
memberImpl.large.payload=Payload가 너무 커서 tribe들이 처리할 수 없습니다.
memberImpl.notEnough.bytes=멤버 데이터 바이트 배열에 충분한 데이터가 존재하지 않습니다.
memberImpl.package.small=멤버 패키지가 너무 작아서, 유효한지 확인할 수 없습니다.
memberImpl.unableParse.hostname=호스트 이름을 파싱할 수 없습니다.
staticMember.invalid.uuidLength=UUID는 정확히 16 바이트여야만 합니다. [{0}]이어서는 안됩니다.

View File

@@ -0,0 +1,41 @@
# 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.
McastService.parseSoTimeout=无法解析SoTimeout[{0}]
McastService.payload=无法发送负载更新
mcastService.missing.property=McastService缺少必需属性 [{0}]。
mcastServiceImpl.bind=尝试将多播套接字绑定到 [{0}:{1}]
mcastServiceImpl.bind.failed=绑定到多播地址失败。仅绑定到端口。
mcastServiceImpl.error.receiving=接收mcast包时出错。睡眠500毫秒
mcastServiceImpl.invalid.startLevel=无效的启动级别。只接受以下级别Channel.MBR_RX_SEQ或 Channel.MBR_TX_SEQ
mcastServiceImpl.invalid.stopLevel=无效的停止级别。只有Channel.MBR_RX_SEQ和Channel.MBR_TX_SEQ是可接受的级别
mcastServiceImpl.recovery=家族成员,运行恢复线程,广播不是功能。
mcastServiceImpl.recovery.stopFailed=恢复线程未能停止成员服务。
mcastServiceImpl.recovery.successful=成员身份恢复成功。
mcastServiceImpl.send.failed=无法发送多播信息
mcastServiceImpl.send.running=McastService.send已经运行
mcastServiceImpl.setInterface=设置多宿主多播接口为:[{0}]
mcastServiceImpl.setSoTimeout=设置集群多播超时时间:[{0}]
mcastServiceImpl.setTTL=设置集群多播TTL[{0}]
mcastServiceImpl.unable.join=无法加入多播组,请确保你的系统已启用多播。
mcastServiceImpl.unableReceive.broadcastMessage=无法接收广播消息。
memberImpl.large.payload=负载太大以至于难以处理
memberImpl.notEnough.bytes=成员包中的字节不够。
memberImpl.package.small=成员包太小以至于不能校验。
staticMember.invalid.uuidLength=UUID必须正好是16个字节而不是[{0}]

View File

@@ -0,0 +1,653 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import java.io.IOException;
import java.net.DatagramPacket;
import java.util.Properties;
import javax.management.ObjectName;
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.ChannelException;
import org.apache.catalina.tribes.ChannelMessage;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.MembershipListener;
import org.apache.catalina.tribes.MembershipService;
import org.apache.catalina.tribes.MessageListener;
import org.apache.catalina.tribes.io.ChannelData;
import org.apache.catalina.tribes.io.XByteBuffer;
import org.apache.catalina.tribes.jmx.JmxRegistry;
import org.apache.catalina.tribes.util.Arrays;
import org.apache.catalina.tribes.util.StringManager;
import org.apache.catalina.tribes.util.UUIDGenerator;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* A <b>membership</b> implementation using simple multicast.
* This is the representation of a multicast membership service.
* This class is responsible for maintaining a list of active cluster nodes in the cluster.
* If a node fails to send out a heartbeat, the node will be dismissed.
*/
public class McastService
implements MembershipService,MembershipListener,MessageListener, McastServiceMBean {
private static final Log log = LogFactory.getLog(McastService.class);
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(Constants.Package);
/**
* The implementation specific properties
*/
protected Properties properties = new Properties();
/**
* A handle to the actual low level implementation
*/
protected McastServiceImpl impl;
/**
* A membership listener delegate (should be the cluster :)
*/
protected volatile MembershipListener listener;
/**
* A message listener delegate for broadcasts
*/
protected MessageListener msglistener;
/**
* The local member
*/
protected MemberImpl localMember ;
private int mcastSoTimeout;
private int mcastTTL;
protected byte[] payload;
protected byte[] domain;
private Channel channel;
/**
* the ObjectName of this McastService.
*/
private ObjectName oname = null;
/**
* Create a membership service.
*/
public McastService() {
//default values
setDefaults(this.properties);
}
/**
* Sets the properties for the membership service.
* @param properties
* <br>All are required<br>
* 1. mcastPort - the port to listen to<BR>
* 2. mcastAddress - the mcast group address<BR>
* 4. bindAddress - the bind address if any - only one that can be null<BR>
* 5. memberDropTime - the time a member is gone before it is considered gone.<BR>
* 6. mcastFrequency - the frequency of sending messages<BR>
* 7. tcpListenPort - the port this member listens to<BR>
* 8. tcpListenHost - the bind address of this member<BR>
* @exception java.lang.IllegalArgumentException if a property is missing.
*/
@Override
public void setProperties(Properties properties) {
hasProperty(properties,"mcastPort");
hasProperty(properties,"mcastAddress");
hasProperty(properties,"memberDropTime");
hasProperty(properties,"mcastFrequency");
hasProperty(properties,"tcpListenPort");
hasProperty(properties,"tcpListenHost");
setDefaults(properties);
this.properties = properties;
}
/**
* {@inheritDoc}
*/
@Override
public Properties getProperties() {
return properties;
}
/**
* @return the local member name
*/
@Override
public String getLocalMemberName() {
return localMember.toString() ;
}
/**
* {@inheritDoc}
*/
@Override
public Member getLocalMember(boolean alive) {
if ( alive && localMember != null && impl != null) localMember.setMemberAliveTime(System.currentTimeMillis()-impl.getServiceStartTime());
return localMember;
}
/**
* {@inheritDoc}
*/
@Override
public void setLocalMemberProperties(String listenHost, int listenPort, int securePort, int udpPort) {
properties.setProperty("tcpListenHost",listenHost);
properties.setProperty("tcpListenPort",String.valueOf(listenPort));
properties.setProperty("udpListenPort",String.valueOf(udpPort));
properties.setProperty("tcpSecurePort",String.valueOf(securePort));
try {
if (localMember != null) {
localMember.setHostname(listenHost);
localMember.setPort(listenPort);
} else {
localMember = new MemberImpl(listenHost, listenPort, 0);
localMember.setUniqueId(UUIDGenerator.randomUUID(true));
localMember.setPayload(getPayload());
localMember.setDomain(getDomain());
localMember.setLocal(true);
}
localMember.setSecurePort(securePort);
localMember.setUdpPort(udpPort);
localMember.getData(true, true);
}catch ( IOException x ) {
throw new IllegalArgumentException(x);
}
}
public void setAddress(String addr) {
properties.setProperty("mcastAddress", addr);
}
@Override
public String getAddress() {
return properties.getProperty("mcastAddress");
}
public void setMcastBindAddress(String bindaddr) {
setBind(bindaddr);
}
public void setBind(String bindaddr) {
properties.setProperty("mcastBindAddress", bindaddr);
}
@Override
public String getBind() {
return properties.getProperty("mcastBindAddress");
}
public void setPort(int port) {
properties.setProperty("mcastPort", String.valueOf(port));
}
public void setRecoveryCounter(int recoveryCounter) {
properties.setProperty("recoveryCounter", String.valueOf(recoveryCounter));
}
@Override
public int getRecoveryCounter(){
String p = properties.getProperty("recoveryCounter");
if(p != null){
return Integer.parseInt(p);
}
return -1;
}
public void setRecoveryEnabled(boolean recoveryEnabled) {
properties.setProperty("recoveryEnabled", String.valueOf(recoveryEnabled));
}
@Override
public boolean getRecoveryEnabled() {
String p = properties.getProperty("recoveryEnabled");
if(p != null){
return Boolean.parseBoolean(p);
}
return false;
}
public void setRecoverySleepTime(long recoverySleepTime) {
properties.setProperty("recoverySleepTime", String.valueOf(recoverySleepTime));
}
@Override
public long getRecoverySleepTime(){
String p = properties.getProperty("recoverySleepTime");
if(p != null){
return Long.parseLong(p);
}
return -1;
}
public void setLocalLoopbackDisabled(boolean localLoopbackDisabled) {
properties.setProperty("localLoopbackDisabled",String.valueOf(localLoopbackDisabled));
}
@Override
public boolean getLocalLoopbackDisabled() {
String p = properties.getProperty("localLoopbackDisabled");
if(p != null){
return Boolean.parseBoolean(p);
}
return false;
}
@Override
public int getPort() {
String p = properties.getProperty("mcastPort");
return Integer.parseInt(p);
}
public void setFrequency(long time) {
properties.setProperty("mcastFrequency", String.valueOf(time));
}
@Override
public long getFrequency() {
String p = properties.getProperty("mcastFrequency");
return Long.parseLong(p);
}
public void setMcastDropTime(long time) {
setDropTime(time);
}
public void setDropTime(long time) {
properties.setProperty("memberDropTime", String.valueOf(time));
}
@Override
public long getDropTime() {
String p = properties.getProperty("memberDropTime");
return Long.parseLong(p);
}
/**
* Check if a required property is available.
* @param properties The set of properties
* @param name The property to check for
*/
protected void hasProperty(Properties properties, String name){
if ( properties.getProperty(name)==null) throw new IllegalArgumentException(sm.getString("mcastService.missing.property", name));
}
/**
* Start broadcasting and listening to membership pings
* @throws java.lang.Exception if a IO error occurs
*/
@Override
public void start() throws java.lang.Exception {
start(MembershipService.MBR_RX);
start(MembershipService.MBR_TX);
}
@Override
public void start(int level) throws java.lang.Exception {
hasProperty(properties,"mcastPort");
hasProperty(properties,"mcastAddress");
hasProperty(properties,"memberDropTime");
hasProperty(properties,"mcastFrequency");
hasProperty(properties,"tcpListenPort");
hasProperty(properties,"tcpListenHost");
hasProperty(properties,"tcpSecurePort");
hasProperty(properties,"udpListenPort");
if ( impl != null ) {
impl.start(level);
return;
}
String host = getProperties().getProperty("tcpListenHost");
int port = Integer.parseInt(getProperties().getProperty("tcpListenPort"));
int securePort = Integer.parseInt(getProperties().getProperty("tcpSecurePort"));
int udpPort = Integer.parseInt(getProperties().getProperty("udpListenPort"));
if ( localMember == null ) {
localMember = new MemberImpl(host, port, 100);
localMember.setUniqueId(UUIDGenerator.randomUUID(true));
localMember.setLocal(true);
} else {
localMember.setHostname(host);
localMember.setPort(port);
localMember.setMemberAliveTime(100);
}
localMember.setSecurePort(securePort);
localMember.setUdpPort(udpPort);
if ( this.payload != null ) localMember.setPayload(payload);
if ( this.domain != null ) localMember.setDomain(domain);
localMember.setServiceStartTime(System.currentTimeMillis());
java.net.InetAddress bind = null;
if ( properties.getProperty("mcastBindAddress")!= null ) {
bind = java.net.InetAddress.getByName(properties.getProperty("mcastBindAddress"));
}
int ttl = -1;
int soTimeout = -1;
if ( properties.getProperty("mcastTTL") != null ) {
try {
ttl = Integer.parseInt(properties.getProperty("mcastTTL"));
} catch ( Exception x ) {
log.error(sm.getString("McastService.parseTTL",
properties.getProperty("mcastTTL")), x);
}
}
if ( properties.getProperty("mcastSoTimeout") != null ) {
try {
soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout"));
} catch ( Exception x ) {
log.error(sm.getString("McastService.parseSoTimeout",
properties.getProperty("mcastSoTimeout")), x);
}
}
impl = new McastServiceImpl(localMember,Long.parseLong(properties.getProperty("mcastFrequency")),
Long.parseLong(properties.getProperty("memberDropTime")),
Integer.parseInt(properties.getProperty("mcastPort")),
bind,
java.net.InetAddress.getByName(properties.getProperty("mcastAddress")),
ttl,
soTimeout,
this,
this,
Boolean.parseBoolean(properties.getProperty("localLoopbackDisabled")));
String value = properties.getProperty("recoveryEnabled");
boolean recEnabled = Boolean.parseBoolean(value);
impl.setRecoveryEnabled(recEnabled);
int recCnt = Integer.parseInt(properties.getProperty("recoveryCounter"));
impl.setRecoveryCounter(recCnt);
long recSlpTime = Long.parseLong(properties.getProperty("recoverySleepTime"));
impl.setRecoverySleepTime(recSlpTime);
impl.setChannel(channel);
impl.start(level);
// register jmx
JmxRegistry jmxRegistry = JmxRegistry.getRegistry(channel);
if (jmxRegistry != null) {
this.oname = jmxRegistry.registerJmx(",component=Membership", this);
}
}
/**
* Stop broadcasting and listening to membership pings
*/
@Override
public void stop(int svc) {
try {
if ( impl != null && impl.stop(svc) ) {
if (oname != null) {
JmxRegistry.getRegistry(channel).unregisterJmx(oname);
oname = null;
}
impl.setChannel(null);
impl = null;
channel = null;
}
} catch ( Exception x) {
log.error(sm.getString(
"McastService.stopFail", Integer.valueOf(svc)), x);
}
}
/**
* Return all the members by name
*/
@Override
public String[] getMembersByName() {
Member[] currentMembers = getMembers();
String [] membernames ;
if(currentMembers != null) {
membernames = new String[currentMembers.length];
for (int i = 0; i < currentMembers.length; i++) {
membernames[i] = currentMembers[i].toString() ;
}
} else
membernames = new String[0] ;
return membernames ;
}
/**
* Return the member by name
*/
@Override
public Member findMemberByName(String name) {
Member[] currentMembers = getMembers();
for (int i = 0; i < currentMembers.length; i++) {
if (name.equals(currentMembers[i].toString()))
return currentMembers[i];
}
return null;
}
/**
* has members?
*/
@Override
public boolean hasMembers() {
if ( impl == null || impl.membership == null ) return false;
return impl.membership.hasMembers();
}
@Override
public Member getMember(Member mbr) {
if ( impl == null || impl.membership == null ) return null;
return impl.membership.getMember(mbr);
}
/**
* Return all the members
*/
protected static final Member[]EMPTY_MEMBERS = new Member[0];
@Override
public Member[] getMembers() {
if ( impl == null || impl.membership == null ) return EMPTY_MEMBERS;
return impl.membership.getMembers();
}
/**
* Add a membership listener, this version only supports one listener per service,
* so calling this method twice will result in only the second listener being active.
* @param listener The listener
*/
@Override
public void setMembershipListener(MembershipListener listener) {
this.listener = listener;
}
public void setMessageListener(MessageListener listener) {
this.msglistener = listener;
}
public void removeMessageListener() {
this.msglistener = null;
}
/**
* Remove the membership listener
*/
@Override
public void removeMembershipListener(){
listener = null;
}
@Override
public void memberAdded(Member member) {
MembershipListener listener = this.listener;
if (listener != null) {
listener.memberAdded(member);
}
}
/**
* Callback from the impl when a new member has been received
* @param member The member
*/
@Override
public void memberDisappeared(Member member) {
MembershipListener listener = this.listener;
if (listener != null) {
listener.memberDisappeared(member);
}
}
@Override
public void messageReceived(ChannelMessage msg) {
if (msglistener!=null && msglistener.accept(msg)) msglistener.messageReceived(msg);
}
@Override
public boolean accept(ChannelMessage msg) {
return true;
}
@Override
public void broadcast(ChannelMessage message) throws ChannelException {
if (impl==null || (impl.startLevel & Channel.MBR_TX_SEQ)!=Channel.MBR_TX_SEQ )
throw new ChannelException(sm.getString("mcastService.noStart"));
byte[] data = XByteBuffer.createDataPackage((ChannelData)message);
if (data.length>McastServiceImpl.MAX_PACKET_SIZE) {
throw new ChannelException(sm.getString("mcastService.exceed.maxPacketSize",
Integer.toString(data.length) ,
Integer.toString(McastServiceImpl.MAX_PACKET_SIZE)));
}
DatagramPacket packet = new DatagramPacket(data,0,data.length);
try {
impl.send(false, packet);
} catch (Exception x) {
throw new ChannelException(x);
}
}
@Override
public int getSoTimeout() {
return mcastSoTimeout;
}
public void setSoTimeout(int mcastSoTimeout) {
this.mcastSoTimeout = mcastSoTimeout;
properties.setProperty("mcastSoTimeout", String.valueOf(mcastSoTimeout));
}
@Override
public int getTtl() {
return mcastTTL;
}
public byte[] getPayload() {
return payload;
}
@Override
public byte[] getDomain() {
return domain;
}
public void setTtl(int mcastTTL) {
this.mcastTTL = mcastTTL;
properties.setProperty("mcastTTL", String.valueOf(mcastTTL));
}
@Override
public void setPayload(byte[] payload) {
this.payload = payload;
if ( localMember != null ) {
localMember.setPayload(payload);
try {
if (impl != null) impl.send(false);
}catch ( Exception x ) {
log.error(sm.getString("McastService.payload"), x);
}
}
}
@Override
public void setDomain(byte[] domain) {
this.domain = domain;
if ( localMember != null ) {
localMember.setDomain(domain);
try {
if (impl != null) impl.send(false);
}catch ( Exception x ) {
log.error(sm.getString("McastService.domain"), x);
}
}
}
public void setDomain(String domain) {
if ( domain == null ) return;
if ( domain.startsWith("{") ) setDomain(Arrays.fromString(domain));
else setDomain(Arrays.convert(domain));
}
@Override
public Channel getChannel() {
return channel;
}
@Override
public void setChannel(Channel channel) {
this.channel = channel;
}
protected void setDefaults(Properties properties) {
// default values
if (properties.getProperty("mcastPort") == null)
properties.setProperty("mcastPort","45564");
if (properties.getProperty("mcastAddress") == null)
properties.setProperty("mcastAddress","228.0.0.4");
if (properties.getProperty("memberDropTime") == null)
properties.setProperty("memberDropTime","3000");
if (properties.getProperty("mcastFrequency") == null)
properties.setProperty("mcastFrequency","500");
if (properties.getProperty("recoveryCounter") == null)
properties.setProperty("recoveryCounter", "10");
if (properties.getProperty("recoveryEnabled") == null)
properties.setProperty("recoveryEnabled", "true");
if (properties.getProperty("recoverySleepTime") == null)
properties.setProperty("recoverySleepTime", "5000");
if (properties.getProperty("localLoopbackDisabled") == null)
properties.setProperty("localLoopbackDisabled", "false");
}
/**
* Simple test program
* @param args Command-line arguments
* @throws Exception If an error occurs
*/
public static void main(String args[]) throws Exception {
McastService service = new McastService();
java.util.Properties p = new java.util.Properties();
p.setProperty("mcastPort","5555");
p.setProperty("mcastAddress","224.10.10.10");
p.setProperty("mcastClusterDomain","catalina");
p.setProperty("bindAddress","localhost");
p.setProperty("memberDropTime","3000");
p.setProperty("mcastFrequency","500");
p.setProperty("tcpListenPort","4000");
p.setProperty("tcpListenHost","127.0.0.1");
p.setProperty("tcpSecurePort","4100");
p.setProperty("udpListenPort","4200");
service.setProperties(p);
service.start();
Thread.sleep(60*1000*60);
}
}

View File

@@ -0,0 +1,695 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import java.io.IOException;
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.catalina.tribes.Channel;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.MembershipListener;
import org.apache.catalina.tribes.MessageListener;
import org.apache.catalina.tribes.io.ChannelData;
import org.apache.catalina.tribes.io.XByteBuffer;
import org.apache.catalina.tribes.util.ExecutorFactory;
import org.apache.catalina.tribes.util.StringManager;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
/**
* A <b>membership</b> implementation using simple multicast.
* This is the representation of a multicast membership service.
* This class is responsible for maintaining a list of active cluster nodes in the cluster.
* If a node fails to send out a heartbeat, the node will be dismissed.
* This is the low level implementation that handles the multicasting sockets.
* Need to fix this, could use java.nio and only need one thread to send and receive, or
* just use a timeout on the receive
*/
public class McastServiceImpl {
private static final Log log = LogFactory.getLog(McastService.class);
protected static final int MAX_PACKET_SIZE = 65535;
protected static final StringManager sm = StringManager.getManager(Constants.Package);
/**
* Internal flag used for the listen thread that listens to the multicasting socket.
*/
protected volatile boolean doRunSender = false;
protected volatile boolean doRunReceiver = false;
protected volatile int startLevel = 0;
/**
* Socket that we intend to listen to
*/
protected MulticastSocket socket;
/**
* The local member that we intend to broad cast over and over again
*/
protected final MemberImpl member;
/**
* The multicast address
*/
protected final InetAddress address;
/**
* The multicast port
*/
protected final int port;
/**
* The time it takes for a member to expire.
*/
protected final long timeToExpiration;
/**
* How often to we send out a broadcast saying we are alive, must be smaller than timeToExpiration
*/
protected final long sendFrequency;
/**
* Reuse the sendPacket, no need to create a new one everytime
*/
protected DatagramPacket sendPacket;
/**
* Reuse the receivePacket, no need to create a new one everytime
*/
protected DatagramPacket receivePacket;
/**
* The membership, used so that we calculate memberships when they arrive or don't arrive
*/
protected Membership membership;
/**
* The actual listener, for callback when stuff goes down
*/
protected final MembershipListener service;
/**
* The actual listener for broadcast callbacks
*/
protected final MessageListener msgservice;
/**
* Thread to listen for pings
*/
protected ReceiverThread receiver;
/**
* Thread to send pings
*/
protected SenderThread sender;
/**
* Time to live for the multicast packets that are being sent out
*/
protected final int mcastTTL;
/**
* Read timeout on the mcast socket
*/
protected int mcastSoTimeout = -1;
/**
* bind address
*/
protected final InetAddress mcastBindAddress;
/**
* nr of times the system has to fail before a recovery is initiated
*/
protected int recoveryCounter = 10;
/**
* The time the recovery thread sleeps between recovery attempts
*/
protected long recoverySleepTime = 5000;
/**
* Add the ability to turn on/off recovery
*/
protected boolean recoveryEnabled = true;
/**
* Dont interrupt the sender/receiver thread, but pass off to an executor
*/
protected final ExecutorService executor =
ExecutorFactory.newThreadPool(0, 2, 2, TimeUnit.SECONDS);
/**
* disable/enable local loopback message
*/
protected final boolean localLoopbackDisabled;
private Channel channel;
/**
* Create a new mcast service instance.
* @param member - the local member
* @param sendFrequency - the time (ms) in between pings sent out
* @param expireTime - the time (ms) for a member to expire
* @param port - the mcast port
* @param bind - the bind address (not sure this is used yet)
* @param mcastAddress - the mcast address
* @param ttl multicast ttl that will be set on the socket
* @param soTimeout Socket timeout
* @param service - the callback service
* @param msgservice Message listener
* @param localLoopbackDisabled - disable loopbackMode
* @throws IOException Init error
*/
public McastServiceImpl(
MemberImpl member,
long sendFrequency,
long expireTime,
int port,
InetAddress bind,
InetAddress mcastAddress,
int ttl,
int soTimeout,
MembershipListener service,
MessageListener msgservice,
boolean localLoopbackDisabled)
throws IOException {
this.member = member;
this.address = mcastAddress;
this.port = port;
this.mcastSoTimeout = soTimeout;
this.mcastTTL = ttl;
this.mcastBindAddress = bind;
this.timeToExpiration = expireTime;
this.service = service;
this.msgservice = msgservice;
this.sendFrequency = sendFrequency;
this.localLoopbackDisabled = localLoopbackDisabled;
init();
}
public void init() throws IOException {
setupSocket();
sendPacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
sendPacket.setAddress(address);
sendPacket.setPort(port);
receivePacket = new DatagramPacket(new byte[MAX_PACKET_SIZE],MAX_PACKET_SIZE);
receivePacket.setAddress(address);
receivePacket.setPort(port);
member.setCommand(new byte[0]);
if ( membership == null ) membership = new Membership(member);
}
protected void setupSocket() throws IOException {
if (mcastBindAddress != null) {
try {
log.info(sm.getString("mcastServiceImpl.bind", address, Integer.toString(port)));
socket = new MulticastSocket(new InetSocketAddress(address,port));
} catch (BindException e) {
/*
* On some platforms (e.g. Linux) it is not possible to bind
* to the multicast address. In this case only bind to the
* port.
*/
log.info(sm.getString("mcastServiceImpl.bind.failed"));
socket = new MulticastSocket(port);
}
} else {
socket = new MulticastSocket(port);
}
socket.setLoopbackMode(localLoopbackDisabled); //hint if we want disable loop back(local machine) messages
if (mcastBindAddress != null) {
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.setInterface", mcastBindAddress));
socket.setInterface(mcastBindAddress);
} //end if
//force a so timeout so that we don't block forever
if (mcastSoTimeout <= 0) mcastSoTimeout = (int)sendFrequency;
if (log.isInfoEnabled()) {
log.info(sm.getString("mcastServiceImpl.setSoTimeout",
Integer.toString(mcastSoTimeout)));
}
socket.setSoTimeout(mcastSoTimeout);
if ( mcastTTL >= 0 ) {
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.setTTL", Integer.toString(mcastTTL)));
socket.setTimeToLive(mcastTTL);
}
}
/**
* Start the service
* @param level 1 starts the receiver, level 2 starts the sender
* @throws IOException if the service fails to start
* @throws IllegalStateException if the service is already started
*/
public synchronized void start(int level) throws IOException {
boolean valid = false;
if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {
if ( receiver != null ) throw new IllegalStateException(sm.getString("mcastServiceImpl.receive.running"));
try {
if ( sender == null ) socket.joinGroup(address);
}catch (IOException iox) {
log.error(sm.getString("mcastServiceImpl.unable.join"));
throw iox;
}
doRunReceiver = true;
receiver = new ReceiverThread();
receiver.setDaemon(true);
receiver.start();
valid = true;
}
if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {
if ( sender != null ) throw new IllegalStateException(sm.getString("mcastServiceImpl.send.running"));
if ( receiver == null ) socket.joinGroup(address);
//make sure at least one packet gets out there
send(false);
doRunSender = true;
sender = new SenderThread(sendFrequency);
sender.setDaemon(true);
sender.start();
//we have started the receiver, but not yet waited for membership to establish
valid = true;
}
if (!valid) {
throw new IllegalArgumentException(sm.getString("mcastServiceImpl.invalid.startLevel"));
}
//pause, once or twice
waitForMembers(level);
startLevel = (startLevel | level);
}
private void waitForMembers(int level) {
long memberwait = sendFrequency*2;
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.waitForMembers.start",
Long.toString(memberwait), Integer.toString(level)));
try {Thread.sleep(memberwait);}catch (InterruptedException ignore){}
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.waitForMembers.done", Integer.toString(level)));
}
/**
* Stops the service.
* @param level Stop status
* @return <code>true</code> if the stop is complete
* @throws IOException if the service fails to disconnect from the sockets
*/
public synchronized boolean stop(int level) throws IOException {
boolean valid = false;
if ( (level & Channel.MBR_RX_SEQ)==Channel.MBR_RX_SEQ ) {
valid = true;
doRunReceiver = false;
if ( receiver !=null ) receiver.interrupt();
receiver = null;
}
if ( (level & Channel.MBR_TX_SEQ)==Channel.MBR_TX_SEQ ) {
valid = true;
doRunSender = false;
if ( sender != null )sender.interrupt();
sender = null;
}
if (!valid) {
throw new IllegalArgumentException(sm.getString("mcastServiceImpl.invalid.stopLevel"));
}
startLevel = (startLevel & (~level));
//we're shutting down, send a shutdown message and close the socket
if ( startLevel == 0 ) {
//send a stop message
member.setCommand(Member.SHUTDOWN_PAYLOAD);
send(false);
//leave mcast group
try {socket.leaveGroup(address);}catch ( Exception ignore){}
try {socket.close();}catch ( Exception ignore){}
member.setServiceStartTime(-1);
}
return (startLevel == 0);
}
/**
* Receive a datagram packet, locking wait
* @throws IOException Received failed
*/
public void receive() throws IOException {
boolean checkexpired = true;
try {
socket.receive(receivePacket);
if(receivePacket.getLength() > MAX_PACKET_SIZE) {
log.error(sm.getString("mcastServiceImpl.packet.tooLong",
Integer.toString(receivePacket.getLength())));
} else {
byte[] data = new byte[receivePacket.getLength()];
System.arraycopy(receivePacket.getData(), receivePacket.getOffset(), data, 0, data.length);
if (XByteBuffer.firstIndexOf(data,0,MemberImpl.TRIBES_MBR_BEGIN)==0) {
memberDataReceived(data);
} else {
memberBroadcastsReceived(data);
}
}
} catch (SocketTimeoutException x ) {
//do nothing, this is normal, we don't want to block forever
//since the receive thread is the same thread
//that does membership expiration
}
if (checkexpired) checkExpired();
}
private void memberDataReceived(byte[] data) {
final Member m = MemberImpl.getMember(data);
if (log.isTraceEnabled()) log.trace("Mcast receive ping from member " + m);
Runnable t = null;
if (Arrays.equals(m.getCommand(), Member.SHUTDOWN_PAYLOAD)) {
if (log.isDebugEnabled()) log.debug("Member has shutdown:" + m);
membership.removeMember(m);
t = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
try {
Thread.currentThread().setName("Membership-MemberDisappeared.");
service.memberDisappeared(m);
}finally {
Thread.currentThread().setName(name);
}
}
};
} else if (membership.memberAlive(m)) {
if (log.isDebugEnabled()) log.debug("Mcast add member " + m);
t = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
try {
Thread.currentThread().setName("Membership-MemberAdded.");
service.memberAdded(m);
}finally {
Thread.currentThread().setName(name);
}
}
};
} //end if
if ( t != null ) {
executor.execute(t);
}
}
private void memberBroadcastsReceived(final byte[] b) {
if (log.isTraceEnabled()) log.trace("Mcast received broadcasts.");
XByteBuffer buffer = new XByteBuffer(b,true);
if (buffer.countPackages(true)>0) {
int count = buffer.countPackages();
final ChannelData[] data = new ChannelData[count];
for (int i=0; i<count; i++) {
try {
data[i] = buffer.extractPackage(true);
}catch (IllegalStateException ise) {
log.debug("Unable to decode message.",ise);
}
}
Runnable t = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
try {
Thread.currentThread().setName("Membership-MemberAdded.");
for (int i=0; i<data.length; i++ ) {
try {
if (data[i]!=null && !member.equals(data[i].getAddress())) {
msgservice.messageReceived(data[i]);
}
} catch (Throwable t) {
if (t instanceof ThreadDeath) {
throw (ThreadDeath) t;
}
if (t instanceof VirtualMachineError) {
throw (VirtualMachineError) t;
}
log.error(sm.getString("mcastServiceImpl.unableReceive.broadcastMessage"),t);
}
}
}finally {
Thread.currentThread().setName(name);
}
}
};
executor.execute(t);
}
}
protected final Object expiredMutex = new Object();
protected void checkExpired() {
synchronized (expiredMutex) {
Member[] expired = membership.expire(timeToExpiration);
for (int i = 0; i < expired.length; i++) {
final Member member = expired[i];
if (log.isDebugEnabled())
log.debug("Mcast expire member " + expired[i]);
try {
Runnable t = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
try {
Thread.currentThread().setName("Membership-MemberExpired.");
service.memberDisappeared(member);
}finally {
Thread.currentThread().setName(name);
}
}
};
executor.execute(t);
} catch (Exception x) {
log.error(sm.getString("mcastServiceImpl.memberDisappeared.failed"), x);
}
}
}
}
/**
* Send a ping.
* @param checkexpired <code>true</code> to check for expiration
* @throws IOException Send error
*/
public void send(boolean checkexpired) throws IOException {
send(checkexpired,null);
}
private final Object sendLock = new Object();
public void send(boolean checkexpired, DatagramPacket packet) throws IOException {
checkexpired = (checkexpired && (packet==null));
//ignore if we haven't started the sender
//if ( (startLevel&Channel.MBR_TX_SEQ) != Channel.MBR_TX_SEQ ) return;
if (packet==null) {
member.inc();
if(log.isTraceEnabled()) {
log.trace("Mcast send ping from member " + member);
}
byte[] data = member.getData();
packet = new DatagramPacket(data,data.length);
} else if (log.isTraceEnabled()) {
log.trace("Sending message broadcast "+packet.getLength()+ " bytes from "+ member);
}
packet.setAddress(address);
packet.setPort(port);
//TODO this operation is not thread safe
synchronized (sendLock) {
socket.send(packet);
}
if ( checkexpired ) checkExpired();
}
public long getServiceStartTime() {
return (member!=null) ? member.getServiceStartTime() : -1l;
}
public int getRecoveryCounter() {
return recoveryCounter;
}
public boolean isRecoveryEnabled() {
return recoveryEnabled;
}
public long getRecoverySleepTime() {
return recoverySleepTime;
}
public Channel getChannel() {
return channel;
}
public void setChannel(Channel channel) {
this.channel = channel;
}
public class ReceiverThread extends Thread {
int errorCounter = 0;
public ReceiverThread() {
super();
String channelName = "";
if (channel.getName() != null) channelName = "[" + channel.getName() + "]";
setName("Tribes-MembershipReceiver" + channelName);
}
@Override
public void run() {
while ( doRunReceiver ) {
try {
receive();
errorCounter=0;
} catch ( ArrayIndexOutOfBoundsException ax ) {
//we can ignore this, as it means we have an invalid package
//but we will log it to debug
if ( log.isDebugEnabled() )
log.debug("Invalid member mcast package.",ax);
} catch ( Exception x ) {
if (errorCounter==0 && doRunReceiver) log.warn(sm.getString("mcastServiceImpl.error.receiving"),x);
else if (log.isDebugEnabled()) log.debug("Error receiving mcast package"+(doRunReceiver?". Sleeping 500ms":"."),x);
if (doRunReceiver) {
try { Thread.sleep(500); } catch ( Exception ignore ){}
if ( (++errorCounter)>=recoveryCounter ) {
errorCounter=0;
RecoveryThread.recover(McastServiceImpl.this);
}
}
}
}
}
}//class ReceiverThread
public class SenderThread extends Thread {
final long time;
int errorCounter=0;
public SenderThread(long time) {
this.time = time;
String channelName = "";
if (channel.getName() != null) channelName = "[" + channel.getName() + "]";
setName("Tribes-MembershipSender" + channelName);
}
@Override
public void run() {
while ( doRunSender ) {
try {
send(true);
errorCounter = 0;
} catch ( Exception x ) {
if (errorCounter==0) log.warn(sm.getString("mcastServiceImpl.send.failed"),x);
else log.debug("Unable to send mcast message.",x);
if ( (++errorCounter)>=recoveryCounter ) {
errorCounter=0;
RecoveryThread.recover(McastServiceImpl.this);
}
}
try { Thread.sleep(time); } catch ( Exception ignore ) {}
}
}
}//class SenderThread
protected static class RecoveryThread extends Thread {
private static final AtomicBoolean running = new AtomicBoolean(false);
public static synchronized void recover(McastServiceImpl parent) {
if (!parent.isRecoveryEnabled()) {
return;
}
if (!running.compareAndSet(false, true)) {
return;
}
Thread t = new RecoveryThread(parent);
String channelName = "";
if (parent.channel.getName() != null) channelName = "[" + parent.channel.getName() + "]";
t.setName("Tribes-MembershipRecovery" + channelName);
t.setDaemon(true);
t.start();
}
final McastServiceImpl parent;
public RecoveryThread(McastServiceImpl parent) {
this.parent = parent;
}
public boolean stopService() {
try {
parent.stop(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ);
return true;
} catch (Exception x) {
log.warn(sm.getString("mcastServiceImpl.recovery.stopFailed"), x);
return false;
}
}
public boolean startService() {
try {
parent.init();
parent.start(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ);
return true;
} catch (Exception x) {
log.warn(sm.getString("mcastServiceImpl.recovery.startFailed"), x);
return false;
}
}
@Override
public void run() {
boolean success = false;
int attempt = 0;
try {
while (!success) {
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.recovery"));
if (stopService() & startService()) {
success = true;
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.recovery.successful"));
}
try {
if (!success) {
if(log.isInfoEnabled())
log.info(sm.getString("mcastServiceImpl.recovery.failed",
Integer.toString(++attempt),
Long.toString(parent.recoverySleepTime)));
Thread.sleep(parent.recoverySleepTime);
}
}catch (InterruptedException ignore) {
}
}
}finally {
running.set(false);
}
}
}
public void setRecoveryCounter(int recoveryCounter) {
this.recoveryCounter = recoveryCounter;
}
public void setRecoveryEnabled(boolean recoveryEnabled) {
this.recoveryEnabled = recoveryEnabled;
}
public void setRecoverySleepTime(long recoverySleepTime) {
this.recoverySleepTime = recoverySleepTime;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import java.util.Properties;
import org.apache.catalina.tribes.Member;
public interface McastServiceMBean {
// Attributes
public String getAddress();
public int getPort();
public long getFrequency();
public long getDropTime();
public String getBind();
public int getTtl();
public byte[] getDomain();
public int getSoTimeout();
public boolean getRecoveryEnabled();
public int getRecoveryCounter();
public long getRecoverySleepTime();
public boolean getLocalLoopbackDisabled();
public String getLocalMemberName();
// Operation
public Properties getProperties();
public boolean hasMembers();
public String[] getMembersByName();
public Member findMemberByName(String name);
}

View File

@@ -0,0 +1,687 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.catalina.tribes.Member;
import org.apache.catalina.tribes.io.XByteBuffer;
import org.apache.catalina.tribes.transport.SenderState;
import org.apache.catalina.tribes.util.StringManager;
/**
* A <b>membership</b> implementation using simple multicast.
* This is the representation of a multicast member.
* Carries the host, and port of the this or other cluster nodes.
*/
public class MemberImpl implements Member, java.io.Externalizable {
/**
* Should a call to getName or getHostName try to do a DNS lookup?
* default is false
*/
public static final boolean DO_DNS_LOOKUPS = Boolean.parseBoolean(System.getProperty("org.apache.catalina.tribes.dns_lookups","false"));
public static final transient byte[] TRIBES_MBR_BEGIN = new byte[] {84, 82, 73, 66, 69, 83, 45, 66, 1, 0};
public static final transient byte[] TRIBES_MBR_END = new byte[] {84, 82, 73, 66, 69, 83, 45, 69, 1, 0};
protected static final StringManager sm = StringManager.getManager(Constants.Package);
/**
* The listen host for this member
*/
protected volatile byte[] host = new byte[0];
protected transient volatile String hostname;
/**
* The tcp listen port for this member
*/
protected volatile int port;
/**
* The udp listen port for this member
*/
protected volatile int udpPort = -1;
/**
* The tcp/SSL listen port for this member
*/
protected volatile int securePort = -1;
/**
* Counter for how many broadcast messages have been sent from this member
*/
protected AtomicInteger msgCount = new AtomicInteger(0);
/**
* The number of milliseconds since this member was
* created, is kept track of using the start time
*/
protected volatile long memberAliveTime = 0;
/**
* For the local member only
*/
protected transient long serviceStartTime;
/**
* To avoid serialization over and over again, once the local dataPkg
* has been set, we use that to transmit data
*/
protected transient byte[] dataPkg = null;
/**
* Unique session Id for this member
*/
protected volatile byte[] uniqueId = new byte[16];
/**
* Custom payload that an app framework can broadcast
* Also used to transport stop command.
*/
protected volatile byte[] payload = new byte[0];
/**
* Command, so that the custom payload doesn't have to be used
* This is for internal tribes use, such as SHUTDOWN_COMMAND
*/
protected volatile byte[] command = new byte[0];
/**
* Domain if we want to filter based on domain.
*/
protected volatile byte[] domain = new byte[0];
/**
* The flag indicating that this member is a local member.
*/
protected volatile boolean local = false;
/**
* Empty constructor for serialization
*/
public MemberImpl() {
}
/**
* Construct a new member object.
*
* @param host - the tcp listen host
* @param port - the tcp listen port
* @param aliveTime - the number of milliseconds since this member was created
*
* @throws IOException If there is an error converting the host name to an
* IP address
*/
public MemberImpl(String host,
int port,
long aliveTime) throws IOException {
setHostname(host);
this.port = port;
this.memberAliveTime=aliveTime;
}
public MemberImpl(String host,
int port,
long aliveTime,
byte[] payload) throws IOException {
this(host,port,aliveTime);
setPayload(payload);
}
@Override
public boolean isReady() {
return SenderState.getSenderState(this).isReady();
}
@Override
public boolean isSuspect() {
return SenderState.getSenderState(this).isSuspect();
}
@Override
public boolean isFailing() {
return SenderState.getSenderState(this).isFailing();
}
/**
* Increment the message count.
*/
protected void inc() {
msgCount.incrementAndGet();
}
/**
* Create a data package to send over the wire representing this member.
* This is faster than serialization.
* @return - the bytes for this member deserialized
*/
public byte[] getData() {
return getData(true);
}
@Override
public byte[] getData(boolean getalive) {
return getData(getalive,false);
}
@Override
public synchronized int getDataLength() {
return TRIBES_MBR_BEGIN.length+ //start pkg
4+ //data length
8+ //alive time
4+ //port
4+ //secure port
4+ //udp port
1+ //host length
host.length+ //host
4+ //command length
command.length+ //command
4+ //domain length
domain.length+ //domain
16+ //unique id
4+ //payload length
payload.length+ //payload
TRIBES_MBR_END.length; //end pkg
}
@Override
public synchronized byte[] getData(boolean getalive, boolean reset) {
if (reset) {
dataPkg = null;
}
// Look in cache first
if (dataPkg != null) {
if (getalive) {
// You'd be surprised, but System.currentTimeMillis
// shows up on the profiler
long alive = System.currentTimeMillis() - getServiceStartTime();
byte[] result = dataPkg.clone();
XByteBuffer.toBytes(alive, result, TRIBES_MBR_BEGIN.length + 4);
dataPkg = result;
}
return dataPkg;
}
//package looks like
//start package TRIBES_MBR_BEGIN.length
//package length - 4 bytes
//alive - 8 bytes
//port - 4 bytes
//secure port - 4 bytes
//udp port - 4 bytes
//host length - 1 byte
//host - hl bytes
//clen - 4 bytes
//command - clen bytes
//dlen - 4 bytes
//domain - dlen bytes
//uniqueId - 16 bytes
//payload length - 4 bytes
//payload plen bytes
//end package TRIBES_MBR_END.length
long alive=System.currentTimeMillis()-getServiceStartTime();
byte[] data = new byte[getDataLength()];
int bodylength = (getDataLength() - TRIBES_MBR_BEGIN.length - TRIBES_MBR_END.length - 4);
int pos = 0;
//TRIBES_MBR_BEGIN
System.arraycopy(TRIBES_MBR_BEGIN,0,data,pos,TRIBES_MBR_BEGIN.length);
pos += TRIBES_MBR_BEGIN.length;
//body length
XByteBuffer.toBytes(bodylength,data,pos);
pos += 4;
//alive data
XByteBuffer.toBytes(alive,data,pos);
pos += 8;
//port
XByteBuffer.toBytes(port,data,pos);
pos += 4;
//secure port
XByteBuffer.toBytes(securePort,data,pos);
pos += 4;
//udp port
XByteBuffer.toBytes(udpPort,data,pos);
pos += 4;
//host length
data[pos++] = (byte) host.length;
//host
System.arraycopy(host,0,data,pos,host.length);
pos+=host.length;
//clen - 4 bytes
XByteBuffer.toBytes(command.length,data,pos);
pos+=4;
//command - clen bytes
System.arraycopy(command,0,data,pos,command.length);
pos+=command.length;
//dlen - 4 bytes
XByteBuffer.toBytes(domain.length,data,pos);
pos+=4;
//domain - dlen bytes
System.arraycopy(domain,0,data,pos,domain.length);
pos+=domain.length;
//unique Id
System.arraycopy(uniqueId,0,data,pos,uniqueId.length);
pos+=uniqueId.length;
//payload
XByteBuffer.toBytes(payload.length,data,pos);
pos+=4;
System.arraycopy(payload,0,data,pos,payload.length);
pos+=payload.length;
//TRIBES_MBR_END
System.arraycopy(TRIBES_MBR_END,0,data,pos,TRIBES_MBR_END.length);
pos += TRIBES_MBR_END.length;
//create local data
dataPkg = data;
return data;
}
/**
* Deserializes a member from data sent over the wire.
*
* @param data The bytes received
* @param member The member object to populate
*
* @return The populated member object.
*/
public static Member getMember(byte[] data, MemberImpl member) {
return getMember(data,0,data.length,member);
}
public static Member getMember(byte[] data, int offset, int length, MemberImpl member) {
//package looks like
//start package TRIBES_MBR_BEGIN.length
//package length - 4 bytes
//alive - 8 bytes
//port - 4 bytes
//secure port - 4 bytes
//udp port - 4 bytes
//host length - 1 byte
//host - hl bytes
//clen - 4 bytes
//command - clen bytes
//dlen - 4 bytes
//domain - dlen bytes
//uniqueId - 16 bytes
//payload length - 4 bytes
//payload plen bytes
//end package TRIBES_MBR_END.length
int pos = offset;
if (XByteBuffer.firstIndexOf(data,offset,TRIBES_MBR_BEGIN)!=pos) {
throw new IllegalArgumentException(sm.getString("memberImpl.invalid.package.begin", org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_BEGIN)));
}
if ( length < (TRIBES_MBR_BEGIN.length+4) ) {
throw new ArrayIndexOutOfBoundsException(sm.getString("memberImpl.package.small"));
}
pos += TRIBES_MBR_BEGIN.length;
int bodylength = XByteBuffer.toInt(data,pos);
pos += 4;
if ( length < (bodylength+4+TRIBES_MBR_BEGIN.length+TRIBES_MBR_END.length) ) {
throw new ArrayIndexOutOfBoundsException(sm.getString("memberImpl.notEnough.bytes"));
}
int endpos = pos+bodylength;
if (XByteBuffer.firstIndexOf(data,endpos,TRIBES_MBR_END)!=endpos) {
throw new IllegalArgumentException(sm.getString("memberImpl.invalid.package.end", org.apache.catalina.tribes.util.Arrays.toString(TRIBES_MBR_END)));
}
byte[] alived = new byte[8];
System.arraycopy(data, pos, alived, 0, 8);
pos += 8;
byte[] portd = new byte[4];
System.arraycopy(data, pos, portd, 0, 4);
pos += 4;
byte[] sportd = new byte[4];
System.arraycopy(data, pos, sportd, 0, 4);
pos += 4;
byte[] uportd = new byte[4];
System.arraycopy(data, pos, uportd, 0, 4);
pos += 4;
byte hl = data[pos++];
byte[] addr = new byte[hl];
System.arraycopy(data, pos, addr, 0, hl);
pos += hl;
int cl = XByteBuffer.toInt(data, pos);
pos += 4;
byte[] command = new byte[cl];
System.arraycopy(data, pos, command, 0, command.length);
pos += command.length;
int dl = XByteBuffer.toInt(data, pos);
pos += 4;
byte[] domain = new byte[dl];
System.arraycopy(data, pos, domain, 0, domain.length);
pos += domain.length;
byte[] uniqueId = new byte[16];
System.arraycopy(data, pos, uniqueId, 0, 16);
pos += 16;
int pl = XByteBuffer.toInt(data, pos);
pos += 4;
byte[] payload = new byte[pl];
System.arraycopy(data, pos, payload, 0, payload.length);
pos += payload.length;
member.setHost(addr);
member.setPort(XByteBuffer.toInt(portd, 0));
member.setSecurePort(XByteBuffer.toInt(sportd, 0));
member.setUdpPort(XByteBuffer.toInt(uportd, 0));
member.setMemberAliveTime(XByteBuffer.toLong(alived, 0));
member.setUniqueId(uniqueId);
member.payload = payload;
member.domain = domain;
member.command = command;
member.dataPkg = new byte[length];
System.arraycopy(data, offset, member.dataPkg, 0, length);
return member;
}
public static Member getMember(byte[] data) {
return getMember(data,new MemberImpl());
}
public static Member getMember(byte[] data, int offset, int length) {
return getMember(data,offset,length,new MemberImpl());
}
/**
* Return the name of this object
* @return a unique name to the cluster
*/
@Override
public String getName() {
return "tcp://"+getHostname()+":"+getPort();
}
/**
* Return the listen port of this member
* @return - tcp listen port
*/
@Override
public int getPort() {
return this.port;
}
/**
* Return the TCP listen host for this member
* @return IP address or host name
*/
@Override
public byte[] getHost() {
return host;
}
public String getHostname() {
if ( this.hostname != null ) return hostname;
else {
try {
byte[] host = this.host;
if (DO_DNS_LOOKUPS)
this.hostname = java.net.InetAddress.getByAddress(host).getHostName();
else
this.hostname = org.apache.catalina.tribes.util.Arrays.toString(host,0,host.length,true);
return this.hostname;
}catch ( IOException x ) {
throw new RuntimeException(sm.getString("memberImpl.unableParse.hostname"),x);
}
}
}
public int getMsgCount() {
return msgCount.get();
}
/**
* Contains information on how long this member has been online.
* The result is the number of milli seconds this member has been
* broadcasting its membership to the cluster.
* @return nr of milliseconds since this member started.
*/
@Override
public long getMemberAliveTime() {
return memberAliveTime;
}
public long getServiceStartTime() {
return serviceStartTime;
}
@Override
public byte[] getUniqueId() {
return uniqueId;
}
@Override
public byte[] getPayload() {
return payload;
}
@Override
public byte[] getCommand() {
return command;
}
@Override
public byte[] getDomain() {
return domain;
}
@Override
public int getSecurePort() {
return securePort;
}
@Override
public int getUdpPort() {
return udpPort;
}
@Override
public void setMemberAliveTime(long time) {
memberAliveTime=time;
}
/**
* String representation of this object
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder(getClass().getName());
buf.append("[");
buf.append(getName()).append(",");
buf.append(getHostname()).append(",");
buf.append(port).append(", alive=");
buf.append(memberAliveTime).append(", ");
buf.append("securePort=").append(securePort).append(", ");
buf.append("UDP Port=").append(udpPort).append(", ");
buf.append("id=").append(bToS(this.uniqueId)).append(", ");
buf.append("payload=").append(bToS(this.payload,8)).append(", ");
buf.append("command=").append(bToS(this.command,8)).append(", ");
buf.append("domain=").append(bToS(this.domain,8));
buf.append("]");
return buf.toString();
}
public static String bToS(byte[] data) {
return bToS(data,data.length);
}
public static String bToS(byte[] data, int max) {
StringBuilder buf = new StringBuilder(4*16);
buf.append("{");
for (int i=0; data!=null && i<data.length; i++ ) {
buf.append(String.valueOf(data[i])).append(" ");
if ( i==max ) {
buf.append("...("+data.length+")");
break;
}
}
buf.append("}");
return buf.toString();
}
/**
* @see java.lang.Object#hashCode()
* @return The hash code
*/
@Override
public int hashCode() {
return getHost()[0]+getHost()[1]+getHost()[2]+getHost()[3];
}
/**
* Returns true if the param o is a McastMember with the same name
*
* @param o The object to test for equality
*/
@Override
public boolean equals(Object o) {
if ( o instanceof MemberImpl ) {
return Arrays.equals(this.getHost(),((MemberImpl)o).getHost()) &&
this.getPort() == ((MemberImpl)o).getPort() &&
Arrays.equals(this.getUniqueId(),((MemberImpl)o).getUniqueId());
}
else
return false;
}
public synchronized void setHost(byte[] host) {
this.host = host;
}
public void setHostname(String host) throws IOException {
hostname = host;
synchronized (this) {
this.host = java.net.InetAddress.getByName(host).getAddress();
}
}
public void setMsgCount(int msgCount) {
this.msgCount.set(msgCount);
}
public synchronized void setPort(int port) {
this.port = port;
this.dataPkg = null;
}
public void setServiceStartTime(long serviceStartTime) {
this.serviceStartTime = serviceStartTime;
}
public synchronized void setUniqueId(byte[] uniqueId) {
this.uniqueId = uniqueId!=null?uniqueId:new byte[16];
getData(true,true);
}
@Override
public synchronized void setPayload(byte[] payload) {
// longs to avoid any possibility of overflow
long oldPayloadLength = 0;
if (this.payload != null) {
oldPayloadLength = this.payload.length;
}
long newPayloadLength = 0;
if (payload != null) {
newPayloadLength = payload.length;
}
if (newPayloadLength > oldPayloadLength) {
// It is possible that the max packet size will be exceeded
if ((newPayloadLength - oldPayloadLength + getData(false, false).length) >
McastServiceImpl.MAX_PACKET_SIZE) {
throw new IllegalArgumentException(sm.getString("memberImpl.large.payload"));
}
}
this.payload = payload != null ? payload : new byte[0];
getData(true, true);
}
@Override
public synchronized void setCommand(byte[] command) {
this.command = command!=null?command:new byte[0];
getData(true,true);
}
public synchronized void setDomain(byte[] domain) {
this.domain = domain!=null?domain:new byte[0];
getData(true,true);
}
public synchronized void setSecurePort(int securePort) {
this.securePort = securePort;
this.dataPkg = null;
}
public synchronized void setUdpPort(int port) {
this.udpPort = port;
this.dataPkg = null;
}
@Override
public boolean isLocal() {
return local;
}
@Override
public void setLocal(boolean local) {
this.local = local;
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
int length = in.readInt();
byte[] message = new byte[length];
in.readFully(message);
getMember(message,this);
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
byte[] data = this.getData();
out.writeInt(data.length);
out.write(data);
}
}

View File

@@ -0,0 +1,350 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import org.apache.catalina.tribes.Member;
/**
* A <b>membership</b> implementation using simple multicast.
* This is the representation of a multicast membership.
* This class is responsible for maintaining a list of active cluster nodes in the cluster.
* If a node fails to send out a heartbeat, the node will be dismissed.
*
* @author Peter Rossbach
*/
public class Membership implements Cloneable {
protected static final Member[] EMPTY_MEMBERS = new Member[0];
// Non-final to support clone()
private Object membersLock = new Object();
/**
* The local member.
*/
protected final Member local;
/**
* A map of all the members in the cluster.
*/
protected HashMap<Member, MbrEntry> map = new HashMap<>(); // Guarded by membersLock
/**
* A list of all the members in the cluster.
*/
protected volatile Member[] members = EMPTY_MEMBERS; // Guarded by membersLock
/**
* Comparator for sorting members by alive time.
*/
protected final Comparator<Member> memberComparator;
@Override
public Membership clone() {
synchronized (membersLock) {
Membership clone;
try {
clone = (Membership) super.clone();
} catch (CloneNotSupportedException e) {
// Can't happen
throw new AssertionError();
}
// Standard clone() method will copy the map object. Replace that
// with a new map but with the same contents.
@SuppressWarnings("unchecked")
final HashMap<Member, MbrEntry> tmpclone = (HashMap<Member, MbrEntry>) map.clone();
clone.map = tmpclone;
// Standard clone() method will copy the array object. Replace that
// with a new array but with the same contents.
clone.members = members.clone();
// Standard clone() method will copy the lock object. Replace that
// with a new object.
clone.membersLock = new Object();
return clone;
}
}
/**
* Constructs a new membership
* @param local - has to be the name of the local member. Used to filter the local member from the cluster membership
* @param includeLocal - TBA
*/
public Membership(Member local, boolean includeLocal) {
this(local, new MemberComparator(), includeLocal);
}
public Membership(Member local) {
this(local, false);
}
public Membership(Member local, Comparator<Member> comp) {
this(local, comp, false);
}
public Membership(Member local, Comparator<Member> comp, boolean includeLocal) {
this.local = local;
this.memberComparator = comp;
if (includeLocal) {
addMember(local);
}
}
/**
* Reset the membership and start over fresh. i.e., delete all the members
* and wait for them to ping again and join this membership.
*/
public void reset() {
synchronized (membersLock) {
map.clear();
members = EMPTY_MEMBERS ;
}
}
/**
* Notify the membership that this member has announced itself.
*
* @param member - the member that just pinged us
* @return - true if this member is new to the cluster, false otherwise.<br>
* - false if this member is the local member or updated.
*/
public boolean memberAlive(Member member) {
// Ignore ourselves
if (member.equals(local)) {
return false;
}
boolean result = false;
synchronized (membersLock) {
MbrEntry entry = map.get(member);
if (entry == null) {
entry = addMember(member);
result = true;
} else {
// Update the member alive time
Member updateMember = entry.getMember();
if (updateMember.getMemberAliveTime() != member.getMemberAliveTime()) {
// Update fields that can change
updateMember.setMemberAliveTime(member.getMemberAliveTime());
updateMember.setPayload(member.getPayload());
updateMember.setCommand(member.getCommand());
// Re-order. Can't sort in place since a call to
// getMembers() may then receive an intermediate result.
Member[] newMembers = members.clone();
Arrays.sort(newMembers, memberComparator);
members = newMembers;
}
}
entry.accessed();
}
return result;
}
/**
* Add a member to this component and sort array with memberComparator
*
* @param member The member to add
*
* @return The member entry created for this new member.
*/
public MbrEntry addMember(Member member) {
MbrEntry entry = new MbrEntry(member);
synchronized (membersLock) {
if (!map.containsKey(member) ) {
map.put(member, entry);
Member results[] = new Member[members.length + 1];
System.arraycopy(members, 0, results, 0, members.length);
results[members.length] = member;
Arrays.sort(results, memberComparator);
members = results;
}
}
return entry;
}
/**
* Remove a member from this component.
*
* @param member The member to remove
*/
public void removeMember(Member member) {
synchronized (membersLock) {
map.remove(member);
int n = -1;
for (int i = 0; i < members.length; i++) {
if (members[i] == member || members[i].equals(member)) {
n = i;
break;
}
}
if (n < 0) return;
Member results[] = new Member[members.length - 1];
int j = 0;
for (int i = 0; i < members.length; i++) {
if (i != n) {
results[j++] = members[i];
}
}
members = results;
}
}
/**
* Runs a refresh cycle and returns a list of members that has expired.
* This also removes the members from the membership, in such a way that
* getMembers() = getMembers() - expire()
* @param maxtime - the max time a member can remain unannounced before it is considered dead.
* @return the list of expired members
*/
public Member[] expire(long maxtime) {
synchronized (membersLock) {
if (!hasMembers()) {
return EMPTY_MEMBERS;
}
ArrayList<Member> list = null;
for (MbrEntry entry : map.values()) {
if (entry.hasExpired(maxtime)) {
if (list == null) {
// Only need a list when members are expired (smaller gc)
list = new java.util.ArrayList<>();
}
list.add(entry.getMember());
}
}
if (list != null) {
Member[] result = new Member[list.size()];
list.toArray(result);
for (int j=0; j<result.length; j++) {
removeMember(result[j]);
}
return result;
} else {
return EMPTY_MEMBERS ;
}
}
}
/**
* Returning that service has members or not.
*
* @return <code>true</code> if there are one or more members, otherwise
* <code>false</code>
*/
public boolean hasMembers() {
return members.length > 0;
}
public Member getMember(Member mbr) {
Member[] members = this.members;
if (members.length > 0) {
for (int i = 0; i < members.length; i++) {
if (members[i].equals(mbr)) {
return members[i];
}
}
}
return null;
}
public boolean contains(Member mbr) {
return getMember(mbr) != null;
}
/**
* Returning a list of all the members in the membership.
* We not need a copy: add and remove generate new arrays.
*
* @return An array of the current members
*/
public Member[] getMembers() {
return members;
}
// --------------------------------------------- Inner Class
private static class MemberComparator implements Comparator<Member>, Serializable {
private static final long serialVersionUID = 1L;
@Override
public int compare(Member m1, Member m2) {
// Longer alive time, means sort first
long result = m2.getMemberAliveTime() - m1.getMemberAliveTime();
if (result < 0) {
return -1;
} else if (result == 0) {
return 0;
} else {
return 1;
}
}
}
/**
* Inner class that represents a member entry
*/
protected static class MbrEntry {
protected final Member mbr;
protected long lastHeardFrom;
public MbrEntry(Member mbr) {
this.mbr = mbr;
}
/**
* Indicate that this member has been accessed.
*/
public void accessed(){
lastHeardFrom = System.currentTimeMillis();
}
/**
* Obtain the member associated with this entry.
*
* @return The member for this entry.
*/
public Member getMember() {
return mbr;
}
/**
* Check if this member has expired.
*
* @param maxtime The time threshold
*
* @return <code>true</code> if the member has expired, otherwise
* <code>false</code>
*/
public boolean hasExpired(long maxtime) {
long delta = System.currentTimeMillis() - lastHeardFrom;
return delta > maxtime;
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.tribes.membership;
import java.io.IOException;
import org.apache.catalina.tribes.util.Arrays;
public class StaticMember extends MemberImpl {
public StaticMember() {
super();
}
public StaticMember(String host, int port, long aliveTime) throws IOException {
super(host, port, aliveTime);
}
public StaticMember(String host, int port, long aliveTime, byte[] payload) throws IOException {
super(host, port, aliveTime, payload);
}
/**
* @param host String, either in byte array string format, like {214,116,1,3}
* or as a regular hostname, 127.0.0.1 or tomcat01.mydomain.com
*/
public void setHost(String host) {
if ( host == null ) return;
if ( host.startsWith("{") ) setHost(Arrays.fromString(host));
else try { setHostname(host); }catch (IOException x) { throw new RuntimeException(x);}
}
/**
* @param domain String, either in byte array string format, like {214,116,1,3}
* or as a regular string value like 'mydomain'. The latter will be converted using ISO-8859-1 encoding
*/
public void setDomain(String domain) {
if ( domain == null ) return;
if ( domain.startsWith("{") ) setDomain(Arrays.fromString(domain));
else setDomain(Arrays.convert(domain));
}
/**
* @param id String, must be in byte array string format, like {214,116,1,3} and exactly 16 bytes long
*/
public void setUniqueId(String id) {
byte[] uuid = Arrays.fromString(id);
if ( uuid==null || uuid.length != 16 ) throw new RuntimeException(sm.getString("staticMember.invalid.uuidLength", id));
setUniqueId(uuid);
}
}

View File

@@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE mbeans-descriptors PUBLIC
"-//Apache Software Foundation//DTD Model MBeans Configuration File"
"http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">
<mbeans-descriptors>
<mbean
name="McastService"
description="Cluster Membership service implementation"
domain="Catalina"
group="Cluster"
type="org.apache.catalina.tribes.membership.McastService">
<attribute
name="address"
description="Multicast IP Address"
type="java.lang.String"/>
<attribute
name="bind"
description="Multicast IP Interface address (default auto)"
type="java.lang.String"/>
<attribute
name="dropTime"
description="Timeout from frequency ping after member disappear notify"
type="long"/>
<attribute
name="frequency"
description="Ping Frequency at msec"
type="long"/>
<attribute
name="localLoopbackDisabled"
description="Is local loopback disabled?"
is="true"
type="boolean"/>
<attribute
name="localMemberName"
description="Complete local receiver information"
type="java.lang.String"
writeable="false"/>
<attribute
name="membersByName"
description="Complete remote sender information"
type="[Ljava.lang.String;"
writeable="false"/>
<attribute
name="port"
description="Multicast UDP Port"
type="int"/>
<attribute
name="recoveryCounter"
description="Counter after membership failure socket restarted"
type="int"/>
<attribute
name="recoveryEnabled"
description="Membership recovery enabled"
is="true"
type="boolean"/>
<attribute
name="recoverySleepTime"
description="Sleep time between next socket recovery (5000 msec)"
type="long"/>
<attribute
name="soTimeout"
description="Multicast Socket Timeout"
type="int"/>
<attribute
name="Ttl"
description=""
type="int"/>
<operation
name="start"
description="Start the cluster membership"
impact="ACTION"
returnType="void"/>
<operation
name="stop"
description="Stop the cluster membership"
impact="ACTION"
returnType="void"/>
</mbean>
<mbean
name="MemberImpl"
description="Cluster member"
domain="Catalina"
group="Cluster"
type="org.apache.catalina.tribes.membership.MemberImpl">
<attribute
name="failing"
description="Has a problem been observed with this member (failing is worse than suspect)"
type="boolean"
is="true"
writeable="false"/>
<attribute
name="hostname"
description="The name of the host where this member is located"
type="String"
writeable="false"/>
<attribute
name="memberAliveTime"
description="The number of milliseconds since this member was created"
type="long"
writeable="false"/>
<attribute
name="msgCount"
description="The number of messages sent by this member"
type="int"
writeable="false"/>
<attribute
name="name"
description="The unique name of this member within the cluster"
type="String"
writeable="false"/>
<attribute
name="port"
description="The tcp port the member is listening on"
type="int"
writeable="false"/>
<attribute
name="ready"
description="Is this member ready to send messages"
type="boolean"
is="true"
writeable="false"/>
<attribute
name="securePort"
description="The tcp(SSL) port the member is listening on"
type="int"
writeable="false"/>
<attribute
name="serviceStartTime"
description="The time the member was started"
type="long"
writeable="false"/>
<attribute
name="suspect"
description="Has a potential problem been observed with this member (failing is worse than suspect)"
type="boolean"
is="true"
writeable="false"/>
<attribute
name="udpPort"
description="The upd port the member is listening on"
type="int"
writeable="false"/>
<attribute
name="local"
description="The flag indicating that this member is a local member"
type="boolean"
is="true"
writeable="false"/>
</mbean>
<mbean
name="StaticMember"
description="Cluster static member"
domain="Catalina"
group="Cluster"
type="org.apache.catalina.tribes.membership.StaticMember">
<attribute
name="failing"
description="Has a problem been observed with this member (failing is worse than suspect)"
type="boolean"
is="true"
writeable="false"/>
<attribute
name="hostname"
description="The name of the host where this member is located"
type="String"
writeable="false"/>
<attribute
name="memberAliveTime"
description="The number of milliseconds since this member was created"
type="long"
writeable="false"/>
<attribute
name="msgCount"
description="The number of messages sent by this member"
type="int"
writeable="false"/>
<attribute
name="name"
description="The unique name of this member within the cluster"
type="String"
writeable="false"/>
<attribute
name="port"
description="The tcp port the member is listening on"
type="int"
writeable="false"/>
<attribute
name="ready"
description="Is this member ready to send messages"
type="boolean"
is="true"
writeable="false"/>
<attribute
name="securePort"
description="The tcp(SSL) port the member is listening on"
type="int"
writeable="false"/>
<attribute
name="serviceStartTime"
description="The time the member was started"
type="long"
writeable="false"/>
<attribute
name="suspect"
description="Has a potential problem been observed with this member (failing is worse than suspect)"
type="boolean"
is="true"
writeable="false"/>
<attribute
name="udpPort"
description="The upd port the member is listening on"
type="int"
writeable="false"/>
<attribute
name="local"
description="The flag indicating that this member is a local member"
type="boolean"
is="true"
writeable="false"/>
</mbean>
</mbeans-descriptors>