libjgroups2.6-java-2.6.15.GA.orig/0000755000175000017500000000000011621261110016304 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/JGroups-2.6-branch.iml0000644000175000017500000000752411366547366022200 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/build.sh0000644000175000017500000000230711366547366017773 0ustar twernertwerner#!/bin/sh # Ant build script for the JGroups project # The following variables have to be set in the following way # PATH should include $JAVA_HOME/bin JG_HOME=. case "`uname`" in CYGWIN*) cygwin=true ;; Darwin*) darwin=true ;; esac LIB=$PWD/lib if [ "$cygwin" = "true" ]; then for i in ${LIB}/*.jar do CP=${CP}${i}\; done else for i in ${LIB}/*.jar do CP=${CP}${i}: done fi if [ -n "$JAVA_HOME" ]; then if [ -f "$JAVA_HOME/lib/tools.jar" ]; then if [ "$cygwin" = "true" ]; then CP=${CP}\;${JAVA_HOME}/lib/tools.jar else CP=${CP}:${JAVA_HOME}/lib/tools.jar fi fi else echo "WARNING: JAVA_HOME environment variable is not set." echo " If build fails because sun.* classes could not be found" echo " you will need to set the JAVA_HOME environment variable" echo " to the installation directory of java." fi if [ -n "$JAVA_HOME" ]; then ${JAVA_HOME}/bin/java -classpath "${CP}" org.apache.tools.ant.Main -buildfile ${JG_HOME}/build.xml $* else java -classpath "${CP}" org.apache.tools.ant.Main -buildfile ${JG_HOME}/build.xml $* fi libjgroups2.6-java-2.6.15.GA.orig/target/0000755000175000017500000000000011621261110017572 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/target/pom-transformed.xml0000644000175000017500000000546111366547366023471 0ustar twernertwerner 4.0.0 jgroups jgroups JGroups 2.6.15.GA http://www.jgroups.org jboss-releases-repository JBoss Releases Repository https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ jboss-public-repository-group JBoss Public Maven Repository Group https://repository.jboss.org/nexus/content/groups/public/ default true never true never src tests conf **/*.xml maven-compiler-plugin 1.5 1.5 functional/**/*.java maven-surefire-plugin true ant ant 1.6.5 true ant ant-junit 1.6.5 true junit junit 3.8.1 test libjgroups2.6-java-2.6.15.GA.orig/INSTALL.html0000644000175000017500000003436111366547366020341 0ustar twernertwerner JGroups 2.2.X Installation

Installation Instructions for JGroups

JGroups comes in a binary and a source version: the binary version is JGroups-2.x.x.bin.zip, the source version is JGroups-2.x.x.src.zip . The binary version contains the JGroups JAR file, plus a number of JARs needed by JGroups. The source version contains all source files, plus several JAR files needed by JGroups, e.g. ANT to build JGroups from source.

Requirements


Installing the binary distribution

The binary version contains
  1. jgroups-all.jar: the JGroups library including the demos
  2. commons-logging.jar
  3. concurrent.jar
  4. log4j.jar
  5. jmxri.jar: the JMX reference implementation, this is not needed if running in JDK 5 or higher
  6. jgroups.bat/jgroups.sh: convenience script to start demo programs (set the CLASSPATH etc) - see below
  7. Some sample configuration files, udp.xml, mping.xml etc
  8. CREDITS: list of contributors
  9. INSTALL.html: this file
We no longer ship the Xerces JARs. To run JGroups you have to have an XML parser installed on your system. If you use JDK 1.4, you can use the parser that is shipped with it.


Place the JAR files somewhere in your CLASSPATH, and you're ready to start using JGroups. If you already have Xerces installed, or if you have another XML parser, the Xerces JARs can be omitted.
If you want to use the JGroups JMS protocol (org.jgroups.protocols.JMS), then you will also need to place jms.jar somewhere in your CLASSPATH.

Installing the source distribution

The source version consists of the following directories:
  1. src: the sources
  2. test: unit and stress tests
  3. conf: configuration files needed by JGroups, plus default protocol stack definitions
  4. doc: documentation
  5. lib: various JARs needed to build and run JGroups:
    1. Ant JARs: used to build JGroups. If you already have Ant installed, you won't need these files
    2. jms.jar: JMS library. Needed if you intend to run the org.jgroups.protocols.JMS protocol
    3. junit.jar: to run the JUnit test cases
    4. xalan.jar : to format the output of the JUnit tests using an XSLT converter to HTML
    5. concurrent.jar
    6. commons-logging.jar
    7. log4j.jar
    8. etc

Building JGroups  (source distribution only)

  1. Unzip the source distribution, e.g. unzip JGroups-2.x.x.src.zip. This will create the JGroups-2.x.x directory (root directory) under the current directory.
  2. cd to the root directory
  3. Modify build.properties if you want to use a Java compiler other than javac (e.g. jikes)
  4. On UNIX systems use build.sh, on Windows build.bat: $> ./build.sh compile
  5. This will compile all Java files (into the classes directory).
  6. To generate the JARs: $> ./build.sh jar
  7. This will generate the following JAR files in the dist directory:
  8. The CLASSPATH now has to be set accordingly: the following directories and/or JARs have to be included:
    1. <JGroups rootdir>/classes
    2. <JGroups rootdir>/conf
    3. All needed JAR files in <JGroups rootdir>/lib. To build from sources, the two Ant JARs are required. To run unit tests, the JUnit (and possibly Xalan) JARs are needed.
  9. To generate JavaDocs simple run $>  ./build.sh javadoc and the Javadoc documentation will be generated in the  dist/javadoc directory
  10. Note that - if you already have Ant installed on your system - you do not need to use build.sh or build.bat, simply invoke ant on the build.xml file. To be able to invoked ant from any directory below the root directory, place ANT_ARGS="-find build.xml -emacs" into the .antrc file in yourhome directory.
  11. For more details on Ant see http://jakarta.apache.org/ant/ .

Testing your Setup

To see whether your system can find the JGroups classes, execute the following command:
java org.jgroups.Version

or (from JGroups 2.2.8 on)

java -jar jgroups-all.jar


You should see the following output (more or less) if the class is found:

Version: 2.2.8 RC1
CVS: $Id: INSTALL.html,v 1.9 2006/12/27 10:17:58 belaban Exp $
History: (see doc/history.txt for details)


Running the performance tests


By default, we're running 2 senders with 10000 1K messages each, to do this, execute the following in 2 shells:

 ./jgroups.sh tests.perf.Test -config ./config.txt  -props ./udp.xml -sender

You should see output like the following in both shells:

-- results:

192.168.5.2:4301 (myself):
num_msgs_expected=20000, num_msgs_received=20000 (loss rate=0.0%), received=20MB, time=3750ms, msgs/sec=5333.33, throughput=5.33MB

192.168.5.2:4311:
num_msgs_expected=20000, num_msgs_received=20000 (loss rate=0.0%), received=20MB, time=3750ms, msgs/sec=5333.33, throughput=5.33MB

combined: 5333.33 msgs/sec averaged over all receivers (throughput=5.33MB/sec)

Running a Demo Program

To test whether JGroups works okay on your machine, run
./jgroups.sh demos.Draw
twice (use jgroups.bat on Windows). 2 whiteboard windows should appear. If you started them simultaneously, they should initially show a membership of 1 in their title bars. After some time, both windows should show 2. This means that the two instances found each other and formed a group.

When drawing in one window, the second instance should also be updated. As the default group transport uses IP multicast, make sure that - if you want to start the 2 instances in different subnets - IP multicast is enabled. If this is not the case, the 2 instances won't 'find' each other and the sample won't work.

You can change the properties of the demo to for example use a different transport if multicast doesn't work (it should always work on the same machine). For example, to use udp.xml, execute:

./jgroups.sh demos.Draw -props ./udp.xml

 

Using IP Multicasting without a network connection

Sometimes there isn't a network connection (e.g. DSL modem is down), or we want to multicast only on the local machine. For this the loopback interface (typically lo) can be configured, e.g.
route add -net 224.0.0.0 netmask 224.0.0.0 dev lo
This means that all traffic directed to the 224.0.0.0 network will be sent to the loopback interface, which means it doesn't need any network to be running. Note that the 224.0.0.0 network is a placeholder for all multicast addresses in most UNIX implementations: it will catch all multicast traffic. This is an undocumented feature of /sbin/route and may not work across all UNIX flavors. The above instructions may also work for Windows systems, but this hasn't been tested. Note that not all systems allow multicast traffic to use the loopback interface.

Typical home networks have a gateway/firewall with 2 NICs: the first (eth0) is connected to the outside world (Internet Service Provider), the second (eth1) to the internal network, with the gateway firewalling/masquerading traffic between the internal and external networks. If no route for multicast traffic is added, the default will be to use the fdefault gateway, which will typically direct the multicast traffic towards the ISP. To prevent this (e.g. ISP drops multicast traffic, or latency is too high), we recommend to add a route for multicast traffic which goes to the internal network (e.g. eth1).
 

It doesn't work !

Make sure your machine is set up correctly for IP multicast. There are 2 test programs that can be used to detect this: McastReceiverTest and McastSenderTest. Start McastReceiverTest, e.g.
./jgroups.sh tests.McastReceiverTest -mcast_addr 224.10.10.10 -port 5555
Then start McastSenderTest:
./jgroups.sh tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555
You should be able to type in the McastSenderTest window and see the output in the McastReceiverWindow. If not, try to use -ttl 32 in the sender. If this still fails, consult a system administrator to help you setup IP multicast correctly. If you are the system administrator, look for another job :-)

Other means of getting help: there is a public forum on JIRA for questions. Also consider subscribing to the javagroups-users mailing list to discuss such and other problems. 


The instances still don't find each other ! 

In this case we have to use a sledgehammer (running only under JDK 1.4. and higher): we can enable the above sender and receiver test to use all available interfaces for sending and receiving. One of them will certainly be the right one... Start the receiver as follows:

java org.jgroups.tests.McastReceiverTest1_4 -mcast_addr 228.8.8.8 -use_all_interfaces

The multicast receiver uses the 1.4 functionality to list all available network interfaces and bind to all of them (including the loopback interface). This means that whichever interface a packet comes in on, we will receive it.
Now start the sender:

./jgroups.sh tests.McastSenderTest1_4 -mcast_addr 228.8.8.8 -use_all_interfaces

The sender will also determine the available network interfaces and send each packet over all interfaces.

This test can be used to find out which network interface to bind to when previously no packets were received. E.g. when you see the following output in the receiver:

bash-2.03$ ./jgroups.sh tests.McastReceiverTest1_4 -mcast_addr 228.8.8.8 -bind_addr 192.168.168.4
Socket=0.0.0.0/0.0.0.0:5555, bind interface=/192.168.168.4
dd [sender=192.168.168.4:5555]
dd [sender=192.168.168.1:5555]
dd [sender=192.168.168.2:5555]

you know that you can bind to any of the 192.168.168.{1,2,4} interfaces to receive your multicast packets. In this case you would need to modify your protocol spec to include bind_addr=192.168.168.2 in UDP, e.g. "UDP(mcast_addr=228.8.8.8;bind_addr=192.168.168.2):..." .


Alternatively you can use McastDiscovery1_4 (runs only on JDK 1.4). Start this program simultaneously on multiple machines. Binding to all available interfaces, this program tries to discover what other members are available in a network and determines which interfaces should be used by UDP. After some time (e.g. 30 seconds), press <enter> on each program. The program will then list the interfaces which can be used to bind to. There may be one or multiple interfaces. When there are multiple interfaces listed, take the one with the highest number of responses (at the top of the list). The UDP protocol spec can then be changed to explicitly bind to that interface, e.g.

"UDP(bind_addr=<interface>;...)"


Problems with IPv6

Another source of problems might be the use of IPv6, and/or misconfiguration of /etc/hosts. If you communicate between an IPv4 and an IPv6 host, and they are not able to find each other, try the java.net.preferIP4Stack=true property, e.g.

java -Djava.net.preferIPv4Stack=true org.jgroups.demos.Draw -props file:c:\\udp.xml

JDK 1.4.1 uses IPv6 by default, although is has a dual stack, that is, it also supports IPv4. Here's more details on the subject.


I have discovered a bug !

If you think that you discovered a bug, submit a bug report on JIRA or send email to javagroups-developers if you're unsure about it. Please include the following information:
 
 




libjgroups2.6-java-2.6.15.GA.orig/lib/0000755000175000017500000000000011621261107017060 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/.project0000644000175000017500000000057711366547366020016 0ustar twernertwerner JGroups org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature libjgroups2.6-java-2.6.15.GA.orig/bin/0000755000175000017500000000000011621261107017062 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/bin/draw.sh0000644000175000017500000000246411366547366020405 0ustar twernertwerner#!/bin/sh # # Convenience launcher for the Draw demo # SEP=":" case "`uname`" in CYGWIN*) cygwin=true SEP=";" ;; Darwin*) darwin=true ;; esac relpath=`dirname $0` while [ "$1" != "" ]; do if [ "$1" = "-debug" ]; then debug=true fi shift done CLASSPATH="$relpath/../classes$SEP$relpath/../conf$SEP$relpath/../lib/commons-logging.jar$SEP$relpath/../lib/log4j-1.2.6.jar$SEP$relpath/../lib/concurrent.jar" if [ "$debug" = "true" ]; then JAVA_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_shmem,server=y,suspend=y,address=jgc1" fi PROPS="\ TUNNEL(router_host=localhost;router_port=5556):\ TCPGOSSIP(initial_hosts=localhost[5556];gossip_refresh_rate=10000;num_initial_members=3;up_thread=true;down_thread=true):\ MERGE2(min_interval=5000;max_interval=10000):\ FD_SOCK:\ VERIFY_SUSPECT(timeout=1500):\ pbcast.NAKACK(gc_lag=50;retransmit_timeout=600,1200,2400,4800):\ UNICAST(timeout=600,1200,2400,4800):\ pbcast.STABLE(desired_avg_gossip=20000):\ FRAG(frag_size=8096;down_thread=false;up_thread=false):\ pbcast.GMS(join_timeout=5000;shun=false;print_local_addr=true)" if [ "$cygwin" = "true" ]; then CLASSPATH=`echo $CLASSPATH | sed -e 's/\;/\\\\;/g'` fi java -classpath $CLASSPATH $JAVA_OPTS org.jgroups.demos.Draw -props $PROPS libjgroups2.6-java-2.6.15.GA.orig/bin/runtest.sh0000644000175000017500000000213511366547366021147 0ustar twernertwerner#!/bin/sh # Runs a single test class from command line, circumventing ant altogether. Useful for # quick debugging. TESTCLASS=org.jgroups.tests.stack.RouterTest reldir=`dirname $0` # OS specific support (must be 'true' or 'false'). cygwin=false; case "`uname`" in CYGWIN*) cygwin=true ;; esac if [ $cygwin = true ]; then SEP=";" else SEP=":" fi while [ "$1" != "" ]; do if [ "$1" = "-debug" ]; then if [ $cygwin = false ]; then JAVA_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=12348" else JAVA_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_shmem,server=y,suspend=y,address=jgroups" fi fi shift done CLASSPATH="$reldir/../classes${SEP}\ $reldir/../conf${SEP}\ $reldir/../lib/junit.jar${SEP}\ $reldir/../lib/log4j-1.2.6.jar${SEP}\ $reldir/../lib/commons-logging.jar" #if [ $cygwin = "true" ]; then # CP=`cygpath -wp $CLASSPATH` #else # CP=$CLASSPATH #fi #echo $CLASSPATH java $JAVA_OPTS -cp $CLASSPATH junit.textui.TestRunner $TESTCLASS libjgroups2.6-java-2.6.15.GA.orig/bin/upload_javadocs.sh0000644000175000017500000000042411366547366022600 0ustar twernertwerner#!/bin/sh # # Uploads the javadoc to SourceForge # Author: Bela Ban # Version: $Id: upload_javadocs.sh,v 1.1.2.3 2008/10/22 10:15:49 belaban Exp $ scp -r ../dist/javadoc/* belaban,javagroups@web.sourceforge.net:/home/groups/j/ja/javagroups/htdocs/javagroupsnew/docs/javadoc/ libjgroups2.6-java-2.6.15.GA.orig/bin/frag_size.bat0000644000175000017500000000031311366547366021544 0ustar twernertwerner@echo off REM Determines the fragmentation size of your system set CLASSPATH=..\classes set CP=%CLASSPATH% set LOG4J=etc/log4j.xml set L4J=%LOG4J% java -cp %CP% org.jgroups.tests.DetermineFragSize libjgroups2.6-java-2.6.15.GA.orig/bin/get_interfaces.sh0000644000175000017500000000057211366547366022430 0ustar twernertwerner#!/bin/sh # Determines the network interfaces on a machine BIN=`dirname $0` CLASSPATH=$BIN/../classes # OS specific support (must be 'true' or 'false'). cygwin=false; case "`uname`" in CYGWIN*) cygwin=true ;; esac if [ $cygwin = "true" ]; then CP=`cygpath -wp $CLASSPATH` else CP=$CLASSPATH fi java -cp $CP org.jgroups.util.GetNetworkInterfaces libjgroups2.6-java-2.6.15.GA.orig/bin/jgroups.bat0000644000175000017500000000177211366547366021276 0ustar twernertwerner@echo off REM ====================== Script to start JGroups programs ============================== REM Usage: jgroups.bat demos.Draw -props c:\udp.xml REM set the value of JG to the root directory in which JGroups is located set JG=. set LIB=%JG% set CP=%JG%\classes\;%JG%\conf\;%LIB%\jgroups-all.jar\;%LIB%\commons-logging.jar\;%LIB%\concurrent.jar\;%LIB%\jmxri.jar\;%LIB%\log4j.jar\;%JG%\keystore set VMFLAGS=-Xmx500M -Xms500M -XX:NewRatio=1 -XX:+AggressiveHeap -verbose:gc -XX:+DisableExplicitGC -XX:ThreadStackSize=32 -XX:CompileThreshold=100 rem LOG="-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Djava.util.logging.config.file=c:\logging.properties" set LOG=-Dlog4j.configuration=file:c:\log4j.properties set FLAGS=-Djava.net.preferIPv4Stack=true -Djgroups.bind_addr=192.168.5.2 -Djgroups.tcpping.initial_hosts=127.0.0.1[7800] java -Ddisable_canonicalization=false -classpath %CP% %LOG% %VMFLAGS% %FLAGS% -Dcom.sun.management.jmxremote -Dresolve.dns=false org.jgroups.%* libjgroups2.6-java-2.6.15.GA.orig/bin/probe.bat0000644000175000017500000000057211366547366020711 0ustar twernertwerner@echo off REM Discovers all UDP-based members running on a certain mcast address (use -help for help) REM Probe [-help] [-addr ] [-port ] [-ttl ] [-timeout ] set CLASSPATH=..\classes set LIB=..\lib set LIBS=%LIB%\log4j-1.2.6.jar;%LIB%\commons-logging.jar;%LIB%\concurrent.jar set CP=%CLASSPATH%;%LIBS% java -cp %CP% org.jgroups.tests.Probe %* libjgroups2.6-java-2.6.15.GA.orig/bin/probe.sh0000644000175000017500000000113111366547366020545 0ustar twernertwerner#!/bin/sh # Discovers all UDP-based members running on a certain mcast address (use -help for help) # Probe [-help] [-addr ] [-port ] [-ttl ] [-timeout ] BIN=`dirname $0` LIB=$BIN/../lib LIBS=$LIB/log4j-1.2.6.jar:$LIB/commons-logging.jar:$LIB/concurrent.jar CLASSPATH=$BIN/../classes:$CLASSPATH:$LIBS # OS specific support (must be 'true' or 'false'). cygwin=false; case "`uname`" in CYGWIN*) cygwin=true ;; esac if [ $cygwin = "true" ]; then CP=`cygpath -wp $CLASSPATH` else CP=$CLASSPATH fi java -classpath $CP org.jgroups.tests.Probe $* libjgroups2.6-java-2.6.15.GA.orig/bin/gossiprouter.sh0000644000175000017500000000074111366547366022211 0ustar twernertwerner#!/bin/sh # # Convenience launcher for the GossipRouter SEP=":" case "`uname`" in CYGWIN*) cygwin=true SEP=";" ;; esac CLASSPATH="../classes$SEP../conf$SEP../lib/commons-logging.jar$SEP../lib/log4j.jar" if [ "$cygwin" = "true" ]; then CLASSPATH=`echo $CLASSPATH | sed -e 's/\;/\\\\;/g'` fi #-Djava.util.logging.config.file=./java.logging.config echo "$CLASSPATH args=$*" java -classpath $CLASSPATH $JAVA_OPTS org.jgroups.stack.GossipRouter $* libjgroups2.6-java-2.6.15.GA.orig/bin/frag_size.sh0000644000175000017500000000057211366547366021417 0ustar twernertwerner#!/bin/sh # Determines the fragmentation size of your system BIN=`dirname $0` CLASSPATH=$BIN/../classes # OS specific support (must be 'true' or 'false'). cygwin=false; case "`uname`" in CYGWIN*) cygwin=true ;; esac if [ $cygwin = "true" ]; then CP=`cygpath -wp $CLASSPATH` else CP=$CLASSPATH fi java -cp $CP org.jgroups.tests.DetermineFragSize libjgroups2.6-java-2.6.15.GA.orig/bin/upload_manual.sh0000644000175000017500000000047711366547366022273 0ustar twernertwerner#!/bin/sh # # Uploads the manual to SourceForge scp -r ../doc/manual/build/en/* belaban,javagroups@web.sourceforge.net:/home/groups/j/ja/javagroups/htdocs/javagroupsnew/docs/manual scp -r ../doc/tutorial/build/en/* belaban,javagroups@web.sourceforge.net:/home/groups/j/ja/javagroups/htdocs/javagroupsnew/docs/tutorial libjgroups2.6-java-2.6.15.GA.orig/bin/jgroups.sh0000644000175000017500000000056411366547366021140 0ustar twernertwerner#!/bin/bash JG=.. LIB=$JG/lib CP=$JG/classes:$JG/conf:$LIB/commons-logging.jar:$LIB/log4j.jar:$JG/keystore FLAGS="$FLAGS -Djava.net.preferIPv4Stack=true -Djgroups.bind_addr=192.168.1.5 -Djgroups.tcpping.initial_hosts=192.168.1.5[7800]" java -Ddisable_canonicalization=false -classpath $CP $LOG $FLAGS -Dcom.sun.management.jmxremote -Dresolve.dns=false org.jgroups.$* libjgroups2.6-java-2.6.15.GA.orig/bin/clusterperformancetest.sh0000644000175000017500000000601311366547366024245 0ustar twernertwerner#!/bin/bash #Script file used for automated performance tests. Prior to running this script #user should use org.jgroups.tests.perf.PerformanceTestGenerator to generate #performance tests input files. # #For a succesful performance test running user should ensure the following: # - can log into all machines listed in CLUSTER_NODES variable # - CLASSPATH variable points to existing lib files # - CONFIG_FILES is initialized to proper performance tests input files # - JGROUPS_CONFIG_FILE is initialized to an existing JGroups stack conf file #lists all the computer nodes used for performance tests CLUSTER_NODES=( cluster01.qa.atl.jboss.com cluster02.qa.atl.jboss.com cluster03.qa.atl.jboss.com cluster04.qa.atl.jboss.com cluster05.qa.atl.jboss.com cluster06.qa.atl.jboss.com cluster07.qa.atl.jboss.com cluster08.qa.atl.jboss.com ) USERID=bela #classpath for performance tests CLASSPATH='commons-logging.jar:log4j-1.2.6.jar:concurrent.jar:jgroups-all.jar' #finds test configuration files #note the running directory of this script and make sure that find command can #actually find configuration files CONFIG_FILES=`find . -name 'config_*.txt'` #JGroups configuration stack used in performance tests JGROUPS_CONFIG_FILE="/home/${USERID}/udp.xml" #JGROUPS_CONFIG_FILE="/home/${USERID}/tcp-nio.xml" #sleeptime between performance test rounds (should be big enough to prevent test #overlapping) SLEEP_TIME=30 LOGIN_COMMAND='ssh -i rgreathouse@jboss.com.id_dsa vblagojevic@' JVM_COMMAND='/opt/jdk1.5.0_06/bin/java -Djava.net.preferIPv4Stack=true' LOGIN_COMMAND="${USERID}@" JVM_COMMAND='java -Djava.net.preferIPv4Stack=true' JVM_PARAM="-Xmx500M -Xms500M -XX:NewRatio=1 -XX:+AggressiveHeap -XX:+DisableExplicitGC -XX:CompileThreshold=100 -Dbind.address=\${MYTESTIP_1}" #verify that we found configuration files config_file_count=${#CONFIG_FILES[*]} if [ $config_file_count -le "0" ] ; then echo Did not find performance test configuration files! exit fi echo "starting performance tests..." node_count=${#CLUSTER_NODES[@]} for file in $CONFIG_FILES; do num_senders_line=`grep num_senders $file` num_senders=${num_senders_line:12} num_members_line=`grep num_members $file` num_members=${num_members_line:12} sender_count=1 sender_or_receiver=" -sender " echo "starting performance test round for $file..." for (( i = 0 ; i < node_count ; i++ )) do node=${CLUSTER_NODES[$i]} if [ $sender_count -le $num_senders ] ; then let "sender_count++" else sender_or_receiver=" -receiver " fi let j=$i+1 if [ $j -eq $node_count ] ; then SSH_CMD="ssh" output_file="-f result-${file#.*/}" else SSH_CMD="ssh -f" output_file="" fi args="-config $file -props $JGROUPS_CONFIG_FILE $sender_or_receiver $output_file" final_command="$SSH_CMD $LOGIN_COMMAND$node $JVM_COMMAND $JVM_PARAM org.jgroups.tests.perf.Test $args > /dev/null" echo starting $final_command on $node $final_command sleep 5 done echo "Tests round is now running, waiting $SLEEP_TIME seconds for this test round to finish..." sleep $SLEEP_TIME done libjgroups2.6-java-2.6.15.GA.orig/bin/draw.bat0000644000175000017500000000212711366547366020535 0ustar twernertwerner@rem Convenience launcher for the Draw demo (contributed by Laran Evans lc278@cornell.edu) @echo off set CPATH=../classes;../conf;../lib/commons-logging.jar;../lib/log4j-1.2.6.jar;../lib/concurrent.jar set JAVA_OPTS= if -debug==%1 set JAVA_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_shmem,server=y,suspend=y,address=jgc1 set PROPS=TUNNEL(router_host=localhost;router_port=5556) set PROPS=%PROPS%:TCPGOSSIP(initial_hosts=localhost[5556];gossip_refresh_rate=10000;num_initial_members=3;up_thread=true;down_thread=true) set PROPS=%PROPS%:MERGE2(min_interval=5000;max_interval=10000) set PROPS=%PROPS%:FD_SOCK set PROPS=%PROPS%:VERIFY_SUSPECT(timeout=1500) set PROPS=%PROPS%:pbcast.NAKACK(gc_lag=50;retransmit_timeout=600,1200,2400,4800) set PROPS=%PROPS%:UNICAST(timeout=600,1200,2400,4800) set PROPS=%PROPS%:pbcast.STABLE(desired_avg_gossip=20000) set PROPS=%PROPS%:FRAG(frag_size=8096;down_thread=false;up_thread=false) set PROPS=%PROPS%:pbcast.GMS(join_timeout=5000;shun=false;print_local_addr=true) @echo on java -classpath %CPATH% %JAVA_OPTS% org.jgroups.demos.Draw -props %PROPS% libjgroups2.6-java-2.6.15.GA.orig/bin/concurrent.sh0000644000175000017500000000135711366547366021632 0ustar twernertwerner#!/bin/bash LIB=../lib LIBS=$LIB/log4j.jar:$LIB/commons-logging.jar FLAGS="$FLAGS -Djava.net.preferIPv4Stack=true -Djgroups.bind_addr=192.168.1.5 -Djgroups.tcpping.initial_hosts=192.168.1.5[7800]" CLASSPATH=../classes:../conf:$CLASSPATH:$LIBS # OS specific support (must be 'true' or 'false'). cygwin=false; case "`uname`" in CYGWIN*) cygwin=true ;; esac if [ $cygwin = "true" ]; then CP=`cygpath -wp $CLASSPATH` else CP=$CLASSPATH fi count=0 while [ $count -lt 20 ] do echo "Starting Draw instance #$count" java -Ddisable_canonicalization=false -classpath $CP $LOG $FLAGS -Dcom.sun.management.jmxremote -Dresolve.dns=false org.jgroups.demos.Draw -props $HOME/udp-2.6.xml & # sleep 1 count=$(($count+1)) donelibjgroups2.6-java-2.6.15.GA.orig/bin/jt0000644000175000017500000000041511366547366017446 0ustar twernertwerner#!/bin/bash FLAGS="-Xmx500M -Xms500M -XX:NewRatio=1 -XX:+AggressiveHeap -verbose:gc -XX:+DisableExplicitGC -XX:ThreadStackSize=32 -XX:CompileThreshold=100" LOG="-Dlog4j.configuration=file:c:\log4j.properties" java $LOG $FLAGS -Dresolve.dns=false org.jgroups.tests.$*libjgroups2.6-java-2.6.15.GA.orig/bin/release_to_nexus.sh0000644000175000017500000000155611366547366023015 0ustar twernertwerner#!/bin/bash # Uploads the artifacts in ./dist (JAR and src JAR) to the Nexus Maven repo at repository.jboss.org/nexus # The artifacts will be in the staging repo, go to repository.jboss.org/nexus and promote them to the releases repo in # the next step # Author: Bela Ban # version: $Id: release_to_nexus.sh,v 1.6.2.2 2010/04/30 12:34:42 belaban Exp $ DIST=../dist POM=../pom.xml JAR=`find $DIST -name "jgroups-*.jar" | grep -v source` SRC_JAR=`find $DIST -name "jgroups-*.jar" | grep source` REPO=https://repository.jboss.org/nexus/service/local/staging/deploy/maven2 echo "Deploying $JAR to $REPO" mvn deploy:deploy-file -Dfile=$JAR -Durl=$REPO -DpomFile=$POM -DrepositoryId=jboss-releases-repository echo "Deploying $SRC_JAR to $REPO" mvn deploy:deploy-file -Dfile=$SRC_JAR -Durl=$REPO -DpomFile=$POM -Dclassifier=sources -DrepositoryId=jboss-releases-repository libjgroups2.6-java-2.6.15.GA.orig/bin/get_interfaces.bat0000644000175000017500000000023111366547366022554 0ustar twernertwerner@echo off REM Determines the interfaces on a machine set CLASSPATH=..\classes set CP=%CLASSPATH% java -cp %CP% org.jgroups.util.GetNetworkInterfaces libjgroups2.6-java-2.6.15.GA.orig/bin/drawnio.bat0000644000175000017500000000144211366547366021242 0ustar twernertwerner@rem Convenience launcher for the Draw demo (contributed by Laran Evans lc278@cornell.edu) @echo off set CPATH=../classes;../conf;../lib/commons-logging.jar;../lib/log4j.jar;../lib/log4j-1.2.6.jar;../lib/concurrent.jar;../conf/log4j.properties set JAVA_OPTS= if -debug==%1 set JAVA_OPTS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_shmem,server=y,suspend=y,address=jgc1 if "%LOCALHOSTIP%"=="" echo Warning: You should set environment variable 'LOCALHOSTIP' to your local ip address before running this script. if "%LOCALHOSTIP%"=="" set LOCALHOSTIP=192.168.1.103 @echo on java -classpath %CPATH% %JAVA_OPTS% -Djgroups.bind_addr=%LOCALHOSTIP% -Djgroups.tcpping.initial_hosts=%LOCALHOSTIP%[7800],%LOCALHOSTIP%[7801] org.jgroups.demos.Draw -props c:\jboss\JGroups\conf\tcp-nio.xmllibjgroups2.6-java-2.6.15.GA.orig/.classpath0000644000175000017500000000212611366547366020322 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/build.bat0000644000175000017500000000154511366547366020132 0ustar twernertwerner @echo off REM This script assumes that tools.jar is already in the classpath if "%JAVA_HOME%" == "" goto noJavaHome set LIB=lib set LIBS=%LIB%\log4j-1.2.6.jar;%LIB%\commons-logging.jar;%LIB%\concurrent.jar set LIBS=%LIB%\ant.jar;%LIB%\ant-junit.jar;%LIB%\ant-launcher.jar;%LIB%\junit.jar;%LIB%\xalan.jar;%LIB%\serializer.jar; REM echo LIBS=%LIBS% set CP=%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\jre\lib\rt.jar;%CLASSPATH%;%LIBS% %JAVA_HOME%\bin\java -classpath "%CP%" org.apache.tools.ant.Main -buildfile build.xml %1 %2 %3 %4 %5 %6 %7 %8 %9 goto endOfFile :noJavaHome if "%_JAVACMD%" == "" set _JAVACMD=java echo. echo Warning: JAVA_HOME environment variable is not set. echo If build fails because sun.* classes could not be found echo you will need to set the JAVA_HOME environment variable echo to the installation directory of java. echo. :endOfFile libjgroups2.6-java-2.6.15.GA.orig/conf/0000755000175000017500000000000011621261107017237 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/conf/tcpgossip.xml0000644000175000017500000000142711366547366022024 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/smack_tunnel.xml0000644000175000017500000000077411366547366022500 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/multiplexer-xmbean.xml0000644000175000017500000001156111366547366023633 0ustar twernertwerner JGroups Multiplexer org.jgroups.jmx.JChannelFactory The default constructor JChannelFactory The domain which is used as prefix for all channels and protocols exposed via JMX Domain java.lang.String The file used for configuration. If this is not an absolute pathname, it will need to be found on the classpath MultiplexerConfig java.lang.String Whether or not to expose channels via JMX ExposeChannels boolean Whether or not to expose protocols via JMX (if true, will set ExposeChannels to true too) ExposeProtocols boolean The create() life cycle operation create The start lifecycle operation. start The stop lifecycle operation. stop The destroy() life cycle operation destroy Dumps the channels dumpChannels java.lang.String Dumps the configuration dumpConfiguration java.lang.String createMultiplexerChannel The name of the stack, as defined e.g. in stacks.xml stack_name java.lang.String The application ID, all IDs need to be unique across a Multiplexer id java.lang.String Whether this application wants to register for state transfer, getState() will only return when *all* registered listeners called it register_for_state_transfer boolean The ID of the substate to be retrieved (or null) substate_id java.lang.String org.jgroups.Channel createMultiplexerChannel The name of the stack, as defined e.g. in stacks.xml stack_name java.lang.String The application ID, all IDs need to be unique across a Multiplexer id java.lang.String org.jgroups.Channel libjgroups2.6-java-2.6.15.GA.orig/conf/encrypt.xml0000644000175000017500000000206611366547366021475 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/auth_simple.xml0000644000175000017500000000202211366547366022313 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/auth_fixedlist.xml0000644000175000017500000000251111366547366023020 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/flush-udp.xml0000644000175000017500000000426311366547366021721 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/encrypt-entire-message.xml0000644000175000017500000000223711366547366024403 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/stacks.xml0000644000175000017500000004340611366547366021304 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/tcp.xml0000644000175000017500000000514211366547366020575 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/EncryptKeyStore.xml0000644000175000017500000000074311366547366023123 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/auth_X509.xml0000644000175000017500000000254111366547366021475 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/jg-magic-map.xml0000644000175000017500000000527311366547366022245 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/bsh.xml0000644000175000017500000000177011366547366020566 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/performancetestconfig.txt0000644000175000017500000000256211366547366024420 0ustar twernertwerner# Author: Vladimir Blagojevic # Version: $Id$ #This file is used as an input for org.jgroups.tests.perf.PerformanceTestGenerator #utility. PerformanceTestGenerator, given this input file with -config flag, then #in turn generates test files that are used in conjunction with #bin/clusterperfromancetest.sh. The number of test files generated is #number_of_senders*message_sizes. Each test file represents one performance #round. In each performance round JGroups cluster is formed and tests are #executed using org.jgroups.tests.perf.Test utility. # #Configuration parameters are: # # - nodes: number of group member nodes (machines) used in the performance test # - total_data: total size of data in bytes # - number_of_sender: specifies how many nodes will be senders in each test # - message_sizes: specifies message size used in each test # - interval: log after interval messages # # #Number of machines used for performance tests. This value should match number #of computer nodes listed in CLUSTER_NODES variable of bin/clusterperformancetests.sh nodes=8 #Total combined amount of data in bytes sent by all senders in a perfromance test round total_data=200000000 #Number of senders in each performance round number_of_senders=1,4,8 #Message size in bytes for each performance round message_sizes=100,1000,10000,100000 #Log after receiving interval messages interval=100000libjgroups2.6-java-2.6.15.GA.orig/conf/gossiprouter-xmbean.xml0000644000175000017500000000664611366547366024036 0ustar twernertwerner JGroups GossipRouter org.jgroups.stack.GossipRouter The default constructor GossipRouter The local TCP port. Port int The local address the server will bind to. BindAddress java.lang.String Time (msec) until a cached gossip member entry expires. ExpiryTime long Max queue size of backlogged connections. Must be greater than zero or the default of 1000 will be used. Backlog int Time for setting SO_LINGER on connections. 0 means do not set SO_LINGER. Must be greater than or equal to zero or the default of 2000 will be used. LingerTimeout long Time for setting SO_TIMEOUT on connections. 0 means do not set SO_TIMEOUT. Must be greater than or equal to zero or the default of 3000 will be used. SocketReadTimeout long Returns true if the router is operational. Started boolean Not used create The start lifecycle operation. Brings the Router in fully functional state. start The stop lifecycle operation. Close connections and frees resources. stop Not used destroy Dumps the routing table. dumpRoutingTable java.lang.String libjgroups2.6-java-2.6.15.GA.orig/conf/tunnel.xml0000644000175000017500000000260711366547366021317 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/EncryptNoKeyStore.xml0000644000175000017500000000214511366547366023416 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/sequencer.xml0000644000175000017500000000302511366547366021777 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/log4j.properties0000644000175000017500000000237311366547366022425 0ustar twernertwerner ## Root logger log4j.rootLogger=warn,console ## ConsoleAapender log4j.appender.console=org.apache.log4j.ConsoleAppender # File appender log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=dump.log log4j.appender.file.MaxFileSize=100KB # Keep one backup file log4j.appender.file.MaxBackupIndex=1 # log everything #log4j.logger.org.jgroups=DEBUG # trace only the protocols #log4j.logger.org.jgroups.protocols=INFO log4j.logger.org.jgroups=warn #log4j.logger.org.jgroups.protocols.pbcast.FLUSH=WARN #log4j.logger.org.jgroups.protocols.pbcast.GMS=DEBUG #log4j.logger.org.jgroups.protocols.MERGE2=DEBUG #log4j.logger.org.jgroups.protocols.PING=TRACE #log4j.logger.org.jgroups.tests=INFO #log4j.additivity.org.jgroups.protocols.pbcast.STABLE=false ## Layout for the console appender log4j.appender.console.layout=org.apache.log4j.PatternLayout #log4j.appender.console.layout.ConversionPattern=%-7d{HH:mm:ss,SSS} [%p] %c: %m%n log4j.appender.console.layout.ConversionPattern=%r [%p] %c{1}: - %m%n ## Layout for the file appender log4j.appender.file.layout=org.apache.log4j.PatternLayout #log4j.appender.file.layout.ConversionPattern=%-7d{HH:mm:ss,SSS} [%p] %c: %m%n log4j.appender.file.layout.ConversionPattern=%r [%p] %c{1}: - %m%n libjgroups2.6-java-2.6.15.GA.orig/conf/bare-bones.xml0000644000175000017500000000156411366547366022030 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/gossip-service.xml0000644000175000017500000000144111366547366022747 0ustar twernertwerner ${jboss.bind.address} 12000 30000 2000 3000 1000 libjgroups2.6-java-2.6.15.GA.orig/conf/pbcast.xml0000644000175000017500000000143411366547366021263 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/smack.xml0000644000175000017500000000160511366547366021105 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/persist.properties0000644000175000017500000000212611366547366023073 0ustar twernertwerner#choice for persist variable is DB or FILE persist=DB #======================================================== #fill these properties for DB persistence to work #======================================================== # this is the string to create the table # choose one of the options #---------------------------------------------------- # hsql #jdbc.table = create table replhashmap(key varchar, keybin varbinary, valbin varbinary) # oracle jdbc.table = create table replhashmap(key varchar2(100), keybin blob, valbin blob) # Db2 and other #jdbc.table = create table replhashmap(key varchar(100), keybin blob, valbin blob) # insert other #jdbc.table = #--------------------- end of table create string # Db related properties jdbc.User = jdbc.Pass = jdbc.Conn = jdbc.Driver = #================ end of DB properties #======================================================== # File-based PersistenceManager #======================================================== # The class that implements the file-based PersistenceManager # (default is FiledPersistenceManager) # filePersistMgr =libjgroups2.6-java-2.6.15.GA.orig/conf/sfc.xml0000644000175000017500000000455511366547366020571 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/jboss-service.xml0000644000175000017500000000403611366547366022566 0ustar twernertwerner jgroups:name=DemoChannel true true true DemoChannel libjgroups2.6-java-2.6.15.GA.orig/conf/mping.xml0000644000175000017500000000220311366547366021114 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/flush-tcp-nio.xml0000644000175000017500000000613711366547366022504 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/udp.xml0000644000175000017500000000501211366547366020573 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/auth_md5.xml0000644000175000017500000000233411366547366021515 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/flush-tcp.xml0000644000175000017500000000477211366547366021724 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/config.txt0000644000175000017500000000357111366547366021277 0ustar twernertwerner# # This file contains configuration for the JGroups performance tests (org.jgroups.tests.perf package) # $Id: config.txt,v 1.12 2006/12/11 10:16:54 belaban Exp $ # # Class implementing the org.jgroups.tests.perf.Transport interface transport=org.jgroups.tests.perf.transports.JGroupsTransport #transport=org.jgroups.tests.perf.transports.JGroupsClusterTransport #transport=org.jgroups.tests.perf.transports.UdpTransport #transport=org.jgroups.tests.perf.transports.TcpTransport # Number of messages a sender multicasts num_msgs=10000 # Message size in bytes. msg_size=1000 # Expected number of group members. num_members=2 # Number of senders in the group. Min 1, max num_members. num_senders=2 # dump stats every n msgs log_interval=1000 # number of ms to wait at the receiver to simulate delay caused by processing of the message processing_delay=0 # Needs to either contain the full property string, or an URL pointing to a valid # location (needs to be changed) props=file:c:\\udp.xml # Dumps statistics about the transport dump_transport_stats=false # Register JGroups channel and protocols with MBeanServer, don't terminate at the end jmx=false ##################################################### # These properties are only used by the UDP transport ##################################################### bind_addr=localhost mcast_addr=228.1.2.3 mcast_port=7500 ############################ # only used by TCP Transport ############################ # List of hosts in the cluster. Since we don't specify ports, you cannot run multiple TcpTransports # on the same machine: each ember has to be run on a separate machine (this may be changed in a next version) cluster=127.0.0.1:7800,127.0.0.1:7801 start_port=7800 ################################################# # JNDI name of topic (only used by JMS transport) ################################################# topic=topic/testTopiclibjgroups2.6-java-2.6.15.GA.orig/conf/manifest.mf0000644000175000017500000000016711366547366021421 0ustar twernertwernerManifest-Version: 1.0 Created-By: Apache Ant 1.5Beta2 Main-Class: org.jgroups.Version Implementation-Version: 2.6.15.GAlibjgroups2.6-java-2.6.15.GA.orig/conf/tcp-nio.xml0000644000175000017500000000607111366547366021362 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/pulltheplug.xml0000644000175000017500000000271011366547366022352 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/compress.xml0000644000175000017500000000245511366547366021646 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/total.xml0000644000175000017500000000304311366547366021130 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/conf/multiplexer-service.xml0000644000175000017500000000141511366547366024016 0ustar twernertwerner jgroups.mux stacks.xml true true libjgroups2.6-java-2.6.15.GA.orig/build.properties0000644000175000017500000000110411366547366021547 0ustar twernertwerner# add your own properties in here # the network interface (NIC) which will be used by the unit tests, change this to # the one you want to use. Note that 'localhost' usually resolved to 127.0.0.1, # which may not work on Linux unless you have a multicast route set up for loopback jgroups.bind_addr=192.168.1.5 jgroups.tcpping.initial_hosts=192.168.1.5[7800] jgroups.udp.mcast_addr=232.15.15.15 jgroups.udp.mcast_port=45588 jgroups.udp.ip_ttl=5 jgroups.tunnel.router_host=192.168.1.5 jgroups.tunnel.router_port=12001 # use true for IPv6 and false for IPv4 jgroups.useIPv6=false libjgroups2.6-java-2.6.15.GA.orig/JGroups-2.6-branch.ipr0000644000175000017500000012611011366547366022202 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/CREDITS0000644000175000017500000000241211366547366017355 0ustar twernertwerner Credits ------- These are the people who contributed to JGroups (in chronological order of contribution): Bela Ban (bela@yahoo.com) - Original author. Responsible for core, pbcast protocols Gianluca Collot (gianluca@tin.it) - Extended virtual synchrony, partitions and merging John Georgiadis (i.georgiadis@doc.ic.ac.uk) - Total order protocol (TOTAL) Jim Menard (jimm@io.com) - Trace module and makefiles Filip Hanik (filip@filip.net) - ANT based build system (./build) - XML-based protocol stack configuration - Class marshalling and magic number mapping (XML) Vladimir Blagojevic (vladimir@cs.yorku.ca) - Causal order protocol (CAUSAL) - Token based total order protocol (TOTAL_TOKEN) - FLUSH - Streaming state transfer Roman Rokytskyy (roman@gate5.de) - JMS protocol - JGroups Services (org.javagroups.service): locking/leasing Ananda Bollu (akbollu@users.sf.net) - FLOW_CONTROL protocol Mandar Shinde (whizkid_bay@users.sf.net) - Persistence manager (org.javagroups.persistence) - Port to J2ME Ovidiu Feodorov (ovidiuf@users.sourceforge.net) - WAN capabilities, TUNNEL and Router Robert Schaffar-Taurok (robert@fusion.at) - Distributed lock manager Chris Lott (chrislott@spamcop.net) - Javadocs Chris Mills (chris.mills@jboss.com) - AUTHlibjgroups2.6-java-2.6.15.GA.orig/doc/0000755000175000017500000000000011621261107017057 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/RELEASE_INSTRUCTIONS0000644000175000017500000000441111366547366022132 0ustar twernertwerner// $Id: RELEASE_INSTRUCTIONS,v 1.16.2.4 2010/04/30 12:13:58 belaban Exp $ Things to do before packaging a release --------------------------------------- Pre-release: - Do a cvs update -dP and commit all pending changes - Run all unit tests (all-tests-cc target) - Run the manual tests in doc/tests/ManualTests.txt - Run the performance tests to see whether performance is about the same (see wiki http://wiki.jboss.org/wiki/Wiki.jsp?page=PerfTests) Release: - Update the ReleaseNotes-xxx.txt for the given release - Set version in: Version.java, manifest.mf, build.xml and jgroups-pom.xml - Create a CVS tag: JGroups_x_y_z - Add tag information to wiki: http://wiki.jboss.org/wiki/Wiki.jsp?page=Branches - Create a distribution: ./build.sh dist - the distribution files are dist/JGroups-x.y.z-rel.bin.zip, dist/JGroups-x.y.z-rel.src.zip - Upload distribution files to web site: - Go to http://sourceforge.net/projects/javagroups/ - Log in and pick http://sourceforge.net/project/admin/editpackages.php?group_id=6081 - Add release to JGroups (new version) - Release notes are included in dist, change log obtained from JIRA - Create PDF and HTML documentation: ./build.sh docs and upload to web site - these files update the JGroups web page (http://www.jgroups.org/javagroupsnew/docs/index.html), specifically the links to manual and tutorial documentation - see JGroups/bin/upload_manual.sh - Create javadoc: ./build.sh javadoc and upload to web site - use process similar to pdf and html (script is bin/upload_javadocs.sh) - Add new release binary jar *and* source jar to Nexus maven repository (http://repository.jboss.org/nexus) a. Run ./bin/release_to_nexus.sh (you need access permissions to Nexus) b. Go to Nexus: https://repository.jboss.org/nexus c. Log in (jboss.org login) d. Click on Staging repositories in the left hand pane e. Right click on the uploaded artifacts (JAR and src JAR) and either drop or promote them. In case of promotion, the artifacts will be promoted to the jboss-releases repository and removed from the staging repo - Announcement to the jg-dev and jg-users mailing lists -include release notes and changelog NOTE: to perform these steps, you will need a SourceForge account and appropriate permissions. libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.4.txt0000644000175000017500000001431311366547366022540 0ustar twernertwerner Release Notes JGroups 2.4 ========================= Version: $Id: ReleaseNotes-2.4.txt,v 1.10 2006/10/27 12:58:17 belaban Exp $ Author: Bela Ban JGroups 2.4 is a backward compatible release, that is it can replace previous versions (2.2.7 - 2.3) by simply replacing the JGroups JAR file. The new features are described below. The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html FLUSH protocol -------------- This essentially adds another virtual synchrony implementation for JGroups (the old one being vsync.xml), however it is based on the default stack which has been used for quite a while now, and which is well tested. FLUSH is required when we have multiple services sitting on the same Multiplexer, and more than 1 of them requires state transfer (for an explanation see JGroups/doc/design/PartialStateTransfer.txt). Partial state transfer ---------------------- Sometimes an application doesn't want to transfer the entire state, but just a subset. Example: JBossCache in JBoss, where we may want to transfer just a subtree, not the entire tree. Streaming state transfer ------------------------ When we need to transfer large state, the existing state transfer requires an application programmer to provide the state as a byte[] buffer. If the state is large, or needs to be retrieved by traversing a tree structure (e.g. a DOM tree), then providing a byte[] buffer may not make much sense. Streaming state transfer provides an InputStream to read state and an OutputStream to write state, and transfer the written bytes in *chunks*, so that the memory requirements are smaller. Example: if we have a huge DOM tree, whose aggregate size is 2GB (and which has partly been passivated to disk), then the state provider (ie. the coordinator) can simply iterate over the DOM tree (activating the parts which have been passivated out to disk), and writing to the OutputStream as he traverses the tree. The receiver will simply read from the InputStream and reconstruct the tree on his side, possibly again passivating parts to disk. Rather than having to provide a 2GB byte[] buffer (besides the state, so the needed memory is ca 4GB temporarily), streaming state transfer transfers the state in chunks of N bytes (user configurable). Check out the documentation for details and sample code. View bundling ------------- When we have many concurrent JOINS, LEAVES or SUSPECTs, then we emit a view for each and every one of them. Say we have 20 JOINs, then we will have 20 views. With view bundling, we can reduce this by simply handling multiple JOIN/LEAVE/SUSPECT requests together as one. This might generate 5 views rather than 20 (config-dependent). Failure detection: FD_PING -------------------------- This allows to run a script to probe for liveness of a host. Default is /sbin/ping (ping.exe on Windows). Can be used in addition with FD_SOCK/FD. Failure detection: FD_ICMP -------------------------- Uses InetAddress.isReachable() to determine whether a given host is reachable. Note that this may or may not use ICMP pings, depending on the JDK implementation. Can be used in conjunction with FD_SOCK/FD/FD_PING. Performance measurements ------------------------ Performed on 4, 6 and 8 node clusters. Results are available at http://www.jgroups.org/javagroupsnew/perf/Report.html. Use of variables in configuration files --------------------------------------- Variables of type ${var:default} or ${var} can now be used in configuration files (both XML and plain text). They will get replaced by the result of System.getProperty(var, default). If no system property is set, the variables will not get substituted. Note that variables and default values cannot contain characters '{', '}' or ':'. User-defined Marshaller is now used for return values too --------------------------------------------------------- Before, a user defined Marshaller in RpcDispatcher was only used for marshalling of a request at the caller and unmarshalling of the request at the receiver, but not for marshalling the response at the receiver and unmarshalling the response at the caller. In the latter 2 cases, Util.objectTo/FromByteBuffer() was used. Now, in all 4 cases, the Marshaller will be used (if set). Optimization in Util.objectTo/FromByteBuffer() ---------------------------------------------- We don't create an ObjectInput/OutputStream for null values and simple primitive types. Incompatibilities to previous version ------------------------------------- - ExtendedMessageListener: we added 4 methods, so if you used ExtendedReceiverAdapter you're fine. Otherwise you will have to recompile. Note that this does *not* affect just linking against JGroups, so if you simply replace your jgroups.jar, you're fine Enhancements and bug fixes -------------------------- - COMPRESS now uses multiple deflaters/inflaters, this results in more concurrent compression, more performance - Multiplexer: few bug fixes (detected by integrating into JBoss), there are now 2 new unit tests that cover the bugs fixed - Bug fix for VIEW_SYNC after network partition and subsequent merge (http://jira.jboss.com/jira/browse/JGRP-217) - DistributedLockManager now releases locks leftover by a crashed process - Multiplexer: problem with state transfer (http://jira.jboss.com/jira/browse/JGRP-280) - Issue when members would suspect themselves after a network partition and subsequent merge (FD, FD_SOCK and VERIFY_SUSPECT) (http://jira.jboss.com/jira/browse/JGRP-282, http://jira.jboss.com/jira/browse/JGRP-283) - Optimization of marshalling in Util.object{To,From}ByteBuffer() (http://jira.jboss.com/jira/browse/JGRP-284) - Incorrect suspect warning with FD_SOCK (http://jira.jboss.com/jira/browse/JGRP-285) - Multiplexer: view not sent when member crashes (http://jira.jboss.com/jira/browse/JGRP-286) - Multiplexer: underlying JChannel not closed in MuxChannel in certain scenarios (http://jira.jboss.com/jira/browse/JGRP-288) - Pull-the-plug scenarios with TCP, lead to incorrect behavior before, is now fixed: - http://jira.jboss.com/jira/browse/JGRP-217 - http://jira.jboss.com/jira/browse/JGRP-302 - http://jira.jboss.com/jira/browse/JGRP-304 Documentation ------------- - The user's guide has been updated (http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html)libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.6.txt0000644000175000017500000000351011366547366022703 0ustar twernertwerner Release Notes JGroups 2.6.6 =========================== Version: $Id: ReleaseNotes-2.6.6.txt,v 1.1.2.1 2008/11/04 12:23:20 belaban Exp $ Author: Bela Ban JGroups 2.6.6 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4 and 2.6.5. Below is a summary (with links to the detailed description) of the major new features between 2.6.5 and 2.6.6. Added socket read, close and connect timeouts in GossipRouter/GossipClient -------------------------------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-852] Configurable, can prevent denial-of-service attacks. Performance improvements ------------------------ [https://jira.jboss.org/jira/browse/JGRP-846] [https://jira.jboss.org/jira/browse/JGRP-847] Certain input and output streams had synchronized methods. Replacing them (because there is no concurrent access) improved performance (perf.Test tests) by about 9%. Bug fixes --------- Concurrent start of multiple channels on shared transport --------------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-849] Eliminate crosss-talking in MPING (on Linux only) ------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-836] FD_SOCK: no detection of multiple crashes ----------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-853] This is a bad regression, not present on CVS head (2.7) ! Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Nov 4 2008 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.5.txt0000644000175000017500000000247311366547366022711 0ustar twernertwerner Release Notes JGroups 2.6.5 =========================== Version: $Id: ReleaseNotes-2.6.5.txt,v 1.1.2.1 2008/10/10 10:35:33 belaban Exp $ Author: Bela Ban JGroups 2.6.5 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3 and 2.6.4. Below is a summary (with links to the detailed description) of the major new features between 2.6.4 and 2.6.5. Elimination of cross talking (on Linux) --------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-777] Different clusters using the same multicast port and only differing in the multicast address will not see each others' traffic anymore. See wiki.jboss.org/wiki/TwoClustersSameNetwork for details. Much faster discovery --------------------- [https://jira.jboss.org/jira/browse/JGRP-735] Nodes started after the initial coordinator will have a very fast discovery phase. Documentation is at http://wiki.jboss.org/auth/wiki/JGroupsPING. Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Oct 2008 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.2.txt0000644000175000017500000000750111366547366022703 0ustar twernertwerner Release Notes JGroups 2.6.2 =========================== Version: $Id: ReleaseNotes-2.6.2.txt,v 1.4.2.4 2008/02/25 16:26:33 belaban Exp $ Author: Bela Ban JGroups 2.6.2 is still API-backwards compatible with previous versions (down to 2.2.5). JGroups 2.6.2 is also binary backwards compatible with 2.6 and 2.6.1. Below is a summary (with links to the detailed description) of the major new features between 2.6.1 and 2.6.2. Shared transport ---------------- [http://jira.jboss.com/jira/browse/JGRP-631] When running multiple channels within the same JVM, we can make all channels refer to the same transport (e.g. UDP, or TCP). The transport then effectively becomes a JVM singleton. Note that the protocols of the individual stacks can be different, but the shared transport is the same. The advantage of this is that costly resources like thread pools are shared between channels, reducing overall resources (e.g. fewer threads). Compared to MuxChannels, shared transports do *not* change semantics, so there are no service views etc. Additionally, there is no separate thread pool needed, like in the Multiplexer. So shared transports can be used as a replacement for MuxChannels. Documentation is at http://www.jgroups.org/javagroupsnew/docs/manual/html/user-advanced.html#d0e2325. FLUSH: flush a subset of the cluster ------------------------------------ [http://jira.jboss.com/jira/browse/JGRP-661] This is needed for buddy replication in JBossCache for example, where we don't want to flush the entire cluster, but just a few members. Advantage: not everyone needs to block during the flush phase; other members can continue sending messages. Eager lock release in NAKACK and UNICAST ---------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-656] Determines when a message can be considered 'delivered'. When set to true, and the message receiver has received it, but calls down() on the same thread, the lock on the sender is released when eager_lock_release is set (default=true). This allows another thread to acquire the lock and process the next message *from the same sender*. This results in performance gains, and does *not* change the ordering of messages from a given sender. Note that messages from different senders are not affected, as they're delivered concurrently anyway. Hooks to provide ones own thread factory and/or thread pools (transport) ------------------------------------------------------------------------ [http://jira.jboss.com/jira/browse/JGRP-683] Users can now replace the thread factories which create all threads. Additionally, the default and OOB thread pools in the transport can be replaced / modified as well. PING can now use multiple GossipRouters --------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-684] This is for redundancy; a member can register itself with multiple GossipRouters, and all will get pinged for initial membership. This eliminates a single point of failure. TCPPING: discovery done in parallel ----------------------------------- [http://jira.jboss.com/jira/browse/JGRP-375] Instead of sequentially pinging all listed members (some of which might block), we do this in parallel. This reduces chances of not getting a response from a member. Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Bug fixes --------- Multiplexer and merging: incorrect merging in some scenarios [http://jira.jboss.com/jira/browse/JGRP-665] Multiplexer ignores OOB flag [http://jira.jboss.com/jira/browse/JGRP-663] Messages get too big for UDP transport [http://jira.jboss.com/jira/browse/JGRP-667] The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Feb 2008 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.txt0000644000175000017500000001061211366547366022540 0ustar twernertwerner Release Notes JGroups 2.6 ========================= Version: $Id: ReleaseNotes-2.6.txt,v 1.3 2007/11/06 12:32:33 belaban Exp $ Author: Bela Ban JGroups 2.6 is still API-backwards compatible with previous versions (down to 2.2.5). Below is a summary (with links to the detailed description) of the major new features. Join and state transfer ----------------------- [http://jira.jboss.com/jira/browse/JGRP-236] We added another connect() method in JChannel, which combines joining a cluster and fetching the state from the coordinator into one method. This is especially useful when we have FLUSH in the stack; thus we only have to use 1 rather than 2 (1 for JOIN, 1 for state transfer) flush phases. Improved ReplicatedHashMap -------------------------- [http://jira.jboss.com/jira/browse/JGRP-581] ReplicatedHashMap was converted to use generics, and java.util.concurrent.ConcurrentHashMap. It therefore supports 4 new methods putIfAbsent(), remove() and the two replace() methods. Developers can choose whether to use asynchronous or synchronous replication, and also pick the timeout for synchronous calls. This class supercedes ReplicatedHashtable and DistributedHashtable, which will be removed in version 3.0. Reincarnation issue ------------------- [http://jira.jboss.com/jira/browse/JGRP-130] Using the GMS.reject_join_from_existing_member (default=false) property, we can reject a JOIN request from a reincarnated member X who crashed, but has not yet been removed (e.g. due to a high timeout in FD). The member would have to retry, and would only succeed when (the old) X has been excluded from the cluster. For shunned members who use AUTO_RECONNECT, we loop until this is true [http://jira.jboss.com/jira/browse/JGRP-584]. New transport property 'bind_interface' --------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-579] This can be used when multipler network interfaces have the *same* IP address, to define the interface to get used, e.g bind_addr="192.168.2.5" bind_interface="eth1". Useful e.g. with IP Bonding on Linux. FLUSH simplification -------------------- [http://jira.jboss.com/jira/browse/JGRP-598] Removed 2 phases from FLUSH protocol, which simplified code and made FLUSH faster. Unicast bundling can be disabled at the transport level ------------------------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-429] When dealing with latency sensitive applications, we may want to disable message bundling for *responses* (but not for requests, as requests might carry large payloads). This can be done via the enable_unicast_bundling (default=true) property. RpcDispatcher can now filter responses as they arrive ------------------------------------------------------ [http://jira.jboss.com/jira/browse/JGRP-518] There's a new callRemoteMethods() method taking an RspFilter, which is called whenever a response has been received, allowing a request to return based on a condition (e.g. the first non null return value) before all responses have been received. Ability to add data to a view ----------------------------- [http://jira.jboss.com/jira/browse/JGRP-597] Arbitrary data can be added to a View, which now has a hashmap. Keys and values need to be serializable. Improved thread naming ---------------------- [http://jira.jboss.com/jira/browse/JGRP-570] All threads now hae meaningful names, including their local address and cluster name. This makes it easier to read stack traces, when we have multiple channels (e.g. in JBoss). In addition, all threads are now created by the same thread pool. Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Performance ----------- Links to performance tuning: http://wiki.jboss.org/wiki/Wiki.jsp?page=PerfTuning Bug fixes --------- AUTH: bug in 2.5 which caused AUTH to fail on second and subsequent JOIN attempts *if* the first attempt was rejected by AUTH. [http://jira.jboss.com/jira/browse/JGRP-577] VIEW_SYNC: there was a regression in 2.5, which causes VIEW_SYNC messages to get dropped. Note that this bug didn't occur in 2.4.x. [http://jira.jboss.com/jira/browse/JGRP-586] X.509 token not marshalled correctly. This affects ENCRYPT. [http://jira.jboss.com/jira/browse/JGRP-576] The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Nov 2007 libjgroups2.6-java-2.6.15.GA.orig/doc/design/0000755000175000017500000000000011621261107020330 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/design/ViewHandling.txt0000644000175000017500000000560711366547366023504 0ustar twernertwerner New view handling ================= Author: Bela Ban Version: $Id: ViewHandling.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ Problem: Merge, join and leave requests need to handled in the order in which they arrive. Currently, the merge handling is independent from the view handling, which results in problems with generating merge VIDs which are lower than the highest VIDs generated by the view handling, as described in MergeProblem.txt. Proposed solution: - Turn the ViewBroadcaster into a ViewHandler which has a queue - JOIN, LEAVE and MERGE requests received by the coordinator simply add an entry to the queue - A thread dequeues elements from that queue and processes them, one at a time, to ensure FIFO handling of those requests --> One result of this is that the merge handling is now *not* done asynchronously anymore ! - A side effect of this is that we could implement a feature which waits for N JOIN/LEAVE requests or M milliseconds, and then processes a number of JOIN and LEAVE requests as *one* view, which is more efficient, given the fact that views are transmitted reliable, ie. for each view we need to receive an ack from each member - When the coordinator itself leave, we need to flush the queue, ie. not accept new requests, and complete the existing requests Merging: - When we receive a MERGE event, the merge coordinator needs to inform all coordinators involved to flush their queues in the ViewHandler. - This processes all requests in the queue, then simply discards new requests (or queues them in a separate queue). We could also simply discard all current requests in the queue, since for example a JOIN request will redirect its JOIN-REQ to a new coordinator until successful. - When the merge has successfully run (or aborted), all the queues in the ViewHandlers need to resume - The reason for doing this is that the MergeView's VID needs to be higher than any view seen by any of the coordinators participating in the merge. If a coordinator was allowed to continue generating new views, then this would not be the case Implementation -------------- - LEAVE of coordinator: - Add LEAVE request to queue - Stop (=flush) queue. Wait until all requests have been processed, then leave - LEAVE of regular member P: - Add LEAVE request to queue: - Compute new view V (excluding P) - Multicast V to all members of V, wait for VIEW_ACKs - JOIN(P): - Add JOIN request to queue - Compute new view V including P - Multicast V to all members of V *excluding* P (this is necessary because we won't receive a VIEW_ACK from P, so we would block) - Send unicast JoinRsp to P - SUSPECT(P): - Add SUSPECT request to queue - Same as LEAVE handling - STOP of coordinator: - Stop (=flush) and close queue. Wait until all requests have been processed, then leave - This is probably superfluous (already done by LEAVE(coord)), but double-check - MERGE: libjgroups2.6-java-2.6.15.GA.orig/doc/design/FlowControl.txt0000644000175000017500000000430611366547366023370 0ustar twernertwerner// Version: $Id: FlowControl.txt,v 1.1 2006/01/27 14:38:35 belaban Exp $ // Author: Bela Ban Flow control (FC) ================= Constants: ---------- - max_credits (example 2000000) - min_credits (example 200000) Vars: ----- - sent_msgs: Map, maps destinations to credits - received_msgs: Map, maps receivers to credits - running: termination condition - insufficient_credit: condition, message sending blocks when true - creditors: list of destination for which we current don't have enough credits to send messages - lowest_credit: the lowest credits of any destination (sent_msgs) - mutex: for blocking of a sender Init: ----- - running = true - insufficient_credit = false - lowest_credit = max_credits On message send: ---------------- - length = length of message - if (lowest_credit - length <= 0): - insufficient_credit = true [- determine creditors] - while (insufficient_credit and running): - wait on mutex (timeout) - if timeout: send credit request to all creditors - else - in sent_msgs: decrement length from all members (if multicast) or specific member (if unicast) - adjust lowest_credit - send message On message receive: ------------------- - length = length of message - sender = sender of message - in received_msgs: subtract length from sender M - if M doesn't have enough credits (credits left <= min_credits): - send credits to M - replenish credits for M (in received_msgs) On credit receive from P: ------------------------- - in sent_msgs: update credits for P - compute lowest_credit - remove P from creditors - if (lowest_credit > 0 and creditor.size() == 0): - set insufficient_credit = false - notify mutex On credit request receive from P: --------------------------------- - send credits to P On view change: --------------- - Remove all entries from sent_msgs, received_msgs which are not in the new view - Add entries for all members of the new view for which there are currently no entries - Remove all creditors which are not in the new view - Compute lowest_credit - if (lowest_credit > 0 and creditor.size() == 0): - set insufficient_credit = false - notify mutex On stop: -------- - Set running = false - Notify mutexlibjgroups2.6-java-2.6.15.GA.orig/doc/design/FLUSH2.txt0000644000175000017500000000563011366547366022064 0ustar twernertwerner FLUSH2 design ============= Author: Bela Ban Version: $Id: FLUSH2.txt,v 1.5 2007/10/22 19:46:52 belaban Exp $ Prerequisites: - A flush is always started and stopped by the *same* member, both for joins and state transfer - For state transfers (but not for joins), we can have different members start a flush at the same time Structures: - FlushState - flush_active: whether we are currently running a flush - flush_leaders: set of members which started the flush (no duplicates) - start_flush_set: list of members from whom we expect a START-FLUSH-OK message - digests: digests from all members, received with START-FLUSH-OK message - stop_flush_set: list of members to whom we send a STOP-FLUSH message - down_mutex: for blocking of multicast down calls, unicasts are passed at all times On SUSPEND event: - If FlushState.flush_active: - Add flush leader to flush_leaders (if not yet present) - If already present: terminate (don't send START-FLUSH message) -Else - Set FlushState.flush_active to true - Add flush leader to flush_leaders (if not yet present) - Set start_flush_set and stop_flush_set - Multicast START-FLUSH - Block until start_flush_set is empty (block on start_flush_set) - Look at FlushState.digests: - If all digests are the same --> return - Else --> run reconciliation phase On START-FLUSH(sender=P): - If FlushState.flush_active: - Add flush leader to flush_leaders (if not yet present) -Else - Set FlushState.flush_active to true - Add flush leader to flush_leaders (if not yet present) - Call block() - Make down_mutex block multicast messages from now on - Return START-FLUSH-OK with my digest to P On START-FLUSH-OK(sender=P,digest D): - Add digest D to FlushState.digests - Remove P from FlushPhase.start_flush_set - Notify FlushPhase.start_flush_set if empty On RESUME event: - Multicast STOP-FLUSH event (asynchronously, don't wait for results) On STOP-FLUSH(sender=P): - Remove P from flush_leaders - If flush_leaders is empty: - Unblock down_mutex On SUSPECT(S): - Remove S from start_flush_set and stop_flush_set - If start_flush_set is now empty --> notify thread blocking on it - Remove S from FlushState.flush_leaders - If FlushState.flush_leaders is empty: (flush leader S crashed during flush !) - Unblock down_mutex On view change V: - Remove all members that are not part of V from start_flush_set and stop_flush_set - If start_flush_set is now empty --> notify thread blocking on it - Remove all members from FlushState.flush_leaders which are not part of V - If FlushState.flush_leaders is empty: (flush leader crashed during flush !) - Unblock down_mutex Reconciliation phase (in NAKACK): - Sends XMIT requests for message not in local digest, but in digest received from REBROADCAST_MSGS - Wait until local digest (updated when missing messages are received) is same as digest received from REBROADCAST_MSGS - Takes suspects into accountlibjgroups2.6-java-2.6.15.GA.orig/doc/design/ConcurrentConnectionEstablishment.txt0000644000175000017500000000365311366547366030011 0ustar twernertwerner // Version: $Id: ConcurrentConnectionEstablishment.txt,v 1.3 2007/09/19 12:42:43 belaban Exp $ // Author: Bela Ban Concurrent initial connection establishment in ConnectionTable ============================================================== [JIRA: http://jira.jboss.com/jira/browse/JGRP-549] If we have members A and B, and they don't yet have connections to each other, and start sending messages to each other at the exact same time, then we run into the following scenario: - A attempts to connect to B (new Socket()) - B attempts to connect to A - A accepts B's connection and adds an entry for B into its table - B adds an entry for A into its table - B gets the accept() from A, but doesn't add the new Connection into its table because there is already a Connection. This leads to a spurious Connection object, which will only be closed and garbage collected once A or B leaves the cluster - We used to close Connections which were already in the table, but this lead to ping-pong effects where concurrent initial senders always closed each others connections - Even worse: a new Socket() always creates a new Connection object and places it into the table, *regardless* of whether a Connection for a given destination already exists or not ! SOLUTION: We need to have a deterministic algorithm to pick a 'winner' for concurrent connections. Let's assume our own address is A On connect to B: ---------------- - Lock CT - If connection exists for B: return existing connection - Else: - Connect(B) - Add connection to CT - Unlock CT On connection accept from B: ---------------------------- - Lock CT - If there is no key for B, we create a connection for B and add it to the CT - Else: - If B's address > A's address --> remove the existing entry for B (closing the connection !) and add the newly created connection for B to the CT - Else close the connection which resulted from accept(B) - Unlock CTlibjgroups2.6-java-2.6.15.GA.orig/doc/design/ReliableViewInstallation.txt0000644000175000017500000001503311366547366026053 0ustar twernertwerner Reliable view installation ========================== Author: Bela Ban Version: $Id: ReliableViewInstallation.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ The default stack sees view as just messages; a view multicast is retransmitted until the sender is excluded because it left or crashed. However, this behavior can lead to problems described below. The problems occur when a view multicast is lost. A: New coordinator doesn't take over ------------------------------------ - Group is {A,B,C}, A is coordinator - A leaves gracefully, multicasts view V2 {B,C} - C receives V2, B doesn't receive V2 - A terminates, which means that retransmission of A's V2 in B stops (B never receives V2) - B's view is now V1 {A,B,C} - C's view is now V2 {B,C} - B does *not* become the new coordinator ! Problem 1: - A new member D wants to join, B replies to the discovery request with coord=A, C replies with coord=B - If D sends a JOIN request to B, B will reject the JOIN request because it thinks the coord is still A ! - If D sends a JOIN request to A, it will time out and receive no response --> In any case, D will continue looping forever, or until B is killed ! Problem 2: - B invokes a group RPC. Because it still thinks A is part of the group, it will wait for A's reply, which never arrives. If the mode is GET_ALL and timeout is 0, then this call will block forever ! B: Continuous SUSPECT messages ------------------------------ - Group is {A,B,C}, A is coordinator - A leaves gracefully, multicasts view V2 {B,C} - B receives V2, C doesn't receive V2 - A terminates, which means that retransmission of A's V2 in C stops (C never receives V2) - C's view is now V1 {A,B,C} - B's view is now V2 {B,C} - When A terminates, C starts multicasting SUSPECT(A) messages to the group - B (as coord) handles the SUSPECT(A) message, but since A is not a member of B's view V2 {B,C}, B simply *ignores* the SUSPECT(A) message ! - C continues multicasting SUSPECT(A) messages to the cluster Problem: - C invokes a group RPC. Because it still thinks A is part of the group, it will wait for A's reply, which never arrives. If the mode is GET_ALL and timeout is 0, then this call will block forever ! C: New coordinator doesn't take over, old coord crashes ------------------------------------------------------- - Same as case A, but old coordinator crashes before V2 can be retransmitted to B - So B doesn't receive V2 D: The coordinator leaves while another member joins ---------------------------------------------------- - In the current impl, the new member P will receive a view V in a JoinRsp - The VIEW V is then to be broadcast by the coord - However, the coord leaves before this happens --> Everybody has the old view, while P has the new view ! --> We need to atomically return the JoinRsp and broadcast the view ! --> Maybe we should broadcast the view first and then return the JoinRsp to the client ! --> See ViewHandling.txt for a new approach SOLUTION: 1: Views need to be ack'ed -------------------------- - When multicasting a view, we block until every member in that view has acknowledged the view reception - Because GMS runs above both UNICAST and NAKACK, we *don't* need to retransmit, but we cannot terminate (as in case A), before having received an ACK for the view from each member in the new view --> This solves case A 2: Handling crashes of the coordinator while multicasting a view ---------------------------------------------------------------- - If the coordinator crashes before having received all ACKs, some members might still not receive the new view. (This is highly unlikely though) - If we therefore receive a SUSPECT(P) where P is not member of the current view, we multicast the current view V to the group. Those member who already installed V will simply ignore it, those who didn't will install it --> This solves case B - The STABLE protocol periodically multicasts digests. We now add the current view to the digest. - When the coordinator receives a view V that is greater than its own view, it installs V and multicasts it - Member who didn't install V will install it - Member who installed V will ignore it - When the coordinator receives a view V which is smaller than its current view V-curr, it will multicast V-curr, so that those members who didn't install V-curr (and therfore sent V with their digest) will install V-curr. - When the coordinator receives a view V which is identical to the current view V-curr, it doesn't do anything - Note that, if the received view is greater than the current view, every member checks whether it would be the new coordinator. If this is the case, it becomes coordinator, installs the view and multicasts it --> This solves cases A, B and C DESIGN: On castViewChange(View v) [coord]: ---------------------------------- - Reset view_change_mutex - Populate missing_acks list with contents of v - Multicast v - Block on view_change_mutex (possibly with timeout (0 == wait forever)) On SUSPECT(P) / on VIEW_ACK(P) [coord]: --------------------------------------- - Remove P from missing_acks - If missing_acks is empty --> notify view_change_mutex - Neeeds to be done in receiveUpEvent(), real work is then done in up() On VIEW(v) [all]: ----------------- - Send VIEW_ACK to coordinator - Neeeds to be done in receiveUpEvent(), real work is then done in up() On JOIN_RSP [client]: --------------------- - Send VIEW_ACK to coordinator Two-Phase View installation ---------------------------- - First phase: coord multicasts PREPARE_VIEW(V) - Coord waits for all ACKs - If SUSPECT(P) is received, coord creates new view V (excluding P) and restarts first phase - When all ACKs have been received, coord multicasts COMMIT_VIEW(V) - When a participant receives the PREPARE_VIEW, it starts a timer. When the COMMIT_VIEW(V) is received, the timer is cancelled. When the timer expires in participant R, we multicast a STATUS_VIEW(V). If a participant has received the COMMIT_VIEW(V) message for the given view, it multicasts the COMMIT_VIEW(V) again, so that R can commit (= install) V. - If the coordinator crashed before multicasting the COMMIT_VIEW, the SUSPECT(coord) message will make the second-in-line the new coordinator, and it will either restart the first or second phase - If the coordinator crashed after sending the COMMIT_VIEW, and at least 1 member M received the COMMIT_VIEW, M will be able to multicast COMMIT_VIEW when asked by other participants. If no member received COMMIT_VIEW, then this is treated as if COMMIT_VIEW was never sent, and the new coordinator will restart the first and second phase libjgroups2.6-java-2.6.15.GA.orig/doc/design/PartialStateTransfer.txt0000644000175000017500000000567611366547366025235 0ustar twernertwerner // Version: $Id: PartialStateTransfer.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ // Author: Bela Ban Partial state transfer ====================== Requirement: ------------ We want to have multiple building blocks sitting on the same Channel (or PullPushAdapter). (This is the case when we have 1 Channel MBean in JBoss, and all the other HA building blocks (also MBeans) are sitting on top of this single Channel, multiplexing/demultiplexing requests between the channel and the building blocks.) If more than one of the blocks maintains state, we want a getState() method with an ID, where the ID identifies which state to fetch, e.g. (DistributedHashtable = DHT) DHT1 DHT2 DHT3 Channel When DHT2 comes online, it wants *its* state, and calls chanel.getState(new Integer(2)). The state retrieved from the coordinator should now only include the state for "2", but not the states of "1" and "3". Problem: -------- When we retrieve state with pbcast.STATE_TRANSFER, we ship the state plus a *digest* which contains the highest delivered seqnos that are included in a state. The digest is then set in the joining member to determine which messages are part of the state, and which ones need to be accepted (consistent snapshot). Since the digest applies to *all* messages (for all states), we cannot retrieve a single state). Example: -------- - Members A and B, joining member C, wants to retrieve state "2" - A receives 5 messages which affect state "1" (C hasn't received them yet) - A applies those messages to the state, then handles the state request for state "2" from C - A returns the state for "2" and the digest. The digest includes the 5 messages which updated state "1", so they will *not* be received by C. - C integrates the state for "2", and the digest. However, its state for "1" is now incorrect, as it doesn't contain the 5 messages, and when C receives those 5 messages, they will be discarded because the digest indicates they are *not* part of the digest - Result: state "1" is incorrect in C Solution: --------- We cannot use pbcast.STATE_TRANSFER, but have to use a stop-the-world model: when a state is requested, everybody stops sending messages (similar to vsync), the (partial) state is transferred, and then everybody continues sending messages. This requires (a) a new state transfer protocol and (b) a flush protocol. The new state transfer protocol sends down a FLUSH event (at the coordinator) before returning the state. The flush protocol stops everyone from sending and makes sure everyone has the same set of messages before returning. On successful state transfer, everyone is allowed to send again. The FLUSH protocol could be reused for view changes as well (as in vsync). The connect() method could also be extended to optionally return the state, e.g. connect(String groupName, boolean fetchState, Object optionalStateId); This way, we would have to run the FLUSH protocol only one: for the view change *and* the state transfer. libjgroups2.6-java-2.6.15.GA.orig/doc/design/JoinAndStateTransfer.txt0000644000175000017500000000267311366547366025155 0ustar twernertwernerImplementation of JChannel.connect() with state transfer - http://jira.jboss.com/jira/browse/JGRP-236 ----------------------------------------------- There are essentially three major different implementations possibilites for new JChannel.connect with state transfer. Solution A: - implement state transfer within CoordGmsImpl.handleJoin running on ViewHandler thread - tightly ties state transfer to CoordGmsImpl and further bloats CoordGmsImpl.handleJoin - can we even keep ViewHandler thread so busy? Possibly run state transfer on a new thread spawned from CoordGmsImpl.handleJoin? Solution B: - listen for SUSPEND_OK in STATE_TRANSFER (flush succeded/cluster is quiet) at coordinator - add additional info to SUSPEND_OK so we know the joining member - do state transfer on SUSPEND_OK running on stack thread - use longer flush timeout for large states or periodically update timout to notify it of large transfer? Solution C: - initiate state transfer within JChannel.connect() after connect_promise is set - call JChannel.startFlush() and JChannel.stopFlush() - we do extra flush in this case Solution C is the most viable option for regular state transfer with flush. startFlush() and stopFlush() will be exposed on JChannel level and will be called by initiator of state transfer. Nice and clean solution. However, if we employ solution C for new JChannel.connect method the overhead of extra flush does not seem acceptable? libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-coord-leave.fig0000644000175000017500000001005011366547366024356 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 3037.500 2175 2775 2550 2925 2175 3300 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 6862.500 2175 6600 2550 6750 2175 7125 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 2362.500 2175 2100 2550 2250 2175 2625 0 0 1.00 60.00 120.00 6 1425 1200 3000 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 1425 1200 3000 1200 3000 2100 1425 2100 1425 1200 -6 6 5925 1200 7500 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 5925 1200 7500 1200 7500 2100 5925 2100 5925 1200 -6 6 10725 1200 12300 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 10725 1200 12300 1200 12300 2100 10725 2100 10725 1200 -6 6 7125 3825 8100 4050 4 0 0 50 -1 0 12 0.0000 4 180 945 7125 3975 get DIGEST\001 -6 6 225 3675 1200 3900 4 0 0 50 -1 0 12 0.0000 4 180 945 225 3825 get DIGEST\001 -6 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 2175 2100 2175 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 6675 2100 6675 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 11550 2100 11550 9150 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 3450 6675 3600 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 2175 2100 2175 9150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 3375 11550 3600 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 3675 2175 4650 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 6675 3675 2175 4575 2 2 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 6690 5325 11544 5325 11544 6375 6690 6375 6690 5325 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 7275 3750 7800 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 6686 3448 6986 1423 7286 3448 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 1528 3375 1828 1350 2128 3375 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 11550 3375 11850 1350 12150 3375 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 6825 8175 6675 7275 7125 6600 7125 6375 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 11775 7950 11625 7050 12075 6375 12075 6150 0.000 1.000 1.000 0.000 4 0 0 50 -1 0 12 0.0000 4 135 795 1800 1725 A:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 6300 1725 B:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 11100 1725 C:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 615 7500 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 825 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 7125 3750 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 225 3600 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 135 840 2700 3075 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 135 840 975 4875 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 105 540 975 5100 returns\001 4 0 0 50 -1 0 12 0.0000 4 135 765 2775 6750 RESUME\001 4 0 0 50 -1 0 12 0.0000 4 135 615 12150 2775 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 2700 2475 LEAVE\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 4500 3825 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 9000 3450 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 8250 4350 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 2475 4125 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 135 1860 8475 6000 VIEW INSTALLATION\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 7125 8025 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 7125 8250 latch\001 4 0 0 50 -1 0 12 0.0000 4 135 885 6975 7200 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 885 12000 7050 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 11775 8250 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 11775 8475 latch\001 4 0 0 50 -1 0 12 0.0000 4 135 1245 7125 7425 detect coord left\001 4 0 0 50 -1 0 12 0.0000 4 135 915 7125 7650 unblock self\001 4 0 0 50 -1 0 12 0.0000 4 135 1245 11850 7350 detect coord left\001 4 0 0 50 -1 0 12 0.0000 4 135 915 11850 7575 unblock self\001 4 0 0 50 -1 0 12 0.0000 4 135 885 1800 1950 Coordinator\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 2850 7200 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 135 735 2850 7425 discarded\001 libjgroups2.6-java-2.6.15.GA.orig/doc/design/varia2.txt0000644000175000017500000005274511366547366022316 0ustar twernertwerner$Id: varia2.txt,v 1.1 2006/05/22 06:34:43 belaban Exp $ Design Issues ============= Digest returned to client ------------------------- The digest returned to clients determines the seqnos they have to expect from other group members. If, in the absence of a retransmission protocol, a client gets back a lower seqno for a member P than is actually the case, the client will never make any progress, because lower seqnos need to be delivered before those with higher values. Therefore the client would hang (again, in the absence of retransmission). The problem at hand is in the coordinator, when a new member joins: the coordinator returns its own digest, which contains the highest seqnos from all current members (including itself) seen so far. It also includes the new member, with a seqno of 0 (starting seqno). Let's assume the coordinator has a seqno of 35. Now, the new view is mcast before the call (HandleJoin) returns to the client (containing the digest). The client therefore gets a wrong seqno for the coordinator. The 2 scenarios are described below: A: The coordinator gets its digest and then mcasts the view. The seqno for the coordinator in the digest returned to the client would therefore be 35. The client will expect the next message from the coordinator to be a message labeled with seqno=35. However, the coordinator mcasts the new view, therefore incrementing its seqno to 36. The client discards this mcast, because it is not yet operational (it is only operational after setting the digest returned from the coordinator). Therefore the client will have a missing seqno=35 when it received seqno=36 or higher, which will cause it not to deliver any message higher than 35 until 35 is finally received. In the absence of retransmission, this would effectively hang the client. B: The coordinator mcasts the new view and then gets the digest. Here, the hope is that the mcast is sent *and* received before the GET_DIGEST event returns. Currently this is the case. However, in slow machine, the GET_DIGEST might actually return before the view mcast is received, giving us a seqno=35 again, compared to seqno=36 in the former case. Since the latter case is highly timing-dependent, we chose to adopt case #1. The kludge in case #1 is that, in order to avoid the client getting a lower seqno, we actually have to increment the coordinator's seqno by 1 in the digest returned to the client. This is done towards the end of the client's Join() method. 'Highest delivered' vs. 'highest deliverable' --------------------------------------------- Currently, we always return the highest delivered message seqno from NakReceiverWindow rather than the highest deliverable seqno. This would leade to discrepancies if for example a consumer did not consume messages, or did not consume them fast enough. However, in PBCAST, the HandleUpMessage() ensures that, as soon as it adds a message, it tried to remove as many as possible. Therefore, deliverable messages are always consumed, making highest deliverable == highest delivered. Local messages -------------- Assumption is that messages sent to self or mcast to the group are always received by self. If this is not the case, the PBCAST layer would have to store not only received messages, but also sent ones. (This is currently not the case, would be an easy change). Garbage collection lag-behind ----------------------------- When a gossip is received by a member, the member asks for retransmission of yet unseen messages (if necessary) and removes itself from the not-seen list of the gossip. Then it re-broadcasts the gossip (if not yet re-broadcast previously). When the member removes itself from the not-seen list, and the list is subsequently empty, this means that the gossip has been seen by all the members. Therefore, all messages lower than the ones in the gossip's message digest can be discarded. However, as the removal from the not-seen list only signals gossip reception, but not actual reception of the requested missing messages, we cannot really be sure that those messages have really been received. Therefore, the strategy adopted here is to introduce some 'lag', which is the number of messages that we should wait before really garbage-collecting messages in the digest. This number is subtracted from the digest's highest seqnos, so if for example the received digest is [P:25, Q:3, R:100] and the lag is 10, then we would garbage-collect messages lower than P:15, R:90 and none of Q's messages. The lag value can be set differently for different protocol stacks, therefore we could determine a number of members that are "late-join-handlers" (see Birman et al). Those members would have a high lag values, to make sure that at least some of the group members have almost all of the messages ready, e.g. for new joiners who want to catch up. As a matter of fact the late-join-handlers may choose to store their message digests in stable storage, e.g. a database. GET_DIGEST vs. GET_DIGEST_STATE event ------------------------------------- The latter is used by the STATE_TRANSFER layer to fetch the digest from the PBCAST layer. However, as GMS (below STATE_TRANSFER) also uses the GET_DIGEST family of events, there would be interference. E.g. when STATE_TRANSFER sends down a GET_DIGEST event and waits for the GET_DIGEST_OK event, it will never receive it because GMS catches and handles it, and does not pass it up, although it was not destined for GMS, but for STATE_TRANSFER. This is the reason why there is yet another event GET_DIGEST_STATE. Clearly a kludge, the better solution would be to tag an event with its sending protocol, have the receiver construct a response event from it, and have every receiver of the response discriminate according to its own identity. Similar scheme as the one used for sending messages. Handling of received gossips (PBCAST.handleGossip() method) ----------------------------------------------------------- First make sure that the sender of the gossip is not null. If it was null, we wouldn't be able to ask it for retransmission of missing messages. We check for this right at the beginning of the method, so we can return immediately and don't have to do all the processing if the sender is null. Then a check is made whether the sender really is a member. If it is not, we return as well. There are 2 cases when this can happen: (a) when a member left the group and (b) when a new member joined, but the membership is not yet updated because the current member has not yet received the new membership list. In the first case, we can safely discard the gossip because we won't need any of the messages of the dead member. Gossips sent by dead members will only be in the system for a short time, until everybody has received them. In the second case, which is assumed to be very rare, a new member S has joined the group, received its digest from the coordinator and, before the coordinator's new view was received by a member P, that member receives a gossip from S. This is highly unlikely, but P will just discard S's gossip. This is not a problem, because S will continue sending gossips, and at some point will be a member, therefore its gossips will not be dropped any longer. Now, we check whether the gossip has been received before. We keep a list of gossips received (bounded cache in which the oldest gossips are removed before adding a new one). If the gossip has been seen before, it is discarded and the method returns. Otherwise we add the gossip to the end of the list. Since a gossip was already received, we already removed ourself from the not-seen list (see below) and re-sent the gossip, so we don't need to handle it again. This also prevents network flooding by gossips, e.g. caused by spurious retransmission requests, all to the same sender (the one who sent the gossip). Now we compare the digest received as part of the gossip for messages (from any senders) that we haven't seen yet. If there are any missing message, we send a retransmission request (a hashtable of senders and the messages missing from those senders) to the sender of the gossip. Next, we check who has already seen this gossip. If the not-seen list is empty (after removing ourselves, because we have now seen the gossip), we can assume that every other member in the group has received the messages in the gossip's digest, and therfore garbage collect the messages seen by all members. If the not-seen list is empty, we also do not need to resend this gossip, because everybody has already seen it. Therefore, the method returns. If there are still members in the not-seen list, we pick a random subset of those members and resend the gossip to them. Great care has to be taken not to flood the network. For example, if the group size is big and the gossips are resent to 30% of the members in a relatively short time interval (gossip_interval), then the network might easily become flooded. This is because every receiver potentially resends the gossip to another 30% of the members and because each receiver might request retransmission from a single sender of a gossip, which might lead to a message implosion problem. On the todo list for JavaGroups is therefore a mechanism which dynamically adjusts the subset percentage and the gossip interval with increasing or decreasing group size. Another additional line of defense is to have a bounded buffer in PBCAST to which all PBCAST-related messages (e.g. gossips, xmit-reqs, xmit-rsps) are added. When the buffer is full, new requests will just be discarded. This prevents flooding of both the buffers and the network (e.g. by not forwarding gossips). Regular unicast messages are passed up the stack, and multicast messages are directly handled (higher priority than gossips). Dynamic adjustment of gossip_interval in PBCAST ----------------------------------------------- The gossip_interval (frequency with which gossips are sent by each member) can optionally be determined dynamically by JavaGroups as a function of the group size. We assume that almost every gossip reaches every other member (definitely if use_mcast_for_gossip is true). Therefore a gossip is used by each member which received it to adjust the time it hasn't heard from the members which have received the same gossip. This is done by resetting the times for all members which are in the gossip's path, i.e. the seen_list. The desired average gossip time (desired_avg_gossip) defines the average time between gossip receptions for a member. E.g. if it is set to 30 seconds, then the member will receive a gossip every 30 seconds on average. The gossip_interval is computed as follows: gossip_interval = Random(desired_avg_gossip * 2 * num_mbrs) For example: if the desired average gossip reception time is 30 secs, and we have 10 members in the group, then the gossip_interval will be randomly chosen from the range [1 - 600secs]. The average random value will be 300secs. Divided by 10 members, the average gossip reception will be 30 secs. The gossip_interval is determined anew before each gossip, so that members vary their gossip_interval. Thus, gossip_interval increases with group size. The following rules govern whether static or dynamic determination of gossip_interval will be used: - if dynamic: dynamic computation of gossip_interval --> gossip_interval is ignored --> desired_avg_gossip and number of members is used to determine gossip_interval - if dynamic=false: static use of gossip_interval --> gossip_interval is set Tracing ------- Most diagnostic output is through JavaGroups.Common.Trace, e.g. Trace.println("FD.run()", Trace.WARN, "could not ping " + dest); The Trace.setOutput() methods enable to switch tracing on/off, so the above message will be written (e.g. to a file) or not. However, if we don't want any output, the 2 strings "FD.run()" and "could not ping" will still be created, which results in a lot of garbage. Therefore, the convention was adopted in the Protocols and Protocols/pbcast directories to check for the boolean flag 'trace' first, and only if it is true, to actually call Trace.println(). Therefore the above example can be rewritten as if(trace) Trace.println("FD.run()", Trace.WARN, "could not ping " + dest); The 2 strings will only be created if 'trace' is true, which saves a lot of accumulated creation time and memory when JavaGroups needs to be run without tracing enabled. Note that code that will only be executed in exceptional cases does not necessarily be preceeded by the "if(trace)" conditional statement, e.g. try { // try out something } catch(Exception ex) { // should never be reached Trace.println("FD.PingDest()", Trace.DEBUG, ex.printStackTrace()); } To balance the need to avoid excesssive string creation on the one hand and the overhead and adverse effect of trace statements on readability, as a rule of thumb, the "if(trace)" statement should be used in places where a big number of strings would be generated if trace is disabled. Membership Updating for JOIN/LEAVE requests ------------------------------------------- When many members join a group at the same time, there were inconsistent views mcast by the coordinator (P). The reason was that P made a *copy* of its membership M1, added the new member and mcast the new membership M2 to all members. P would only update its membership upon reception of M2. However, if there were client JOINs before receiving M2, P would compute the new membership based on its (old) M1, which would not take into account members who joined between M1 and M2. Solution: P updates its membership locally before mcasting the new membership. As soon as M2 is received, the membership will be set to M2 (which is the same). There is a little twist on the above solution: when a coordinator P receives a number of simultaneous JOIN requests (e.g. from A, B, C), the membership will be {P, A, B, C} after C's request has been processed. However, if we then receive the view for the first join, which is {P, A}, the membership would be reset to {P, A}. Further JOIN requests received before view {P, A, B, C} would operate on {P, A} instead of the correct {P, A, B, C}. Solution: a temporary membership list keeps track of the unofficial membership. This is one hasn't yet been made official by means of a view change, but which keeps joining members which are not yet contained in the official membership. [bela Nov 22 2003]: implemented the same mechanism for LEAVE requests (using a 'leaving' list in pbcast.GMS) Last Message dropped in NAKACK ------------------------------ When a negative acknowledgment scheme (NACK or NAK) is used, senders send monotonically increasing sequence numbers (seqnos) and receivers expect them in the same sequence. If a gap is detected at a receiver R, R will send a retransmit request to the sender of that message. However, there is a problem: if a receiver R does not receive the last message M sent by P, and P does not send more messages, then R will not know that P sent M and therefore not request retransmission. This will be the case until P sends another message M'. At this point, R will request retransmission of M from P and only deliver M' after M has been received. Since this may never be the case, or take a long time, the following solution has been adopted: the STABLE layer includes an array of the highest seqnos received for each member. When a gossip has been received from each member, the stability vector will be sent by the STABLE layer up the stack to the NAKACK layer. The NAKACK protocol will then do its garbage collection based on the stability vector received. In addition, it will also check whether it has a copy of the highest messages for each sender, as indicated in the stability vector. If it doesn't, it will request retransmission of the missing message(s). A retransmission would only occur if (a) a message was not received and (b) it was the last message. MERGE2 protocol --------------- This is a description of the new merge protocol, consisting of the MERGE2 protocol and the pbcast.GMS and pbcast.Coord/Part/Client modules. The MERGE2 protocol periodically fetches the initial membership (using the FIND_INITIAL_MBRS event, handled e.g. by the PING protocol). When it discovers that there is more than 1 coordinator, it sends a MERGE event up the stack with the list of coordinators as argument. For details see the javadoc for MERGE2. Changes to the group membership protocol were made in the pbcast directory. Those modifications will be backported to the main protocol directory. The following discussion refers to the changes in pbcast. Overview: First a merge leader is determined. This is done by lexical sorting of the coordinators' addresses and taking the first one. Because this is deterministic, and the outcome the same at all coordinators, we don't need an additional round of messaging for leader election. The leader then sends MERGE_REQ to all coordinators. Each coordinator returns its view and digest. The leader merges them into one view/digest. This data is subsequently sent to each coordinator, who in turn rebroadcasts it to the members of its subgroup. Each member (including the coordinator) will then install the (same) new digest and view, thus agreeing on the membership and forming one single group. On view installation, each coordinator checks whether it is still coordinator (of the new group), and becomes participant if not so. It is possible that, during a merge, members send messages (e.g. after the digest has been returned). The digest would obviously contain lower sequence numbers than actually send. For example, member P's digest entry shows P's highest seqno to be 14, but it actually send 15 and 16 after the digest was returned. In this case, there may be retransmissions of messages 15 and 16 from P. The following section describes the implementation in pseudo code. When GMS receives a MERGE event, it calls impl.Merge(evt.GetArg()). Only the coordinators react. CoordGmsImpl.Merge(): - Returns if merge already in progress - Else: set merging to true, create merge id - Determine merge leader - If leader: start MergeTask (needs to be done asynchronously to avoid blocking) MergeTask: - Send MERGE_REQ (with merge id) to other coords and wait for all responses (including self) - Remove the rejected or missing MERGE_RSPS from list of coordinators - If resulting list is empty (containing only self): abort thread, set merging to false - Else: - Merge all digests. Should be mutually exclusive (different subgroups). If not, take maximum of all digests - Establish new view (max of all ViewIds + 1) - Establish new membership (merge all members and sort) - New coordinator is first member of new membership - Send INSTALL_MERGE_VIEW to all coordinators - Terminate MergeTask Reception of MERGE_REQ (HandleMerge(): - If merging is false: return MERGE_RSP with rejected=true - Else: - Set merging to true - Set merge id - Set timer. If timer goes off, cancel merge (to prevent hangs if merge leader dies) - Get digest and view - Return MERGE_RSP with digest/view to sender Reception of INSTALL_MERGE_VIEW: - If merging == true and merge ids are the same: - Cast view (MergeView) to all members of subgroup (including view and digest) Reception of MergeView: - If digest: merge digest with current digest - Install view - If not coord anymore: become participant - Send VIEW_CHANGE (with MergeView as parameter) up the stack - Set merging to false pbcast.STABLE protocol ---------------------- - Every member maintains a digest: the highest seqno *delivered* for each member, e.g. A:22, B:2, C:19. This means the highest delivered messages were 22 for A, 2 for B and 19 for C. Note that the highest delivered message is always <= the highest *see* (or *received*) message - Periodically (or after having received a certain number of bytes), every member multicasts its digest - When another member receives the digest, it updates its own digest such that the minimum of its own seqnos per member and the seqnos from the other digest becomes the new seqnos - Every member also maintains a heard_from list, which is initialized to all members - When a digest is received, we update our own digest, then remove the sender from the heard_from list - When the heard_from list is empty, because we have heard from all the members, we send our digest as a *stability* message. This is done in a randomly staggered fashion so that we can avoid everyone sending their stability digest at the same time. When a member receives a stability digest, it cancels the sending of its own stability digest - When a member receives a STABILITY message, it can be sure that everyone agreed on it, and therefore removes all seqnos <= the ones in the stability digest - One problem is that everyone needs to agree, we cannot have unilateral action. The result could be that someone removes messages from its store that have not yet been seen by everyone, therefore it cannot serve those messages when a retransmission is requested One way in which this can happen is as follows: - 2 members: A,B - A receives a stable message from B, and removes B from its heard_from list - A sets its own digest, but this time it has 3 members ! (A,B,C) - A removes itself from the heard_from list, and since that list is now empty, it sends a stability message {A,B,C}. However, B has *not* agreed on C's highest delivered seqnos, so this is unilateral action and may cause the above problem. Solution: when any member receives a digest that doesn't have the exact same members, it does the following: - Reset the heard_from list - This will ensure that (a) no stable message is sent and (b) no stability message is sent libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-join.png0000644000175000017500000003354611366547366023153 0ustar twernertwerner‰PNG  IHDR†<"7"ý pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v IDATxœíÝɶ£:¢@N­úÿ_öø]Š«Î45D råÁ°`# ñ÷ù|hç?­7€·IhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆþÛzFò÷÷×zöù|ZoÃS¯PiKù½â åW$=æ ûĉ¨EŸà’<£(€Ð­ŸÅSùý©«$­á¾ ¹¢ì+ß“èSo§B·~Oåw8jI[Z L¹ mÅ|ÁâR—œ/^(Ùêw±.Q‰Vá?Ûå•ß@o]-i/~vˆ |>Ÿàú/xFmyJnÕð*Ÿw¾±ðç¢ B Êï!]åiµ¤UÜ•-ª«î#Ð\ܵô¨£eY„~¼°üvøÔ’6óݶ•4É[€÷”“¿¿¿ûֺ䡯ð Šd)¸µ.Ê \ð³x*¿Ãé.#÷¬â%ÅÏŽ,ÛmúUöþírèÓáÕ#º£ ®r}IÀ…4(ƒw÷KSiîŽðçOÊï3 ¬¢ÇmêVŸ?á|uTaG:ÍWGsvÂÓ^I5ܼEŸytIhN$x…n«H‘€æDR€ùõ\Eºˆ¤Óë<.")͉¤3뿊tIhN$˜V•ç?@$˜Yÿ­ö‹H 0«!z‘~‰¤4&’Lh *ÒE$ 9‘`6cU‘.")͉¤S®ŠtIhî¿­7à¥ÖG) wPZÒ¾yôóù|>ŸQó aÄVûE$meÝW¤R‘ôiƒ^»ý7fˆ¤í©(^ÎíMÇTÉŽÉ…H¥°‡’ãR~)µvwP¹êô+ÕìãVÑÃΕÁí»”bèÄè…qèíWKÚ…oÛý¸»pÅ›Éñ‚‰Éªµm _ ¨ãÓ—ài¯  ±ï÷äÑÏ?¶qs˜\0:‘ô9úÐ9nsü ÂzîkñuAlôk]‘ôQ…}åôvèý&p´ ÊRUH¥ÜÁ)µ!‘àiN{UH¥0‘  Ö9¾4XÆoµ_DÒÇÜ·¯8C[‡Ê [äòŸGÎÑÏÁôF mÈ Pyó@0ðÊø*9~Ó6•nÊEUµË0‘ o».Í}Ò8¤ž[0 ÷›ãRV$}Âû p‘C@ŽHÚ—¢œá -e¦¡87$’Иۛ:ò¶;œ ŠZ£.(z ‰¤Ãsö‚¶”Aøéîºj•AŹ!‘ôvöo`q( {·¦F;ÿ}‚cKü;nÇ÷ÝN òó¥¥xÇËõŸX$íŽf;’¤F~ŠSÄ÷ÏBºØNO¾wý¼üЉE$è‹ëÒ¡IŒ%8Úl?…—’ï½H$àEFéÔW”›éO‹kLk-yIïö@U‡Ã´µ§ ªõž”«B÷/ç ‘[óæéŸÙQ¸øI$½‘ó|5OtN$í”;œx’‘;ä ¼ŠH:° T¦j¸Þþ=c½rÒ¦Óxlˆ¨[×"’¾ÎÝCÝK'ÀÝ mñ“‰Çó¯N$­ìùýòÜU—B-ÊLãhqžÌÄ"i}£ìšªK€Nˆ¤áå€é‰¤oçT47两¹g°–Ÿ$›¼3~c.¢Ò"”Q¡® ”Ã&œs¢8wx‘4ŽPÛ±asOÊÊ=‚6íöç#k»Òó¶Á Q6aCû­ý¼®W4Ü;Íö‚¾ðRr†C+ˆæ{`ë±Ã&À×µ¤_åö¦ÓâKÿZKîœæ{˜žÃ&œVx°gÜÅ¥Ðéå&œÄ®%½{0°¸£UC…ZÌN¶^ë™2X¥)ãU‡M8¡°÷eg:~í¼:t:lbàZÒÕ}Wëxòv¬Uò21ž¼š¶]2tî%Ç·º•©@‡®Ö’åNå‡Ä«È¥Õä–Pѹ/¹Ü£ôÜ6\\Na§º¸dà& &L¬r_ÒmïÏgra²Á÷U‡­&vç—4nß¾m;»²WÞ¾¼ooá w¡B[üdÖÞj¸ÕJS·*DÒ ;ù‰~ñÉ*·ýYá=åæŽþ¦Û‹“\Ëø?\r'ÌýÆõäõ?À­®¶q'ÛÍã?ËM¢q4)·ËÇ›ñ‰îÝŽW7ëQ»nG…òÒv~É…?÷Ϲr»DüÒÏÞÉ;7¶t‚i(Î Uˆ¤É’xLů½·_Ð-Gô,w…Œ«·óà«\º½)wSجVVàåDÞΉ„A½!†.ž¼†ZÒWs†c,/‰¡+OÈÞC$}© áú4_ÇУ’måóQI6¼5*GéÙpU¡ LCqnH-é„ Õ**GéÓp1€ºDÒ”7ìéy¶DÒ#¹8ÙÓ'WG䈤“nËu¾§b({èÆ;õ£ô@ à(µ¤3ÈÝÏ$¡ò {©%Þ¡ç\/╨  "µ¤/²}€µ Á bh’ aŠsC"éë¬Áôg©ûùÃ䣓ÁwÛµ èf°óá4¹0¿}g7†ä6ä>TY0âAyÎËì„yž!’¾Ôš¢ !ã;Oÿâ‰É9·)m}i]ãvæä¹'}Ó¿ÿOÄí‡ßµóC%’üPÉïêïïoˆëoU¡×>ŸÏöÿÁK;7f;güöÜ“'؆܇*Û~¨Ÿsî\&/7R%I'j:;¯mX|3ß?“_Wc3ùRJ·µyßínÚí†Áx!LC-OCFÒ­Š§Šmr­²@`Ž ×Íß—t½â¹xéóM·åö}N˜°½)î0ÿ¼ø™;»úÒ4@N© ùꇧü@[Ê LCqnhþ†{:'’ИH @c")‰¤—¸à:‘€ÆDRI.ñD7€ëDRIhL$ 1‘€ÆDR€KŒK pH @c")‰¤——à:‘€ÆDRIhL$ 1‘àã’\÷ßÖÜè{3øš™Ö?÷ß$¾ù»œä2ãw6 XTî½ß‚?s«ˆß»g!I¹­-L‘J.RK ïò Oë¿_Ûÿ3ÿ.QüŠ¿úõ†ñÛ·KؾgÇ䜹·ç’\i¼ukãPžû€œ&’ÂÌΦr hPé˜|{¹&raO«2êg¹&5¹Ò õK0)@]")°,Ëò·ñò³Î² Ú¾w¶t'«Tª²$© .‘&·†§ríf®åý"ƒæìCë×r4ÌU¯(­B*¨H$þ%þÿDËuåܳ¨m•íiUR^þ} x‘æ÷€ûÛ¯ƒ˜• ©{Ö[Xly‚Û­rïÍõv=´ä–ogÎ}{îm¨åÀYŠ>Š¼Ö•Ê­öûrÊÝc”*þóD•dr™…µæINY”A˜ˆâܯ~xÊTá”Ú†{I.q‡Àu")‰¤4&’ИH @c")À%†Œ¸N$ 1‘€ÆDR€KŒK pH @c")‰¤4&’ИH p‰qI®IhL$ 1‘àã’\'’ИH @c"é-4ä·r&#’ÞÅ ¸•ƒ 0‘àã’\'’֧긕ƒ 0‘€ÆDÒÊþþþ´âÁ«<\gé LI$ 1‘´>uÀ­d€ùˆ¤•­ç ' à2À”DRI.Q[ pH @c"ien;nå LI$¸Ä³”®ûoë  gDhëùÑòŸ\ÀDÒšZ5¨iцž,øZíáVM®÷”ë/ ÷-iúXDÒŠ\å·r&&’Ï) ÚRaÏgõ£+‘´»p+`n"i5jJ€[9ÈÀdôÆÙI+h»K©;¶(ƒÎ[ð §Ô†DR€6\p®DÒ«ìLÀ­d`VkÑVÆ‘€æDÒKÔ^·r^¢—Šn;¿ß)ŸÏg}i=.çzoß¼%¹®àÕàåµô£·í·QaŠsC]Ç•ë”5ž/oÙΟû\Û—~þ?7Cn @E207e|k˜†ûBµå~Áo¿.37=÷F*&’.™Tú·Q15n+G“Kî'¤D Úº© ös÷pJmh¤Hº¤Réç÷­q‰öQ§ àV20=Å<0X$ýJ^ÄTiÙOJ¦Rjé+’ncßÑÞ ‘q{‹}°ŠïÒ~ÆÙm*uYÜÊA¦§˜Çzjù'æFƒÚþxÛ9 óÖ²?ž¾üûΧíÛÕ•T'¤GP‘ƒ LO1Oê«á~ö!àV20=Å<&’ çp+˜žbž#’O÷VhK„i(Î ‰¤{¹¬nå ÓSÌ DRIwqYÜÊA¦§˜—‰¤4&°ÿ沸•ƒ LO1ÿI-)‰¤7REº‡HúCÿ»‘AÔ ­‹e°ÿƒ ¼Ç§Te|'‘€ÆþÛzº6Ü•Mpy7ÖÆÃ wQÆ÷Ig°&Ñ`¿—P€!ˆ¤3ÈeÍdBLànö8D}rÖ¬•í‚)tbÖƒ 𥌢–ôu¾Åãz9Éõ H^®+MþLù|>;»"$7š“GI_j}§ Ìw Û$úý0}ù'G®·q3˜s»UqúÜnprÀ  •6ÐÅÍé4öù|’ɯºà›<´ÒsŽòc1¥s…h ƒ ¼G­s¢~‚ZR*ˆË^Ü(¿­ÝYP·áõ»ŠíbãUƒIß.n=?$wA™ìZz®knþrïUhBé9îÞ¶3]i¾_«<fÓs«Û“b_õc1‡·d~I© œJ×Û› ¯Æÿ?DE)=pÁyš/.áûÓ‰öôïÊyt'×õ39˜T¼ðŸu«/üÉ×;2ðJ÷i¾¸„×îO¯ýàð0e f¥tŸ¦á>dgnå ³Rº¯pÇýðh«b<ôH³ò=…?GIÛóŒ´B‡‡¦ä”ÚH ЋC4[gŽf¶>#wrM>z-þ¼INØ£t\¤á`Už¸–K½¹é¹7T¤–”ÿ¹8l>ð€d9½éafqi­%Ô’ &Ùˆ¿ÿQ½çÖ¸òTè\'’þˈ»Ôp “©ò€Ü’±JË~’TÊ8¥6$’ôeÏ#Í ÷-ý\l[×”¼óR)p‡ñ*o5b-i]¾¸Õž"¶ç‘fÉ9“óÇ/ï5\äÔY…/ñ_FÜ«ê¶÷ñ¡¹CG)ƒž+¡ÊuîhÌ P'åªH=Œí(‘ 2c;JÃ=ÀÓ<Œ-è9 ¯IDAT- –txîm‚¶˜ÆÃÅÙÃØ¶Ô’œq½6ÇÃØV")Ë¢ŠÚñ0¶E$@õž(À!½ÖÓªgcÛO_R€ú¾¹07Ô¶>h;gaþÂZ¶óÇÓ—ßù´}{?ÕR"éÛõ³/Àd’gØý¾Tž!˜Þùé^Ãý«É£@Ô’¾T·c@ -h:ÉuWOÎÏÿóq IÉ¥)omòOªuêRK:¼åa}ÄÛóZÉou¼´Î÷Ч Üγþ¯"ž˜Ü€xþäŸø–`ŠsC"é„þþ‘{I‘»Õgóȸø¥eÇC5Ž®îô{¯¯ªÐp1±•|ozm½û{¾þ›æ¶V<}Àˆ€»‰¤ÿ2bÑ÷ôVùÌÉï1ÉJУötâÌu?=$Weo e‡~ñ2ð®I'd%ª¡Oê™Å‡Þ~Ó̹%8М¾¤³YïŒQó°ý_x\«]=®ËÏUŸÛ=èŠZÒ9©ôj"Ùæ?¥cÉ{ç§~Þ­ã[ÝM~­Eqˆ¤Ð‘þ{ì'¿°ŸH:<§ç÷®WÀ~ýç×B¥fÏ_,pˆâÜH 󘩒µ I~ÝŽö2Ê0‘ÞbâJÖ‚;ò«l PH:¼Š7I¸åâ冾õªŠ=Ÿbû=|Ë‹RÓPœI]^Ò+ GÍ(sQZ‘H T0}¯€þ·`h"išë¨ë啬1€-‘hoúJVÊDÒáU<«³¡[=W²*50 Ź!‘žJV€Ñ‰¤üÜhœ<µ¯gôø®ä`þä¹×vÎíöä6 ü'ïa|+€Î‰¤Ã»µµ}]x2 /m·dýpGrkãûœIú’~ÂWÞÛjOÚ®úÜ–$ߢ`Àcn=€¼ùàv+·^‘ÔÛŽú*Í"é·ÀÇÛ«êíü¹›Çs¯þ´è?ŸO®9¬°å…-Y»móÈ"x · nzpT‡×]ÃiIþxÁ ñÌAïÉsÛ/¹|6)ž²6œÅ!hP;·Í@çÜÎmó@*ö f&~Ù‹fh¸¿¸T¼²IV¸lÎqp”JV8a†H𔫄Xuï%Š+9¶u Ü@-n£SÉ I-{¾ÿS芔ëPOOà\ãWnáÛMJþùóÁJ oÜ~ödo3`Pn‹ƒÛT²2«®/9„ÅÇw`n/)ìnM¨dePÓ6Üç ÷/äàö°+=YVê÷Ê5hZ"Gõ´u´ :¸Ñ-½œRê·–Ô>LÉÁn¹õІú¤@WŒoÅ}DR •¬\!’Opë-»ñžèD¬ß1Ð?7¨®b¯€õqW·‰ª:­%ut¦äàçTì|fÍuIö;Ý+@6íD‘4ù°»o]B¼£Øu*] ­BtpƒNÄÅ*RRê"’ÆUèë!;Þ3 Ï}芃tH_Ò>uIGa`RnÝJÖa uØÁüÉ|³ÿŽœíœÛíÉm@ùOÊNÜ}¼wOeêþ¾­å_ÿÊO_7|ßåÿsÓr÷ûûû»þÓôÆÁ­gߟ&øuÖ)ÁKëÌq‹xz°Àí<ëŸñ*â‰É ˆçOþIEÛ_¸ç«NþúKôÓ/Å_ÿâO_q—X·ä¾CV³Hºö©Jþ¹lº[­‡õužxf€N8¸eýþã“w²>ìâ)ùbD¸5p·+¿~ÛŸ~[ îÛ’f ÷å&y™èú/É×m%+<~ÎìàÖÖ6‰ÞýýÇͲGå¶V<­.ø¶W‰q½õýñï{ñ×?ýÓÇ3[².6èìoÀÑm>­—¾¤ð¤d%èQ{:qnaO¯(Weo }27#æfˆg¾þëŸøéƒMЧlÛg‚°ýóáª}_RhåzCüþzÖëNîÇÕOŸä–ô°_‰¤Ãëa7‚7SG´ÿW ¢ÀÅZÕòÆ+JþŸ[=üUÿüÅ+nO²Q~{æûö!†„^ó}^NQ®‡_®!>ž¾§`ržÂ½ù¹Ž‰ñr‚íÙÓ€=‚â¼§ŸåÏ})9gü–ä¯ô§O¶¹¶3Σ¹äšìJ[—#éðœ¡-e¯¶·†Pʼnâü’#À•©no€ ÞKx¹[;6èK pÆ{Æþ¤ñP×+j›è™ZRIhL$Þº°@Ï”A˜†âÜH v"¤Êµçˆ¤‡‰žu‰¤4f¨|€’Oòü>ʨð¼YCmî'’Ïîm)ƒ0BqŽŸ)¿æÑø]…‡Ú“£áà7óV")@Éßß_²64ží™í™’ŠoO!î£ Â4‚✋˜Ûvù /iüLJô%H(§ÉõÕílèiîhL$ 1‘€ÆôÊ 1µ¤4&’ИH:<óB[Ê LCqnH$ 1‘€ÆDRIh̸¤4¦–€ÆDRI‡g5hK„i(Î ‰¤4&’ИH @c")—€ÆÔ’ИH @c"éð ¢m)ƒ0 Ź!‘€ÆDRIhL$ 1ã’ИZRIhL$žAÔ -e¦¡87$’ИH @c")‰¤4f\RSK @c")‰¤Ã3ˆ´¥ Â4ç†DRIhL$ 1‘€ÆŒK @cjIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhìÿÔV@G§çù–IEND®B`‚‰PNG  IHDR†<"7"ý pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v ¹IDATxœíÖ1 À0À¿çMF={gæ@çÕüÎ’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤ÄÔûu ÌþVIEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/design/UNIFORM.txt0000644000175000017500000001173511366547366022243 0ustar twernertwerner Uniform message delivery ======================== Author: Bela Ban Version: $Id: UNIFORM.txt,v 1.2 2006/04/25 03:00:31 belaban Exp $ Definition ---------- Dynamic uniformity is the property that a message M is delivered to the group members only after all members acknowledge M's reception ([1] ch 13.9). This ensures that either all operational members or none receive M. The latter could occur if M's sender crashed right after sending M, but before *any* of the members received M. If at least one member P received M, and P doesn't crash, then M will be delivered to all members. This is different from two-phase commit (2PC) protocols in the database world, where even non-operational (= crashed) members must eventually deliver M, by logging the PREPARE and COMMIT actions, and processing the log when restarting after a crash. 2PC is not necessary in the group communication world, because the state of a new member can be initialized from an existing member by means of state transfer. Design ------ There is an additional UNIFORM protocol, which needs to sit somewhere on top of NAKACK (note that uniformity doesn't apply to unicast messages, only to multicast messages). UNIFORM also needs reliable unicast messages, so it must sit on top of UNICAST, too. init(): - preparing-table (sender), committing-table (sender): map> where M is and List is a list of members from which we expect ACKs for M - prepared-table (receiver): map where M is and Entry contains (1) the real message received as part of PREPARE and (2) the list of members to which this PREPARE was sent - committed-table (receiver): list where M is On send(M): - Add PREPARE header to M - Put message M in preparing-table (key is M (dest + seqno), value is list of members from which we expect ACKs (this is the membership view at the time M was sent) - Pass message down On reception of PREPARE(M) from P: - Add M to prepared-table if M is not yet in committed-table - Send PREPARE-ACK(M) to P On reception of PREPARE-ACK(M) from P: - Remove P from M's list in preparing-table - If M's list is empty: - Remove M from preparing-table - Add M to committing-table - Send COMMIT(M) message to group On reception of COMMIT(M) from P: - Remove M from prepared-table (if present) - Add M to committed-table (if not yet present) - Pass M up the stack (delivering it) - Send COMMIT-ACK(M) to P On reception of COMMIT-ACK(M) from P: - Remove P from M's list in committing-table - If M's list is empty: - Remove M from committing-table - Send ACK(M) to group On reception of ACK(M): - Remove M from committed-table - On reception of COLLECT(P) from R: - Get all messages from P in prepared-table and committed-table which are not yet in list L sent as part of the COLLECT message and add them to L - Send a COLLECT-ACK to R containing the updated L On reception of VIEW(V): - We need to agree on all pending PREPARE, COMMIT and ACK messages for a sender P who is not in V anymore. (This is similar to the LEAVE/STOP use case below) - Some member R drives this flush protocol, R might be the coordinator, or a deterministically chosen member (e.g. the one right next to P) - Get a list L of all messages from P in prepared-table and committed-table - Send a COLLECT(P) message to the group, the message contains L - Wait for all COLLECT-ACK responses: - Merge response into 2 lists: L1 and L2 - Add all elements of L1 into preparing-table, for each element of L1 send a PREPARE message - Add all elements of L2 into committing-table, for each element of L2 send a COMMIT message - The rest is driven by the regular processing of the sender On reception of SUSPECT(P): -Ignored On LEAVE/STOP: - Flush preparing-table: wait for all PREPARE-ACKs but don't send new PREPARE messages - Flush committing-table: wait for all COMMIT-ACKs but don't send new COMMIT messages - Flush acking-table: wait until all ACKs have been received, don't add new elements Misc ---- - Provide JMX stats of all tables, so we can introspect them at runtime, e.g. to ensure that they don't grow infinitely - The PREPARE, COMMIT and ACK messages contain also a member list at which the message is targeted - The VIEW processing needs to be handled in a separate thread. When a view V2 arrives while V1 is still handled, and if V2 contains fewer members than V1, then the wait for COLLECT-ACK might wait forever. Therefore, all members from V1 not in V2 will be removed from the processing of V1, so the wait for COLLECT-ACK can terminate - The fact that processing of a message M from P is continued by another member R when P crashes, will lead to eventual delivery of M as long as at least 1 member has received M and that member remains operational for M to be committed across the group References ---------- [1] Birman, Kenneth P.: Building Secure and Reliable Network Applications. Manning, Greenwich CT, 1996libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-leave.png0000644000175000017500000003133111366547366023276 0ustar twernertwerner‰PNG  IHDRN<I_l¨ pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v IDATxœíÝÛ¶«*ºÐŒÕêý_9ûÂ*·Kñ,üô~1Û‰Qs?ñïûý~ˆèŸ·w€»ˆza‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰za‰zaýçíèÉßßßۻбï÷ûö.Ð=eð ew)¿gœ)¿¢Þ>êÊc”p®¢ £ Òå÷˜“åW.@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@X¢@Xÿy{øüýý}¿ßúóÿ§%¾ä¦=0–ÅjRúñ+€Ð¬´xþ\`Àò+ê½,=Þ¤ Ì¿žŸ }¿ßUÕœ­©@hÖÏâ©üNtà6¡ô\{¦ƒÓS;£S¡Y?‹§ò;Óª÷¦ù‡Xÿ.¥'(«5¤¿æì#é`@ÙÞŸ“ð“”heð³vØò«U¯?¬|¿ßÕùÊüçôŸú#¥MÃP¾ÿ³zpã +~”Axƒò›Òª÷² "»VÕòRx^:to¯½eY„vÄ.¿Zõ^3ýP– ÙK·üþþþþüìæ5·FwË–‚[ àG„~OåwÖS,}Ý…)þç@Õ…óÿ¯Xº\ÏÃú:¢Yw”ÁYi¬ÞÞ¸±®ÊàÝã~”A^wÇðç\*ÊïGÔÛE]y˜ŽKø!æ£ãu~„‡üètà„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„%ê„õŸ·w`PÓ¾ßï»{¦UïSÎû~¿ßïwÎ|—Óª÷޹1oJ{Úö€-Ïuçj0{¼ª$WÕæ|þ¼ús˪ ¸Jl[:C¦—ÿ\Œ@îûtãTdO³-‚ÇV±iÕÛç’L–]‰´[()ƒ(%¹¹•nû/áX(äÊï+D½Î×¥ÞÛ3½ºz„G›?õcepù*¥8Uº7?•Ž{Îöÿ\èâG²0êÀm‚>\™ "=îþýýû¸âÂa Qài![.Qß2}n³OÒ9ûóƒ©Üç¨b€’1ç]šjÅì¶i…9?¾}¬^iU0”áj–m9Ýôu@x«Ià–«€ËéÀxšHx—69àu¢Þ-ž¬ßWr\‡)tFÈâüÏÛ;Àw_‘ &ê5-äé°ÝteÆÛ{tLÔëžÃ¼K„0BgQà®ÉZ ê„å Üë½x*¿jyÖ¢ƒõZ·eºüÕ­2ÒdzÏ]pÏ à Q/‚Ò1 ›ü0àZÊ„²8‹zÝÛþ»\Þ7ä¯XqYÆp¾ß¯™ºànº\Fˆz{>BÛâ”ö>8O9‚0BgQïz½œÊkÞ€ðD½d08•p˜¨7:‡¬Ë+pÓh²¼¶tùHöU•§>ÕèÓKÏì^fí‚k5X T›0¬.£^M¦?+‘eùxöµóÿÓõ7Xk/µ¼o0‚.Ê j¶ù»¢wUé,O@+Oeص¡ŽèÆ–«6»uÙª7©÷;–žª^µæÆéÆ…ðT›0 Ž[õ¾ÿsßú?ÍL±SiukdaXÏ”ÁKšÞ‡ª6ဿގ[õf÷5GÍó ;I­†i§ËÓúÊÂ¥W²ãJË©6a·êÍ*Y!›–uÐÏe'©Kó8îù‘éÿËWã²K ÏŸüêÁùÏÕ¦Wë_.ìÛ¡)]¤Õ&Œ£ËV½©)µ'eë—eÝ´ª…WÕÖü§Æ»šö¶¬Ž(õÎ_½o*T›0¬.£^©îØ’ ¶YÉöiM~~×Ù¨4ýn™Ûåç^QòdU IØN}qÖóuniF´ÙjÎÛ§?¿hÞ2UÏŸõùÀåB–,—eD°êØ-5ÞÐ5ß#“å¡(äa ¸–¨Ī‘OíO¹€f¹,£?õ#Ê4ð¿t±'л¹_¾¶ÐªwJ³Um›{¥Ò ÀÒ†>9'£¡æ=â¹;D–* w!‹óío)ä§–ªO¶ò̦_y9 eËÉЯŹéÜî¼2¯aŽX¥Wñ0Bç{ñ[æ ¡Ò4+;àäg­ŸynN³qTLºÂÕš/Q]Ùб©¯Ô{ŒàŸ[×þýŸ[·ÕGVé–JuZæ»0?žm•\-³ÚzºpöÏì>\bÞ“½'ïÎx-¸7êñ€K*²(0é¥ìœ ^ïVéä¦oí á…üu«Ç´­™çxÿ¸#Ñfé —³â’Oòð† ¢@£~ž€}Ý‘¨¼Ã˵•>Æ3‚^¼õvÍÀ·kú½ŸSÓ}œº¶÷C.Í2¸Zrã—õ³FþùËIHO÷èÏÉó…­þÙÖ»k»D7Xº}¬^)Ø-G /ÁËn‚Õxät±ì«m¥«å°½òÏoÈ~~YÙo?ûªôÁ4€®ž*ý Q- `öÿf¦Ò”„×NJº\áö=ØÞïÀ-ò´¿ T>Óä‘M$é’1¼ò¦v}È[jðÃ_ÖÉ·ÿ5B‘Óîûý¤CÍ–!ly¢›îI:Îoµ†Õ[˜BÒò‘ìÊW²éŸéY\ºþô¬¬´“[Šg¶8Ý«ÒÙ¾!FògpÙ}]'ßÂÛzŒû,Šâ²¬¦uÍ'—ùV‹‹L6žoùK'ëõ“ø½gä»V•þ¨²¿±åš¡qƒÔo×6þÁPζêUŠ_}0ì.é&J)0»'\èØ‡\±wlN®§ò£:¹fà& &pñX½åèºgòV¶ão¨êà•7»ñC^u -_¾ìC9³g^þï×Â^ü Uúdƒ™GÐ:“çV!KÓQo5 öÀxÞlÑöcð8•Ýëö~§ËÐ_ê!½ã‹ËþKÿ‡~=y^ têl_g¶ÿ4ý³Þ5–òëý³én|“k9ÓÍE­ ¯í°®¯mã‡\ùsû’Ûw ô“HŸú9úsãnÀ’A#FÈâ|AÔË^HÁc.üØ[û]*AËJg>@¿Z;^âÔe¥‹dáñÊ]ÛÒH КSéÕIm0!ÏfàŒk¹›É|Ê#.Ò%vœjÕS¤xúŠwéÚ7™£t¾Lª4ÉåÏÅ– W¦¸ÒÀ zÿn¯ë+ÞíRš sõHeÒƒR”ÌΜpÍN×õºwU¯«Þ[†RxwlU·–Êã«ö¶ó›¨4ÝÍ›3½ …<ŠzÀ(zlº[f¯l³Ü%G¦tõÁ|½|zÀGÔc¢î&ªã]ÅóEµ4¿}€!ê!çÊ…=³XMD_i竬áLb»°ùxž¨t/XÓ]ÖòòØRø›ÿü:^?ÿŽÙû Un5™½‘ÌUã›(œ£SAÓ©âÝlj€Ó´ê Í‘ƒ¾ ïfîHœ'ê jÕmŠ7ðn¯lŸ)Àvuºw eNc-ë®éN‚0Bg­zUš4æÑ¦îâ@/D½ê\¢4HÏ,À3D½V3&8ˆÒ&gÏõ‚X]¦ç8J#Ä;€w~82íy´@¼h‡V½J×aH~<Ã/  YZõº·ë>˜‡a.¢é  Zõ²¼Á¥c3ˆwY!'â‚1…,΢ÞpæÀ÷ó×üóFLÙ[6e岋yÕݼñf¥‘¾|cwvvJoªnut}Éël„žY€Þ‰zƒšÓIåà=-³ÊUéƒÙ%—ég~jÞâráì WJwÍÞ >¼–o6}ÕÆ7•]IöMe?«¿¿¿.Î5ÝD"êk{óÞê%g¶xþ6nóJJ—^x^vm¥M¤øÀ#ÞD%êñÃù°lA<Ð=š]áö~ l¶µëí÷˜úÚ[ŽõºwwKÛ…álÕo;?{fÍu¥·¶q€àgçۿ䳺w7ñIB!‹³¨ÇÖ~ÕóàXîÉÍe·x`v½¤ÊBÓÀàþy{èÞ²Ÿ4}ðsº‰«Ô÷šÞù÷Â(“Ýâ®7RZÃyëoá»p÷vhP‹#ÄÙåªô®gû$é”%¥—Ïyï÷»üÿê©;³\2}yi…Ù·³Ú‡Ò›ª[¾©ŸKn\'7é©Q‡¬-s7æ½Øz·úd¦?³WúàüHå?¥×VV¾ñÿŸ\"L·^Ú¥ÒÛOWRSõ½Šªñl²8kÕ ¨ÒnÔæÑ·Ùw'÷¤ëV/™WRz¼ôÂó²k+m"}¼’)xŒ¨A=*µ9ĭͽú\±csÿæ÷û=Ð=š]áö~ l¶µëí7û%0õ"X×›m$ë"\Ζ ZÙñv—+5¡m øÙùö/ù¬¸•¨D›¤š [œßÛc¸'7—Ýâ}Øõ’¾¾Y€¡üóöpÖê(;uN‡üWöçoáûo¯ìÏå–ý¤éƒŸÓM\¥¾×´íöÂ4»Å]o¤´†0ß{Åï²8Qs·.zfÚ>I:eIé%ÏO¶R¨wl^•úF·¼€‰z#Þ½+ L>I®%êuï±>²®ÞÁ}駆„,Îa/Ë(õš­ÉöOe3M½Ç*½*"Fƒ¦;èZ̨·œÐz¤4í–…çË J·Xnz9§Új%½œ+ˆwF̨7Û5ÛÅÞ©1–S¦ý|aËwÐ3 QÅŒzÙÛ\²p©ÅëáÔÎÓt#ˆõ>;[Ѳ g'ŨL6ñVÚ+Ý!}\¼ƒ;(MFÈâ6êMÎtànéM½~‡¨ÕÝ2Ä;Y̨W¹hš7Å¥/,M]{rCÛ÷'ý¿x#ëixÙv?ï4°|ª4Ëêå'[ù”oWpÓG]¿/p·¾Æé!‹sÀ·4šÊ°<_.< 䱯²8ÇìÀ%Þ/8@ÔÝöQ+«ÓÈìÄ[î”®*Ý¥ÒëåJCkœîÂLÔ]v²‚ì-‚– l™p`zùÏU­”žM/­ËÞÜèç^ÁPþy{8K½ï§ nif;³ªÊš?IŒK›ôä<Î ùҪ׽í3ù…ü·ªLhNYplUébל¶ðmÜЍ׽íµÛ±éÕž0²-N­&ŸŸ'œJ{„®ª²æúNþýýiÕƒ,Q¯{»nrlý^¥Âeƒ$Œú½O®êüš¥=.ò'4\Ô ù-ÞJ@„•&ì,Ý1<Û°·wýõ5ÿ\­´YÃE=žñX@T§ÃySÑË^`›–ÊùñícõJ«Ú»æÕ¥óŸ®Ï€Š€å¡4ëÒêÿŸßÁ¬~ûÚ´~¹cϼRÓ|Èë/ƒ‡=v÷HØ.dqتWj䯌®ÏÏ´ ˆ4Eÿ2tJ‚gŒzKÆóe"¼d…Wimº r!_1„²8zŸò8â·ÖC¿Dº0µ¤òÒ?룉ëãö>ÃÜO@à£Þh´22_¡ B!‹sü\‡9nXõ`tr›æC€^ˆzÀn®Pè…¨¸„8@ÔˆL@„Á‰z¬ ˆFÀá‡tD@„[‰zôG@„D=FaŽ$êu/ä$@Ðe06͇C Yœ]–E®P¡w¢\L@¤¢4A@ä¢^ÇÊ9ˆÔ~8 ÃH¡ ã{¤UØä±Ä[Óáh§g¢p£¹êÖæÃï÷;¯„Ì'êuÏ@=‚y¬ùp•ùB6ø‰z@{SÚ2† yQÈP½·QÊ oöÏÛ;À]D½îvv7 yHõÂõÂõºg^=¸DÈCª¨–¨–¨–¨–¨×½“ÀóBRE=€°D=€°D½î…œžò*ê„%ê„%ê„%ê„%êu/ä$@ð¼‡TQ ,Q ,Q¯{!'€ç…<¤Šza‰za‰z·Ù ´C%lôŸ·w ¬¿¿¿×l7h:æÍŸöüçöcárái=Ùu¦¯ªìÀjU¥×~’ŸÊrùìdW••d•ö¶òˆßskT2ÀZõº§®OMŸÉüïdùÿÕ«?ɧš®*}v2}Ó—/×°|*ÍdÙ%K//­$»Ñt%óÞ¦a·ô Y݉z×Ó±ò°c%³Þb÷³½äg«á¼À–ätÉo¦Þò—Ýè*M®Öà—Ü,_ °¨Ç@þ¦G~¶±UdÃÐôàÆÏlà^—¬$KÚ@Ô»Øó£gŒ?‹PRÿüK=°S8[ukîÚnº•½ßËå {—ödˆÜ'd'ê1¢tÜüÿå¼4Tn˪–MŒ‡]²’úúï[9·õ®çœûS´ÚþɯâK)ümÙneµõX]&Rzmi4ᮕd÷|¹péÓócnJØN}q½‡kaaÌÎL8Rï½Ý>aJéÚˆÒ ,韚вë¬l¥²Lö‘lQõʰ _Ü!dá ø–^'êÁPD=#dáÒÛ½x?JxEÈCª¨–¨w±m¿@;T2À.¢^÷L„—yHõÂõ®¤c¸•JØKÔKÔ»Œ³màV*àQ¯{ª~¸DÈCª¨w×ìíPÉLjz— y*´C% ê]àÝ4Îõ!<£ôà!©¢@X¢ÞYζ[©d€3D=€°D½Sœm·RÉ'ýçíø¯å@È©^›ù~¿óSs}W5¹|áê%Ùm­ž]½°¾•vTvØ®µCü%Z‰zS^Y~Äs‚I£Ì¼ðê%ÓŸéãK˧*ÿOw)]§³màV*à¼n:p/i»*¥ÉÒã¥t¡•V½-²Íu?»kok¹þtÍí„?½·R;• Œ#d¹ë¦Uo’íÌÜ·ÅO§Bþ€v¨d€«tõ&Ùv¬û®NȦ=€öµõ–qjï°•(¶¼ävµ‰im?câ2í9Ûn¥’.ÔV…’vÎ~þÔJKf—OŸZ½öØd+é ßUM<ãòd&êÁ[B–¾€oéVmæª?Mèŵ°ÍJèW[¸]P·RÉõvÐxÜJ%\NÔëžKƒà!©¢ÞVζ[©d€;ôt· *Bžˆ'‰z›4~¶Ýò¾Ax—œh5^ÉýÒ –óÈßœm·RÉ÷Ѫ–¨–¨÷Cû+®½…w,ƒíW20އTQ ,“­Ôtw¶½:ékça@ÝU2@wD½æ„·:fH~08Q/‚R†Ë&?Æ¡ï (jÇŠÀˆZÉMѪ7œéÐrþSêÎ^¾4o4ûçê‘ï÷»±K:»ÀLÔÔ§'¤i Ë„7ýõøçùl~pãVK.÷*MuËÎnX1ÙJ^G+‡SÎ÷ûÍ&ªË­>É]ݸd/_!+DU20Ž Zõ¸@zÐJ;g—-vpËP8mb¹ÚtÀЍ7º´u—Ò PvèÞ±a‚¥å룀ܬÑ:VÎtãÎMt?3ß±ÍmI‡C}YÄ0Z%¼HÔãõ´7_–Qy6ýÿ.ö Ë™eƘ'ÜúU§ÿÔsÞ¼Lih]vÒ–tå?ÛüÊèט• ð ÕMưµð°o¦¬Ñ»¦ n¥’žä Üî9lÀ».,ƒ»n!S¿êçlD[îIS8¡Ú!¤‡TQ »n!3/œÞ{“ûÒ&J²k[îOiêÐ)­zÝ;p$žó¯•ýTçGVOÍ ¿ßU¨J_­p¹Ìügº‰ôÁì¤Ëgÿ¤Â§a„,΢^@ÿSz*äO¹sJK?ç¹Ál•±Î\óuòÛ<¹u§w­Ç$TïøK;ò¸Ã2áÝý9ŸÿNK{+ö= ÇJè—¨÷/=¶pL‡ånoòŲv{m$WY©MhIDATÞ·K©‰1Ýêv}ã=V20Žgb¢^«c¼_jG¦cùá¯`× ÏÑ'÷€Æ«Í<¢_ËÁöài+ìåIk^©¹×Ï`ZõbÒHóŠlßëô`åRŒU‹l½]6»ÌòÁ¥4ôgûg—³«Ìû™Ýmº£_ëîØ¶¥y¦¯wÄ,›Þ^Ù*öVÝU20ŽÅS«^÷¶ü(wõÖÅû•÷ËwÑ_„²8‹zCØÛäpÓš€‡‰z¡\2ÍŠ\aˆzݫ̦ûÀ{r!„Üc YœE½J¿Ëlò{ñG,ÀÃD½µÀ3Ê.'Ñhÿ Ê…D¸’$ê ç’›wµF.€,QoP˻ݿ½/O“ G¨¦«kñªêÍÞM.d#åxŒV=¸ŒöBZ#êÎñ·È…<@ÔëÞù &íµO.l™âa„,΢D#0õø|4ì L.ˆMÔË3÷Œù®ÙE.¼Šâ{ÂôsÄRvmËý)í@ýOO«^÷nÙ=u鮂ÑüÈê©yáô¾éã«.—™ÿL7‘>˜ÝtùìŸp WW@!‹³¨Ç&sJKsÒÜ`¶ÊXg ÌÉ4æÚF˜èÀ-müì›]&¼»?Š´sv¯ÒÞŠ}´i´Jx…¨W£ þíöÚ2H®4¼o—Rcº'Е p7¸|>’ÜùÙíí‚ç~:p`"êuï|0ªä¼íi•®î薚׿ÚPöÿð-sFÈâl˜[£^¶tõÂúd+»¶R¹V·4Ki*–ÒŸž¨7ºçG…§În"êuïLVsõœ§A!‹³+pUš$ˆDÔQȳ %êT¹ø@c EÔ‹ tEjú¬CÑ‘DeJ9_1 ë娗íç.ÕgÊ-Ýk9³Ú–I×ês°]ÛzáÚtÑB Tnw¬ 8àͨ·¬øö^(°Z¾R›¤kž™¯ìF}¯®º¸!»'ç×ùQ½ÂKTn«õ¸ ^äÆhg*‘wï—º¬íIé®*VxÆ­ÈÈ•1Gb) would likely result in OutOfMemoryException. In order to alleviate this problem a new state transfer methodology, based on a streaming state transfer, will be introduced in JGroups 2.4 (see http://jira.jboss.com/jira/browse/JGRP-89) New streaming state transfer supports both partial and full state transfer. Design ----------------------------------------- Streaming state transfer functionality will be implemented by a new protocol STREAMING_STATE_TRANSFER. Existing STATE_TRANSFER and the new STREAMING_STATE_TRANSFER can evolve easier as separate classes, development and maintenance are less error prone and tedious. On a JChannel level JGroups 2.4 release will allow: - both state transfer types using different JChannel configs (one for byte and one for streaming transfer) - state transfer type choice is static, implicit and mutually exclusive (cannot use both types in one JChannel config) - use of flush in combination with state transfer ( choice is static and configured as a parameter in either protocol) New API ----------------------------------------- In order to expose streaming state transfer application level callbacks in a push mode existing interface ExtendedMessageListener has been expanded to include additional four methods. public interface ExtendedMessageListener { /*existing methods ommitted for clarity*/ /** * Allows an application to write a state through a provided OutputStream. * An application is obligated to always close the given OutputStream reference. * * @param ostream the OutputStream * @see OutputStream#close() */ public void getState(OutputStream ostream); /** * Allows an application to write a partial state through a provided OutputStream. * An application is obligated to always close the given OutputStream reference. * * @param state_id id of the partial state requested * @param ostream the OutputStream * * @see OutputStream#close() */ public void getState(String state_id, OutputStream ostream); /** * Allows an application to read a state through a provided InputStream. * An application is obligated to always close the given InputStream reference. * * @param istream the InputStream * @see InputStream#close() */ public void setState(InputStream istream); /** * Allows an application to read a partial state through a provided InputStream. * An application is obligated to always close the given InputStream reference. * * @param state_id id of the partial state requested * @param istream the InputStream * * @see InputStream#close() */ public void setState(String state_id, InputStream istream); } Similarly to the current getState and setState methods of org.jgroups.MessageListener, application interested in streaming state transfer in a push mode would implement streaming getState method by sending/writing state through provided OutputStream reference and setState method by receiving/reading state through provided InputStream reference. For pull mode (when application uses channel.receive() to fetch events) two new event classes will be introduced: - StreamingGetStateEvent - StreamingSetStateEvent These two classes are very similar to existing GetStateEvent and SetStateEvent but will introduce a new field; StreamingGetStateEvent will have an OutputStream and StreamingSetStateEvent will have an InputStream. Thus in a a pull mode application will also be able to provide/receive streaming state. Current state transfer API -------------------------------------------- Current state transfer API that initiates state transfer consist of the following methods: public boolean getState(Address target,long timeout)throws ChannelNotConnectedException,ChannelClosedException; public boolean getState(Address target,String state_id,long timeout)throws ChannelNotConnectedException,ChannelClosedException; Introduction of new STREAMING_STATE_TRANSFER will *NOT* change this current API. Streaming state transfer implementation -------------------------------------------- Streaming state transfer can be implemented using TCP sockets. Joining member would open TCP socket to a member providing state, appropriate IO streams would be obtained and passed to each member's application level and thus state transfer could be completed. TCP layer will chunk up messages automatically and the entire state does not have to be in-memory in order to complete state transfer. Lets recap how current byte transfer works so we can compare it side-by-side to a proposed streaming state transfer. BYTE STATE TRANSFER (current implementation) MEMBER A Member C (coordinator) - Joining member A sends down GET_STATE event - A sends state request to coordinator C - C receives state requests and gets digest from NAKACK - When digest is returned from NAKACK request state from application layer - When application sends state bundle digest and state and send state response to A - A receives state response with digest - A sets digest and gives state up to application STREAMABLE STATE TRANSFER (proposed solution) MEMBER A Member C (coordinator) - Joining member A sends down GET_STATE event - A sends state request to coordinator C - digest from C's NAKACK is obtained - C responds to A with tcp server socket's host:port and digest - A receives and sets digest from C - A creates tcp socket Sa, Sa receive buffer size is set, and connected to C's server socket - A connects to C's server socket, socket Sc is returned, - Sc send buffer size is set; thread T is spawned at C - Running on T we get Sc socket's OutputStream, pass it up to channel level and invoke C's getState(OutputStream ostream) or queue StreamingGetStateEvent event (depending on push/pull mode); - When done, cleanup resources - Get Sa socket's InputStream, pass it up to channel level and invoke A's setState(InputStream istream) or queue StreamingSetStateEvent event (depending on push/pull mode); - When done, cleanup resources Threading model considerations ---------------------------------------------- Threading model used for state writing in a member providing state and state reading in a member receiving a state is tunable. For state provider we will use configurable uti.concurrent thread pool to spawn threads providing state. Thus member providing state, in a push mode, will be able to concurrently serve N state requests where N is max_threads configuration parameter of the thread pool. If there are no further state transfer requests pool threads will be automatically reaped after configurable "pool_thread_keep_alive" timeout expires. For a channel operating in the push mode state reader channel can read state by piggybacking on jgroups protocol stack thread or optionally use a separate thread. State reader should use a separate thread if state reading is expensive (eg. large state, serialization) thus potentially affecting liveness of jgroups protocol thread. Since most state transfers are very short (<2-3 sec) by default we do not use a separate thread. Channel configuration should set parameter "use_reading_thread" to true to enable use of separeate reading thread. libjgroups2.6-java-2.6.15.GA.orig/doc/design/ProbabilisticBroadcast.txt0000644000175000017500000001331711366547366025533 0ustar twernertwerner Probabilistic Broadcast for JavaGroups ====================================== JavaGroups currently uses virtual synchrony (VS) in its main protocol suite. VS is suited for tightly coupled, lockstep replication. Typical examples are clusters, replicated databases etc. Group size is 100 max, and it is targeted to LANs rather than WANs. The problem with VS is that is has to enforce that all members have received all messages in a view before proceeding to the next view. This is done by a FLUSH protocol, which ensures (by retransmission) that each member has seen all messages in the current view. During the FLUSH protocol, all members are essentially blocked. Messages can be sent, but they will be sent only when the FLUSH protocol has terminated (in one of the subsequent view, not in the current one). The FLUSH protocol itself may need to be restarted, e.g. in the case when a participating member fails during the FLUSH. When one node (or a link) in a VS group is slow, it will bring the performance of the entire group down, as members proceed at the pace of the slowest members (at least during membership changes). (Otherwise, the likely result is just growing buffers and retransmissions, as messages waiting to be delivered are buffered). The bimodel multicast (or probabilistic broadcast) protocols (PBCAST) developed at Cornell try to solve this problem by providing probabilistic reliability guarantees rather than hard ones. In a nutshell, the probability of a very small number of members receiving a message is high and the probability of all members receiving it is high as well. The probability of some members receiving a message is very small, because the 'epidemic' nature of PBCAST infects the group exponentially, making sure every member receives a message, or none. PBCAST protocols therefore scale very well, both in terms of group member size as well as over WANs with intermittent link/node failures. By implementing a PBCAST protocol, JavaGroups can now be used in WAN settings. However, there are no hard reliability guarantees anymore, just probabilitic ones. Yes there are a number of applications, which don't need hard reliability, and can live with probabilistic guarantees, for example replicated naming services and publish-subscribe applications. In these settings, eventual convergence of replicated state and low-cost of the protocol is more important than lock-step replication. The JavaGroups API will not be changed at all. However, applications with a protocol stack configured to use PBCAST have to be aware that views are only an approximation of the membership, not a hard guarantee. The PBCAST protocol is located in the ./pbcast subdirectory of ./Protocols. The major changes are: GMS --- Unlike VS, the JavaGroups implementation of PBCAST does not per se guarantee that the set of messages delivered in a view V is the same at all members. Therefore, applications cannot rely on the fact that when they send a message in view V, it will be received by all current non-faulty members in V. Views are delivered at each receiver at a certain position in the incoming message stream. However, as PBCAST only provides FIFO (which guarantees that messages from sender P are seen in the order sent by P), it is possible that messages sent by senders P and Q in view V1 can be received in different views at each receiver. However, it is possible to add total order by implementing a TOTAL protocol and adding it on top of a given protocol stack. This would then essentially provide VS. Consider the following example: P send messages m1 and m2 in view V1 (consisting of P, Q and R). While it sends the messages, a new member S joins the group. Since there is no FLUSH protocol that ensures that m1 and m2 are delivered in V1, the following could happen: m1 is delivered to Q and R in V1. Message m2 is delivered to Q, but is lost to R (e.g. dropped by a lossy link). Now, the new view V2 is installed by Q (which is the coordinator). Now, m2 is retransmitted by P to R. Clearly, VS would drop m2 because it was sent in a previous view. However, PBCAST faces two choices: either accept the message and deliver it or drop it as well. If we accept it, the FIFO properties for P are upheld, if we drop it, the next message m3 from P will not be delivered until m2 was seen by R. (Message IDs are not reset to 0 because we have no total order over views beeing delivered at each member at the same location in the message stream, as shown above). Therefore, we have to accept the message. This leads to the conclusion that views are not used as a demarcation between message sets, but rather as indication that the group membership has changed. Therefore, protocols in the PBCAST suite will only use views to update their internal membership list, but never make the assumption that all members will see the view change at the same logical location in their message streams. FLUSH ----- Not used anymore, as we're not flushing messages when proceeding to the next view. NAKACK ------ Not used anymore. Functionality will be covered by PBCAST. NAKACK made assumptions about views and messages and can therefore not be used. VIEW_ENFORCER ------------- Not used anymore. Messages sent in one view can be delivered in another one, although this usually doesn't happen. But we cannot make any assumptions about it. STATE_TRANSFER -------------- Not used anymore. New protocol for state transfer, especially geared towards big states (transfer in multiple transfers). However, STATE_TRANSFER could still be used (a TOTAL protocol has to be present). QUEUE ----- May be used by the new state transfer protocol STABLE ------ Not used anymore. Functionality will be covered by PBCAST protocol. Refs ---- [1] http://www.cs.cornell.edu/Info/Projects/Spinglass/index.html libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-leave.fig0000644000175000017500000000741111366547366023261 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 3037.500 2175 2775 2550 2925 2175 3300 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 3787.500 2175 3525 2550 3675 2175 4050 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 4387.500 2175 4125 2550 4275 2175 4650 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 6862.500 2175 6600 2550 6750 2175 7125 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 7537.500 2175 7275 2550 7425 2175 7800 0 0 1.00 60.00 120.00 6 1425 1200 3000 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 1425 1200 3000 1200 3000 2100 1425 2100 1425 1200 -6 6 5925 1200 7500 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 5925 1200 7500 1200 7500 2100 5925 2100 5925 1200 -6 6 10725 1200 12300 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 10725 1200 12300 1200 12300 2100 10725 2100 10725 1200 -6 6 7125 3825 8100 4050 4 0 0 50 -1 0 12 0.0000 4 180 945 7125 3975 get DIGEST\001 -6 6 225 3675 1200 3900 4 0 0 50 -1 0 12 0.0000 4 180 945 225 3825 get DIGEST\001 -6 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 2175 2100 2175 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 6675 2100 6675 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 11550 2100 11550 9150 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 2325 2175 2625 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 3450 6675 3600 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 6675 3825 2175 4875 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 7200 6675 7500 2 2 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2190 5400 6675 5400 6675 6450 2190 6450 2190 5400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 4950 11550 5475 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 6686 3448 6986 1423 7286 3448 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 1528 3375 1828 1350 2128 3375 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 1350 7650 1200 6750 1650 6075 1650 5850 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 6975 7650 6825 6750 7275 6075 7275 5850 0.000 1.000 1.000 0.000 4 0 0 50 -1 0 12 0.0000 4 135 795 1800 1725 A:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 6300 1725 B:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 11100 1725 C:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 615 7500 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 825 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 7125 3750 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 225 3600 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 135 840 2700 3075 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 4050 3300 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 2700 3825 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 2625 4275 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 3825 4725 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 135 840 975 4875 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 105 540 975 5100 returns\001 4 0 0 50 -1 0 12 0.0000 4 135 885 600 6375 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 885 7050 6900 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 2625 7725 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 4125 7200 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 525 7950 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 525 8175 latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 6750 7950 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 6750 8175 latch\001 4 0 0 50 -1 0 12 0.0000 4 135 765 2775 6750 RESUME\001 4 0 0 50 -1 0 12 0.0000 4 135 615 8250 2175 LEAVE\001 4 0 0 50 -1 0 12 0.0000 4 135 1860 3450 6000 VIEW INSTALLATION\001 4 0 0 50 -1 0 12 0.0000 4 180 1095 7650 5025 LEAVE_ACK\001 libjgroups2.6-java-2.6.15.GA.orig/doc/design/STABLE.txt0000644000175000017500000000233711366547366022074 0ustar twernertwerner STABLE ====== Author: Bela Ban Date: May 29 2007 Version: $Id: STABLE.txt,v 1.2 2007/05/31 09:56:58 belaban Exp $ Goal: to purge messages delivered by all members Vars ---- - digest: used to reconcile all STABLE messages, re-initialized on STABILITY message (with digest from NAKACK) - votes: list of members from whom we have received STABLE messages. Reset on STABILITY message Initialization -------------- - Set digest from NAKACK When the timer expires or max_bytes have been received ------------------------------------------------------ - Send STABLE message Sending of STABLE message ------------------------- - Get the current digest (from NAKACK) using the GET_DIGEST_STABLE event - Send the STABLE message to group On reception of STABLE message from P ------------------------------------- - If P already present in votes: discard - Else: - Update digest - Add P to votes - If votes from all members: - Send STABILITY message On reception of STABILITY message --------------------------------- - Pass STABLE event down to NAKACK to purge messages on which every member agreed that they have been delivered - Set digest from NAKACK - Clear votes View change ----------- - Set digest from NAKACK - Clear votes libjgroups2.6-java-2.6.15.GA.orig/doc/design/varia1.txt0000644000175000017500000003111611366547366022302 0ustar twernertwerner$Id: varia1.txt,v 1.1 2006/05/22 06:34:43 belaban Exp $ Design Issues ============= Correct determination of left members (UNICAST and others) ---------------------------------------------------------- In UNICAST we have a connection hashtable which stores the sender's or receiver's address and an entry for the connection to/from that address. Whenever a view change is received, we remove members from that connection table that are not in the view. However, this introduces the following bug. Consider the case where we have 2 members in the group: {A, B}. Now C and D want to join simultaneously. Let's assume C is joined first, then D (JGroups doesn't join more than 1 member at a time). This will result in view {A, B, C} which is sent to A, B and C (not to D since D is not a member yet). Let's also assume that there is already an entry for D in A's connection table: if A is the coordinator, this entry is the result of D's JOIN request sent to A. Let's assume D's JOIN request has a seqno=1. When A receives the new view {A, B, C}, it will remove D's entry in its connection table BECAUSE IT THINKS THAT D HAS LEFT THE GROUP. Now D resends its JOIN request (with seqno=2). Since this unicast message is not tagged as first message, A will reject it. (Note that D does not retransmit the message with seqno=1 because A ack'ed it. Receivers ack all messages received even though the messages might not be accepted by their connection table). To solve this problem, we have to determine whether D really left the group, or whether it is just a new member not yet seen by the group. To do this, we maintain a 'group history' in a bounded cache which contains the last n members of the group. E.g. when C joins the cache is {A, B}. Before A removes D's entry from its connection table, it checks whether D actually was a member. Since D is not in {A, B}, its entry will not be removed. This has been modified (bela dec 23 2002): Now we do not remove an entry from the connection table in retransmit(), and only remove it in a VIEW_CHANGE when the diff between 2 view shows that a member left (see determineLeftMember in org.jgroups.util.Util). For example: first view is {A,B,C}, next view is {A,B}, then left member is C. The use case is when 2 or more members merge into a single group, e.g. A and B. Let's say A is the merge coord. At this point A needs to send a unicast message to B which is not member in A's view. Whenever we received a view change or retransmission request in A or B, each one would remove the other's connection entry from the connection table, therefore preventing the merge protocol from completing. Setting of recv/send buffer sizes in UDP (FRAG problem) ------------------------------------------------------- There was a bug in FRAG which caused fragmentation of larger messages to receive only the first fragment of each message, and not further fragments. The reason was that the default send buffer size of UDP was 8195: when a larger message (e.g. 30000 bytes) was to be sent, it was fragmented into 4 packets (3 * 8195 and 1 * 5415 bytes). All 4 fragments were then passed down to UDP almost simultaneously which caused the UDP send buffer to overflow which resulted in fragments 2, 3 and 4 to be discarded. This explains why retransmission of fragments always correctly received the packets. Also, if there was a small timeout (e.g. 5ms) between the fragments passed down to UDP, *all* the fragments would be received. The solution is therefore to set the mcast_send_buf_size, mcast_recv_buf_size, ucast_send_buf_size and ucast_recv_buf_size in UDP explicitly, based on the frag_size value of FRAG. Note that the above problem did not cause messages to be dropped, but added a number of spurious retransmissions. Use of multiple sockets in UDP ------------------------------ There are currently 3 sockets in UDP: - a MulticastSocket for sending and receiving multicast packets - a DatagramSocket for sending unicast packets and - a DatagramSocket for receiving unicast packets Normally there would only be 2 sockets: a socket for sending and receving unicast packets and a socket for sending and receiving multicast packets. The reason for the additional unicast send socket is that under Linux, when a packet is sent to a non-existent (e.g. crashed) receiver, there will be an asynchronous ICMP HOST-UNREACHABLE sent back to the sender. However, since it is asynchronous, it will not cause an exception in the current send(), but in *one of the following ones* ! Therefore, we need to close and re-open the send socket every time. This feature is due to the fact that the older JDK for Linux from Blackdown did *not* specify BSD compatibility when creating DatagramSockets, which meant that ICMP HOST-UNREACHABLE errors were actually progagated up to the interface (send()), and not just discarded, as would be expected. Newer JDKs (e.g. SUN's JDK 1.2.2 and 1.3 for Linux) don't seem to have this feature, e.g. they seem to have BSD_COMPAT set to true). An item on the todo list is to remove the code which closes and reopens the send socket (search for is_linux in UDP.java) and test. This way, we would have only 2 sockets and didn't need to close/reopen the socket on every unicast send. Why not merge all sockets into one, e.g. a MulticastSocket, for sending/receiving unicast as well as multicast packets ? -------------------------------------------------------- All MulticastSockets need to use the same IPMCAST address and port to be able to receive multicast packets. However, in JGroups, every member also needs to have its own address/port to receive unicast packets. In this case, this wouldn't be true, because the MulticastSocket only has one port, and that would be the IPMCAST port. Therefore, returning a unicast response to a multicast request would be impossible because senders cannot be discerned (they all have the same port). Example: sock=new MulticastSocket("myhost", 7500). In this case, sock.getLocalPort() returns 7500, which is true for *every member in the group*. Update (bela Sept 12 2006): even if we don't choose the mcast socket's port (new MulticastSocket()), when sending on multiple interfaces (send_on_all_interfaces="true"), we receive a different sender address for each message sent on a different NIC. This needs to be handled with *logical addresses* (see JIRA for 2.3). Sending of dest_addr and src_addr in Message -------------------------------------------- There was an idea of removing the src_addr and dest_addr from the serialized data exchanged between UDP protocols, which would reduce the size of packets. The idea was that the DatagramPacket itself would contain the sender's address (src_addr), and the receiver's address would be the one to which the packet was sent to. However, there are a number of problems. First, since we cannot have just one socket (see above), a receiver of a unicast and a multicast packet by the same sender S would see 2 different sender addresses (assuming for now, that we have 1 multicast and 1 unicast socket). Let's assume that the sender's port of the unicast socket is 5555 and its multicast port 7500. The receiver would receive a packet from S:5555 and one from S:7500, which are 2 different addresses although coming from the same sender. Second, a multicast sent to a number of receivers cannot be replied to: a receiver sees that the sender's port is 7500 and replies to that port. However, that port is used by the multicast group, therefore the sender of the multicast will not receive a unicast response. Third, a member always needs a unique IpAddress as local address. In the above case, we would always have 2 different IpAddresses, which causes problems identifying the member. Note that IpAddresses are used as keys into various structures, e.g. in NakReceiverWindow. UDP PacketHandler ----------------- In the old design, unicast and mcast receiver threads first received the message, then unserialized it and finally passed it on to the next higher protocol. However, as unserialization takes a long time, the thread spends too much time in its unserialization routine, whereas it should be receiving messages. Therefore a lot of messages got dropped (network buffer overflow) and had to be retransmitted. The new design offers an alternative: the ucast and mcast threads just receive a DatagramPacket and add it to a queue served by the PacketHandler thread. Then they return to their task of receiving messages. The PacketHandler thread continuously dequeues messages, unserializes them and passes them on to the protocol above. The advantage of the PacketHandler is that large volumes of messages are better handled. The disadvantage is that we allocate a new byte buffer every time a message is received and add it to the queue. In the old design, there was one buffer for ucast and one for mcast messages. A receiver thread would receive a message into that buffer (65K), unserialize off of that same buffer and only after passing the message up the stack would it return to receiving a new message into the same buffer. So the advantage is not using up space (reuse of the same buffer). The new scheme copies the bytes received (e.g. 425 bytes) into its own separate byte buffer and adds that buffer to the packet handler's input queue, therefore using more memory, but being faster overall. To enable the PacketHandler set use_packet_handler to true in UDP. Storing copies of Messages vs references of Messages ---------------------------------------------------- - Some protocols were changed May 26 2002 (by Bela) to store references instead of copies of messages in retransmit tables. This avoids accumulating large amounts of memory for copies that are not needed. This change was possible by switching from stack-based to hashmap-based headers in messages. When a message would have been stored in a retransmit table by reference rather than copy, each subsequent retransmission would have added headers to the stack. With the hashmap-based headers, headers for a given protocol are just overwritten by the header from that protocol. - The caveat is that the buffer (payload), or source or destination address should not be changed after the message is stored by reference in the xmit table, because the original message will be changed as well. GMS-less reliable message transmission -------------------------------------- Here are some more points to look at for GMS-less message transmission. Looks like such a stack could be FRAG:UNICAST:NAKACK:STABLE. Let me look at each protocol in turn. FRAG: - No changes; should work out of the box UNICAST: - The first message to a receiver is tagged with first=true. This allows the receiver to determine the starting seqno - When a member crashes we don't know about it. Therefore we need to periodically clean the connection table of unused connection entries. However, this could remove a connection to an idle (not crashed) sender. In this case, when it sends the next message, and we don't find an entry, we'll just return an error to the sender, so the seqno agreement process starts again. This way, UNICAST is reliable in that we don't lose any messages - When a receiver crashes, we don't know about it. Therefore, if the receiver hasn't acked all messages, we will continue retransmitting those messages until the end of time. To prevent this, we will close a connection after N unsuccessful retransmissions (without having received an ack). NAKACK: - The first seqno from a sender causes the creation of a NakReceiverWindow for that member (if it doesn't exist already) - When a sender crashes, and the receiver hasn't received all missing messages, the receiver will keep requesting retransmissions of the missing messages from the sender (forever). Therefore, similar to UNICAST, we purge senders from which we haven't received message retransmissions for N attempts (or a certain time). - Periodically we will purge idle connections. These may either be idle members, or crashed members, but we have no means of detection. When an idle member has purged, and sends the next message, a new entry will simply be created for it. STABLE: - A hard one: how do we know when a message is stable in all receivers so we can remove it from the retransmission table, when we don't have membership information ? The simplest scheme is probably just to periodically purge messages that are older than N seconds. If we accidentally purge a message that has not yet been seen by all members (small probability though), we just send an error back to the retransmitter. The requester could then for example remove the retransmitter's connection, so retransmission requests would stop. libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-coord-leave.png0000644000175000017500000003301211366547366024400 0ustar twernertwerner‰PNG  IHDRŽ<1àb pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v IDATxœíÝÛ–›º¶Pû´üÿ/û<°Ã"è¦DïieŒª0aXñýý~ˆçÿžÞÈUJT (Q€ DU‚UJT (Q€ DU‚UJT (Q€ DU‚UJT (Q€ DU‚UJT (Q€ þ<½-ù~¿OoBÃ~¿ßÓ›@óÔàjg©ß#Þ\¿¢ê6oÞWŽp„â,jp5HêwŸ—ׯ%ª”¨ @P¢*A‰ª%ª”¨ @P¢*A‰ª%ª”¨ @P¢*A‰ª%ª”¨ @P¢*A‰ª%ª”¨ @P¢*A‰ª%ª”¨ @P¢*A‰ªõçé àóý~¿_}†ñçaÎÅ\´%ÐiY J;¿„°Òò\œAý6GT}Xz¾Lg÷þÅ™•~¿ßìÔ’=Ó(@k±<Õot¡TB³sçpr½k£àí „µXžê·ZUŸ4R½À¦Ò/ˆ³%¤Õ˜’.^({õð`~’ŠVƒpƒÅëûê·QZU£Xìp3óûýfßÇ—Ãõ)¥UëüþšM\ùÁÊË„'¨ßþhU}؉»ø¦EéSi×Õ­¶Ö²„8ÔodZU3ìèÓFì-Ékêçûýî(›qɾB¶ .-À„ËSývÃׂ NüµØQfvcòøóYç˹¡Û/ œâŠ•úªn-À•E:«Á«û½©AwÅN¸8•úíÀ«ù­^¾¯áOÇ)ìH»ùÓñ8;án/ÿÓé@P¢*A‰ª%ª”¨ @P¢*AyZÕö>Oµ¾œì¨rÒªú¤é«.*}~ñúm8¸j€‹ˆª™µ}žõ*€nèÈì§¥)‹‰Bðlzé ®ÙùÓ—5€DÕX¦M­ßïwÚuœ2äÈ´[jšM§³g/g Ì®à:Ä•ÆÄS‚ãb7ñBT}L©sjö‚þ)k›KKïž²€³èð¤´Kè0%½^?»dÿ™Œ0‹˜ã»Ó—é³+þézÓÙn&ª>lÍUþÙ”M/×oÀÁåœN‚UJT (Q€ ÜVõŒÙ=þ@»œÖ¯£Uõã3¨JC«MÇ,OŸ…Î)DÕgL‡ˆ²[@»JÃ>r îvé ú³Ô«f€¦‰ªÏVÄÊRÉ®“ž¾ŸÓ™ñ×ÜÀ•ú#ìi§Pƒ(ä QµÓS×ÇN÷û3ëIµxWël(5¬¼;v±¥-»À馦3L5f¿”½÷AV =õcÊï÷ö{»>ti<±ÉÇ×þ¶³?ï8%}«Ôë÷×,P––Súøø2ýT:1 г·J/!&Qõ°§ŠÊ€•Æ”™Qw4…®\ÎÊn¢éŽvéФÅ/ô—®˜ú3M¨‹À±Í¢´ìnÔ8~Slö±»Fã$['ªa÷ºa<®Eø C4Âñ Æô¹é(Tﱺo.'ýENÜÂþ8é$ª²ìÁÒ}ö0ê˜/÷[7.XéH5ÆÄƒ½ÅŽ‹Vþ"¨Ú¼¾>Ïþj‚2kØ©²´}ßÒîünwÝŠ„`¶j1(»¸p‘£aèÈÓ¨³ƒz¬ŒzÔRòôY+·a·ì–¬Y˾·øÁtGs2@*P€#7´>; Çl¼:óZ‚éL‹ÍÉgQ@É¡¨:>‡ú“{èð'9öe›+ï.*=Pdñ˜›Î½Mrv¿ä)=ßÓŽ-Äé2Ýžù!˜zmPV;Ü¡¨ºØ‚8›!;ÚÜøÖîmH—\­éøs³)Óì8ûf½Žlðti¥?ãšiÄmˆÜ%5x܃@ÍÉL)瀢t8¸gœxÙ=Ûzzî¾kànN'Ã>¯mNþ<ý»ÃJQ¢jV©Ev¬íd¶ÅwÚöyʺÒ~k¶$öV‚¬×å ¿¸Zn¡T´¦géø^‚*»ôÝÒÂÇ®Züàl¥•N÷ʘ•¿R©SliEŸÑf®Á¥ °RæälPÎÑ\þ_ò’ÿõú`U÷¬¸Úš/«@p¥ ¬œc Ý 9|MTTpƒJ5íRÎñ]ûíAw€ƒö]£¬s^Óke¯ªt³%ŸxGAeEû†tn‚øþïÒ¥ÿþºt-½âÔì@ºæ :Ìó›§g›‘fóÌ֞Μ}™Ý†SŒ[²õâ•W u×FUnà@ Ïj¥ÇñDéàÖOm ݳw¤¯*Àf÷œÏÆg”|<pµtÓ§º|’¿äîÑ4‘´!¢*À²J(¼tl¬zÊŸ'–7xº´ÒŸñÈŠˆ¯ò?[ÿñ²Ía§¢ê¦X7 ¿º84éÇWg±ug.æ;›seQ,¬+4!mÖ Þ µØ`Ù–ƒÛ¿š·.j6ñÜ;œ¤÷Xû íº<ªf¿åg¿a§‡Âõ³Mg®Ü-ëøE¶îÌ‹ƒ}ÎZ­JË)}|œ±œ?ÿ‘ÙZ*ígt–M×(í$éÿà¹ëÌVOYWÚ¯`Í–ÜŽÆQzò|€ìÕ¥tJåÐS:w¦{p—ût—¿ƒM;óšðÓ&w¢³²éu¿WÚÕ2"+½0§Ü³%Ì~…!,N§džm˜¾*ËÏö%ÈnäšÝ&ÛÇ ÝªÒ/²~E\'Nã¨Ý  Ó¾Åf‡ƒÒ¾R0]üŠÿÉeÖÙlûNáÊúyv>^œ^ZNvF›•o¶–§K¾G—í¦/9¾Õ÷gâ‹FiÎÑVÕÊá£tÅp‡t¥›Ýhξ9Û7æà6\N¥x.y.³é›éÄœ+õ\áä÷ÇÄìÍW¥ÕWý²o³rg®÷9Þ ð”o›÷ì¨dÓk0½öÝ«áìþ׌¯ûÆÑ7TSsNˆªé¾­ BÙ9ןÛNl¾…v×N6•þλg¥´öRwªòåí¦oû}¹‡ÆQB9z"É^O_Ö/ùÍ>øYº¾ŸnƬkvu½˜ŒÞŸ•;sååú9×o@©ôÒ·{™¯ÜŒÅmÛúÙ‹¨AšÓ}ãènÊ9 ¢jöF(nãÏÎAÏÞê´(`6Qƒ$Œî£œ:Ô tÕhH´ãrül A¸RÏúöPºL °žlz®‹:Y}Ê=IÒ99‹ÆQÐÐ ÜM6=Ñâ`·ÓJƒï®™-½‹®òë £P÷üÓª€îɦª}=­>êÙlÎßï—¹åœîŽ+õ°›¨Ú<Íôªlzu VR8k\Õq™ÓöÑéJ§«Ë†T4ŽöÁ)5 Q8Á«²é*×ÖOR:]E½3ëkÿ5ŽÂýDU`Ùô÷ÇÄ#Ïgi‘ÆQˆFTV‘MŸâ‰€çF¡-¢*'›†2ÞÉ4<&÷“ ¯ãËOáÂýçßà›}Î_úñÙ’?ÿî gõ—=—+õÐXàA²i@žX¢q^B«*¼—lœ'jDÕW[<ù->Ò&a6˜Î§pѰ¾1éåÅÒº•Ïé©« 9™]]X²isV–O»4Žu.'5ïà5Áúciê+ZÿКÅ,]å¬üüÉ%Útíõ{P²ü6ýRõ­zœlz×åë„Q¢œÒªÊ?–è¾Û„³OÍI•N9ý˜’]Zi¥Gõ<{¤“My„+õÀEDUŠWÞ×Ë>ÆæH׺5›1½(?FÛëÚôëÿ[N6åGGˆªìé»YZHöi7—\WjÂ\ßÃoÓ¯áÙ”‹£@@¢*ÿ9~*ºyœðqŒÉÒµø­K»hæƒdSNäJ=ÐQµy7œ]²÷MOxãi)à¦7-øË®¼uìÒ%T–<})@í?Hã(ì¦@r§Û«­Â)ò©ô‘û«ªtTÝ7.U}¥k>¾•lÊVG—U¹Uz~}ç(›²Hã(ÀGTí€Aàš ›vlw £Sj@úªÂ%dS®Ô!ªÂ9dÓ7“G."ªÂN²é«¸XðQÖ’Mû¶;Œ>ø<€îé> E²ig´Œ4G«*üG6í€n£ Yl. ÞH/ûh½ìg+ _ùó'—h³öËnRé×ORÿ¥ê[ЪÚ<ƒÀm"›6¡­0ªOtð/Yйu³Œ )M/}ð¸ìÒJ«È>ŒúŠ­z½€DU:'›†åJ=ƒÒ•÷õÆëã¿ßoÇåõìׯôS}¼ó¢M¿þñ¿4GT¥7²im5Žò”Ãå´I,Ûßôt¥F¸•d?ýSþVÐQ•æÉ¦ÏÒ8ÊYŽï0û:\]v;¶aÓGï!ªÒÙôfGyVö¾¢ény0ž–nzÓÒ‰{ûÊ[Ç.]4Á>MdÓ« £fgžu`ýœíŠúHZ•áYjº¡œÒ 8ó´›ivæìt—øÎÒgT-åÅéôñçÝárë=þzlÒgx*õ(]…ª4%ýìÌl-wV%Ã³Ô tC9俤yê ž¥¡Ê9 >;ÐQ€ DU‚Ò'€ ´ª”¨ @P¢jó< ž¥¡Ê9 ×EU{!@+^UhÅŸ§7à|³Ç¢Ž/ÇŸÇgQLŸ€š}jv9ŸË‘ ÀT‡­ªCŽüý~³@9&ÔÙôñåôƒÙ…Lßàj¶ªNø0ßiûë) \Š´ªîàO‘StFQï£@PkDÕæ9™Á³Ô tãºrÖ¤º›¨ Žø 0U.äËç¢*@,.Bõn¢*ÀU|ù9®4ûr6å÷û­ìÒÝ ‚ÿ{o^æÝÒÏãËqbeÎé”tó¦S²«Øý‹@ödh*>Áªš·{¸ßï7m¿¼Î¬h7­t圎 <ȸªÐ åœ Û8:þ<¼5&Ô!"¯Yì4ÔŽM³•U@Óœ#¡uªø ¢êÛM/ÍïP*Ël×Õ}ÝdKó×{ÇB‹|é‚Ö©âÓéÀ¶+òégÓ{¤f3YÝštë¸@ôoƒÖ©â‹ˆªœ žV‡éõwÓŸ7Ѱ ]ò €ÿÙq]~ø¡žSÇyJ]K³ƒ^¥ _l‹µ'Ó.1Ð:U|Yþ£ÒàJZ§Š¯£À“œá uªøR¢jótÓ„g©Aè†rHTxŒÆh*¾š¨ @P¢*ÿ¹ç9«À@c À"Qàr*t@!ß@Tmž"g©Aè†rHT¸›–è€B¾‡¨ @P¢jóN¼ÊDØak ºyÂRž‰ªwóZ§qç6žÞ€qzƒwš¶×aÊt˜ÈñàPjÜ~pö‘ìºfïÎ>X_K¢*Àµ†h8McXLSã8óì#ÃËtúÔô­ÊÏé&…ý"­ÀMž €M®¨åSžÂSJå饆¢Uµygí[‘wSˆLá@7ž-çlséâåþÝëš.?òqL«*À|e; ®[ã'öТ*Ÿ“(¬sÃ3Oé!<­ŠªÍ;½k °ÉšTeЄ«ãÚtù[oÀ¯lÛô–ÿÙ*†¥-ÆÜÈiU_U€Ë y±4jÕô ítÎÊü•µLçO§þ½ãjúñ€ß«EÕ· ¸S@—²'ÜõߪÏ0›ÞÊÙ_€W“Sá º¡œï§Uõ¥.œbvÑ¡Ô1<;s:ý!%Ù¥Í RßÚìK¸‚ØÔùrðF})»é‡•?W¶?ûHÒÖoIe`½Å½¨¾—~þ͈•ØÔ×µ©âšx€ ¨Ú‰Pm!¥ž£ë?~ÑÌ¥%86Ñ„Ò}ŠíÀÒ*Ì(‡Gè«Ú›árv©¯ê”ß$’ IDAT¥Ö¯1m¾n,‚R{³SâX³gVn[ª¸Åް•¡<îáûAóâ|É+už["§2¼Ôʵ”Z›Ò·*ëšmÏš~ðY=TE} Ó—•=pý®^ùl½ÛëÖ¬ =©—mœî«ø£7¯ïÊIÏšÿ²4êxT‚UÒWµy}—Mß¿}°—B7”s@úªœOë ¼Ö#·‹¬—ݼúWÆk¿¨ pšz‚• ¹.ný=|ñoÚ¾h«*DÕæEþêo ¡Ê9 }U{àR#<®ra=} qeØ™ì£+)MY9¾Çâ*®_²NTíÁtÿ“Yáq¾= M ?›YKìHŸÝSzœòlø¹éêf Ÿß1MÆ›žr7^÷Ÿ}ðþƒ›¨Ú‰tÿ{êL9­–÷éŧpExL\$øîݼÅA†ÂþRp\¥ ÒÜydEi‹é¾~¥1¿fë«Ú›ißçGî×Ë>¦üÜÅî›Úå. hÔ¸‡WúÌÞZ™~ëükT6ø)ZU›W:IÄig…¾©,èÆ‘r.5m¦—æ§-JŸ{…¦sf—?~¤>eÚçµòÍp:[v«ží±¥—‹\Y³ý»ÓnÝé–Ôû‰¯ÿøGh †Ó»¾ J÷^dߪϙ.ÿø]¥•U”> 1ż>Þ=­ª/ri;k½¿v©'øôÝÙiuM7ðô«ê¿ Dá. Qµy;ô7÷ ›^ÎêowbWt8®ø®^W}3ެ¨ï»4 ÄÛª^íž{°*½dÆõn:4ìô ·©ìÿ¥·V6yÖ—¹›‚Žðí¡yç~<ÒÎZéÄ6>[K¥åf±óœ¾ªDpi«êÊý}/ð³úªÖ«ok‡Z•KõúÕæúôæ]T9÷ô €ÜÙ¸ÔbTý8-ÞN_UònîÏ Áíë±ÍA¢jó®N2+Ô©èÆÕå\ꇳ~½»¯Ûd‡ìhâð%ª²V£»8¡]¶¹NJ¢*›É¬À=DÕæ=øõHf…OƒM@ÉÁr“ñ) q3Î6›2›þ)Œ˜‘Ι} G:òÆìƒmÝ&ªr™³´ZÙ­ôä¶ôqqÙµ,Nù$á»ôˆ»ÈDUÎ$³ÂÍÜ¥ý©wiÍÖl6÷ATåN`pwi@7V>99û¸òYóíôeëå)ªr-™€÷HŸ$<ý¡ôÈÆ±“ëš§»¥ œ­hº¨5 ®¥m¥2+};ýÁª‹widoÅXù\âÙ*>¹¯—³U—>øQÔ¼@[!¯ZU¹›vVØÊ]„þ~¢*‘Yá6îÒõOoG=~GÅq¿¿¾=½E°Aä=¶OÇŸg䯯ÂüüÛËíš…çÙ½òE¹y]¶vhg¥!é çöU­ÏöMïØ_~’âJ—¿²¯jåƒÐ®5õÛåi72îæõ]32+ñeoµÇB‹DÕ€ôU%4ýYiN„Nº!ªÒ™^HTmÞÛ›ÌJ4ö@è†rHT¥U2+tOT¥y‹™UxÎb¤á›Wµynà•Ægu› —²wA7”s@ZUéP¶ÕBÐQ•ž¥™U`€†ˆªô¬t)G7#h‚¨JÏäQhš¶%€ói¹‡Ž)ð;€ DU€ Œx'Qµyªž¥¡Ê9 Q€ DU‚2X@hÓ+’ÃMÇkî>®_Çœ~<»üÙÏ+_f·á¬¥=¿ÞI«*@\C*Œ×ĵažßÄ8}öñ霳åkOgξÌnÃ)Æ-Ñ•ÞFTmž6xV+5xd;ŸÍˆÓVdi•KµRί¢Àù†Duð´7d²ôÂ÷¸äYhKW—^ÜßdÌ…³¶ÕŰ˜Î0Û’q±Ó.é§äÂ:¥ÆYCTjñ\8›!yúÖîmH—\­³MJ§ /§iuÖhš âœzšçhÏj¢žSO¼ì^¹ gW HTx©éY9ûó‰§íìÅý±…õã¡ê@AM¯žOû­~rÙ?¹ëòé°SÙô9¦Æé”ìÂgôÓ—ÙûôgËÏö%Èn¤ü 8 4Ï¡žUªÁ{jó%G¯ÜôA?Bx!ß.qCóLýaQU®âb"ÀA:”¨Ú<·àYjº¡œUʸªqíèíªƒ,Ü)xM Ö›oKÏ.ž?<{þðš—Ùm8ëpqîÒ¦´ª4É•JxÜøØ‹ìƒ-*†y~ãôÙǧsΖ?®=9û2» §(=þã¢*ÀÃŽÇ‹2âJÓVä+¶D€æ¹ÒϪÔàpÔ>¥H+8M¯ñoöÙT2éã‹Çé³ë聾gŸf¼ikǵ—ú T¶¼²%ãb§“ôS÷aDU€ u(O{ž95]Eö4 œkñ»èl†tæôëåŽmH—\­³MJ§L¿Ï~…éËÛŽ0:´Aô„^üN{âe÷Ê XOU›çìϺ¡ÇvŽÇ·úªˆ¦“ýùÄ­Í^ÜŸyZĘ&Í30 <ëº,‡fÖDèL}ØTÎkJ¯Ôµ4Uv!黥…/Ž]µøÁÙJ+œþîÙ®ºg‘rš'ªÂ³Ô tãü˜õŽãÃ¥¯:pÔEÝ'DU€óMG³ê[úœ‚½¢]€iU (Q€ DÕæ½¡ D¦¡Ê9 Q`ÑVÞJT8™HzQ€ þ<½mË>ÑtxtÓì­tfêDÕæÙÑáYjº±»œ§ ux9æÔt™³™©Óà(Ñó"¢*À~ßï7ÛzšÎvÏötƃU›·¦<€ë¨AèÆ¦r.EÏéõýY_Õô#Ž‹ôUجž2Çw§³ ¦;è@P¢*A‰ªån‚Òª @P¢*A‰ªÍ3¤0= max_bundle_size: - send all messages - set count = 0 - set start = current time - add message - set count = count + msg.length - compute new wait_time: max_bundle_time - (current time - start) - if wait_time <= 0: - send all messages - Init() On Timeout: ----------- - send all messages - Init() New implementation (Nov 2005) ============================= - done in TP.send(), this way message bundling and using a separate thread for sending messages are orthogonal issues Variables: ---------- - queue, queue size On send(): ---------- - if msg size + queue size >= max_size: - cancel timer - bundle and send queued msgs - clear queue - add msg to queue - if timer is not running: start timer On timer timeout: ----------------- - bundle and send queued msgs (if any) - clear queue libjgroups2.6-java-2.6.15.GA.orig/doc/design/Reincarnation.txt0000644000175000017500000000471411366547366023717 0ustar twernertwerner Problems with reincarnation =========================== Author: Bela Ban Version: $Id: Reincarnation.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ JIRA: http://jira.jboss.com/jira/browse/JGRP-130 The identity of a JGroups member is always the IP address and a port. The port is usually chosen by the OS, unless bind_port is set (not set by default). Let's say a member's address is hostA:5000. When that member dies and is restarted, the OS will likely assign a higher port, say 5002. This depends on how many other processes requested a port in between the start and restart of the member. JGroups relies on the fact that the assignment of ports by the OS is always (not necessarily monotonically) *increasing* across a single machine. If this is not the case, then the following problems can occur: 1. Restart: When a member P crashes and then is restarted, if FD is used and P is restarted *before* it is excluded, then we have a new member *under the same old address* ! Since it lost all of its state (e.g. retransmission table), retransmission requests sent to the new P will fail. 2. Shunning: Regarding shunning: a member keeps its last N (default is 100) ports used, and makes sure it doesn't reuse one of those already-used ports when it is shunned. However, this is process-wide and *not* machine-wide, e.g. when we have processes P1 on A:5000 and P2 on A:5002 (on machine A), and both of them are shunned at the same time, when they rejoin, P1 does not use port 5000, but might use port 5002, and P2 doesn't use 5002, but might use 5000, so they could assume each other's identity ! Both problems cannot be solved by remembering the last 100 ports: in case #1, this list is lost because we start a new process and in case #2, the list is process-wide, but not machine-wide. Again, these problems occur *only* when the OS reuses previously assigned ports. SOLUTION: A: Remember any ports seen by members on the same host, e.g. if we receive a view, we will add all ports of any members residing on the same machine to lastPortsUsed B: Use temporary storage (per host) to store the last N addresses assigned on a given host. This makes sure we don't reuse previous addresses C: Use logical addresses, such as java.rmi.VMID or java.rmi.server.UID, which are unique over time for a given host. Then, it doesn't matter what ports we use because the ports are not used to determine a member's identity. The JIRA task for logical addresses is http://jira.jboss.com/jira/browse/JGRP-129. libjgroups2.6-java-2.6.15.GA.orig/doc/design/ConcurrentStartupTest.txt0000644000175000017500000000526411366547366025471 0ustar twernertwerner Notes regarding ConcurrentStartupTest ===================================== // Author: Bela Ban // Version: $Id: ConcurrentStartupTest.txt,v 1.2 2006/05/20 22:02:12 belaban Exp $ The unit test org.jgroups.tests.ConcurrentStartupTest simulates multiple nodes in a cluster being started at the same time fetching the state (simple list) and then multicasting a message with their local address, so that everyone adds the address to their list. A joining member fetches that list (= the state), and sets it as its own state, overwriting the existing list, and from now one adds all received addresses to the list. At the end of the run, all members should have the exact same state. The problem is this wasn't the case. Reason: when members B and C joined almost concurrently (coord == A), they received the right view, but when B multicast its message (say 4204), then the following scenario could ensue: - Concurrently: - B (already received the state and now) mcasts 4204 and - C requests state from A - C receives 4204 from B #1 Coord receives message from B before state transfer request from C - A receives 4204 from B - A adds the seqno for 4204 to its digest and updates its state (includes 4204 in its list) - A receives the state transfer request from C - A returns (a) the digest including the seqno for 4204 so it won't be received again and (b) the state (including 4204) #2 Coord receives state request from C first, then the message from B - A receives the state transfer request from C - A returns its list *excluding 4204* (not received yet) - A receives 4204 - C receives digest (excluding seqno for 4204) and state (excluding 4204) - C sets its local state from the received state - When B multicasts the next message, or when STABLE kicks in, the last message from B will be determined to be lost and C will ask B for retransmission of that message (4204). When received, 4204 will be added to the list. Issue: the last message dropped problem of #2 can only be solved if STABLE has enough time to determine that there is a last message dropped problem, or when B multicasts another message. In our test, it is the former. Therefore, if we run into issue #2, and *don't* allow for retransmission of 4204, then the state of the joining member will be inconsistent. This is the reason for the sleep of 5s in each MyThread after multicasting its message. Note that issues like these will be completely solved with the introduction of FLUSH for (a) joining and (b) state transfer. Because the cluster will be acquiesced (nobody sending any messages) during the join and state transfer phases, we will not run into the problem of concurrent mcast messages and state transfer. see FLUSH.txt for more details.libjgroups2.6-java-2.6.15.GA.orig/doc/design/AddressTranslation.txt0000644000175000017500000000433611366547366024727 0ustar twernertwerner// $Id: AddressTranslation.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ On multi-homed systems, the identity of a member is bound to a NIC (either chosen by the OS, or by the user through bind_addr): Address. When a message is sent, the msg contains this address as the sender's address. Responses go to the same address. However, if that NIC breaks, and the sender's OS chooses a different NIC for the datagram packets, the receiver will still send the response back to the old address (the identity of the sender cannot change). If we set the sender's address in any Message on *reception* of the message, we would be able to send the response back to a valid NIC in the above case. However, this means the *identity* of the sender changes, which JGroups cannot handle. SOLUTION I: we could introduce a logical address, which contains the physical address of the NIC through which it was sent. Problem: a lot of code would have to change. SOLUTION II: we maintain, in each transport, a table of sender's address as defined in the Message, and physical address of the {Datagram,Multicast}Packet received. Whenever we send a unicast message, we get the destination address from this table through a lookup in which dest_msg.dest_addr is the key. We need to reap the table every now and then to purge old addresses, we could use view changes to do so. Example for SOLUTION II: - Member P: address=1.2.3.4:5555 - P's box has 2 NICs: 1.2.3.4 and 5.6.7.8 - Receiver R receives a message from P: P.src_addr=1.2.3.4:5555, datagram's address is 1234:5555 - R doesn't add an entry to the translation table, because the addresses are the same - R sends a response: it looks up 1.2.3.4:5555 (dst) in the translation table - There is no entry, therefore R sends the response to 1.2.3.4:5555 - P's NIC 1.2.3.4 is unplugged - P sends a message through NIC 5.6.7.8 - R receives a message M.src_addr=1.2.3.4:5555, datagram's address is 5.6.7.8:5555 - R adds an entry to its translation table: 1.2.3.4:5555 --> 5.6.7.8:5555 - R sends a response to 1.2.3.4:5555, since there is an entry for 1.2.3.4:5555, it uses 5.6.7.8:5555 as the destination address of the datagram packet SOLUTION II allows us to reuse the existing code, but provides for changing underlying IP addresses. libjgroups2.6-java-2.6.15.GA.orig/doc/design/UNICAST.new.txt0000644000175000017500000001677411366547366023032 0ustar twernertwerner New UNICAST design ================== (see UNICAST.txt for the old design) Author: Bela Ban Version: $Id: UNICAST.new.txt,v 1.6.2.2 2009/09/15 20:57:26 belaban Exp $ Motivation ---------- UNICAST has issues when one end of the connnection unilaterally closes the connection and discards the state in the connection table. Example: we have a conn between A and B. There's a partition such that A sees {A,B} but B sees only {B}. B will clear its connection table for A on reception of the view, whereas A will keep it. Now the partition heals and A and B can communicate again. Assuming A's next seqno to B is #25 (and #7 for receiving messages from B), B will store the message because it expects #1 from A (new connection). As a matter of fact, B will store *and not deliver* all subsequent messages from A ! The reverse direction is also bad: B will send #1 to A, but A expects #7, so A will discard the message. The first 6 messages from B are discarded at A ! Goals ----- #1 Handle the above scenarios #2 Handle the scenario where a member communicates with a non-member (get rid of enabled_mbrs and prev_mbrs) #3 Handle the scenario where a member talks to a non existing (or previous) member. Get rid of ENABLE_UNICASTS_TO and age out connections to non existing members after some time (JGRP-942) #4 Should be usable without group communication ('Unicast JGroups') Design ------ As example we have a unicast connection between A and B. A is the sender and B the receiver: A <-------------------------------------------------> B B:entry.seqno=#25 A:entry.seqno=#7 recv_win=#7 recv_win=#25 send-conn-id=322649 send-conn-id=101200 recv-conn-id=101200 recv-conn-id=322649 A has an entry in the connection table for B, and B has an entry for A. Each connection has a connection ID (conn-id). Each entry also has a seqno which is the highest seqno sent to the peer so far, and a recv_win which has the highest seqno received from the peer so far. For example, A's next message to B will be #25, and the next seqno expected from B is #7. A sends a message to B: ----------------------- - If the entry for B is null, or the seqno=0: - Create an entry, set the seqno to 1 and set send-conn-id to the current time (needs to be unique, could also use UUIDs) - Send the message with the next seqno and the current conn-id and first=true - Else - Send the message with the next seqno and the current conn-id B receives a message from A: ---------------------------- - If first == true - If entry or entry.recv_win for B == null - Create a new entry.recv_win with msg.seqno - Set entry.recv-conn-id to conn-id - Else: - If conn-id != entry.recv-conn-id: - Create a new entry.recv_win with msg.seqno - Set entry.recv-conn-id to conn-id - Else - NOP (prevents duplicate connection establishments) - Else - If entry.recv_win == null || conn-id != recv-conn-id: no-op - Drop message - Send SEND_FIRST_SEQNO to A A receives GET_FIRST_SEQNO from B: ---------------------------------- - If conn-id != send-conn-id: drop message - A grabs the first message in its sent_win - A adds the entry.send-conn-id to the UnicastHeader (if not yet present), sets first=true and sends the message to B Scenarios --------- The scenarios are tested in UNICAST_ConnectionTests #1 A creates new connection to B: - The entry for B is null, a new entry is created and added to the connection table - Entry.send-conn-id is set and sent with the message - Entry.seqno now is 1 #2 B receives new connection: - B creates a new entry and entry.recv_win (with msg.seqno) for A - B sets entry.recv-conn-id to msg.conn-id - B adds the message to entry.recv_win #3 A and B close connection (e.g. based on a view change (partition)): - Both A and B reset (cancelling pending retransmissions) and remove the entry for their peer from the connection table #4 A closes the connection unilaterally (B keeps it open), then reopens it and sends a message: - A removes the entry for B from its connection table, cancelling all pending retransmissions - (Assuming that B's entry.recv_win for A is at #25) - A creates a new entry for B in its connection table - Entry.send-conn-id is set and sent with the message - Entry.seqno now is 1 - B receives the message with a new conn-id - B does have an entry for A, but entry.recv-conn-id doesn't match msg.conn-id - B creates a new entry.recv_win, sets it to msg.seqno - B sets entry.recv-conn-id to msg.conn-id #5 B closes its connection unilaterally, then A sends a message to B: - B doesn't find an entry for A in its connection table - B discards the message and sends a SEND-FIRST-SEQNO to A - A receives the SEND-FIRST-SEQNO message. It grabs the message with the lowest seqno in its entry.send_win, adds a UnicastHeader with entry.send-conn-id and sends the message to B - B receive the message and creates a new entry and entry.recv_win (with msg.seqno) - B sets entry.recv-conn-id to msg.conn-id #6 Same as #4, but after re-establishing the connection to B, A loses the first message (first part of #4) - A creates a new sender window for B - A sends #1(conn-id=322649) #2(conn-id=0) #3(conn-id=0), but loses #1 - B receives #2 first. It thinks this is part of a regular connection, so it doesn't trash its receiver window - B expects a seqno higher than #2 (from the prev conversation with A), and discards #2, but *acks* it nevertheless - A removes #2 from its sender window - B now finally receives #1, and creates a new receiver window for A at #1 - A retransmits #3 - B stores #3 but doesn't deliver it because it hasn't received #2 yet - However, B will *never* receive #2 from A because that seqno has been removed from A's sender window ! #7 Merge where A and B are in different partitions: - Both A and B removes the entries for each other in their respective connection tables - When the partition heals, both A and B will create new entries (see scenario #2) #8 Merge where A and B are in overlapping partitions A: {A}, B: {A,B}: - (This case is currently handled by shunning, not merging) - A sends a message to B - A removed its entry for B, but B kept its entry for A - A new creates a new connection to B (scenario #1) and sends the message - B receives the message, but entry.recv-conn-id doesn't match msg.conn-id, so B removes entry.recv_win, sets entry.recv-conn-id to msg.conn-id and creates a new entry.recv_win with msg.seqno (same as second half of scenario #4) #9 Merge where A and B are in overlapping partitions A: {A,B}, B: {B}: - A sends a message to B (msg.seqno=25) - B doesn't have an entry for A - B discards the message and sends a SEND-FIRST-SEQNO to A - A receives the SEND-FIRST-SEQNO message. It grabs the message with the lowest seqno in its entry.send_win, adds a UnicastHeader with entry.send-conn-id and sends the message to B - B receive the message and creates a new entry and entry.recv_win (with msg.seqno) - B sets entry.recv-conn-id to msg.conn-id Issues ------ - How do we handle retransmissions of the first message (first=true) ? We *cannot* create a new entry.recv_win, or else we trash already received msgs ! Use a UUID (as connection-ID) instead of first=true ? Maybe the system time is sufficient ? After all, the ID only has to be unique between A and B ! ==> Solved by using connection IDs (see above) libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-state-transfer.png0000644000175000017500000003624611366547366025156 0ustar twernertwerner‰PNG  IHDR†<"7"ý pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v IDATxœíÝÙ–£¸¶PGúÿ_ŽûàS\B@ s>äȰ1`Áò–€Ÿßßß´óOëàíDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ ±[¯ÀH~~~Z¯ÂÀ~[¯ÃÓ¯ÐiKû½â íW$=æ ÛÄ쉨EµRðv tk³yj¿ÃQ%mii0冴ÿà æ·ºä#ñà…’½~à'jÑÚ <`³_^û ô–ÑUI{±9 &ðûûüþ îQ[~$·hx•ßÿî|aáÏ6-h¿‡t•§UI«¸)šUWÃG ¹xhéQGÛ²6ýxaûíð-¨’6óÝÖEšä)À{ÚÉÏÏωmk™óпð Šd+¸µ~´A¸`³yj¿Ãé.#÷¬âOŠÍ,ë}úU¶þõ|Óá¯1FtG\䯒m€;iÐï—¦ ÒÜáæ5ž´ßgfXEëÔ­>¿Â!øè¨Â†tšŽæl„§½$’ê¸x‹>óèG$ 9‘àº-‘~DRšIæ×s‰ô#’L¯ó<úq÷¦‡õvKÜäõÛ:Y7X«x MÞ¶þô¬7«P%}Îr»¦àÁÜĬ҉[{ÀóêC×·­¿xš!Cäf‘´Zw`€·q •Žûç,­(¸©ý÷·K|k²å7M²«"ît(Ü6-9ÿï‚EÄó'èÿ—“©{ ]Ë݃4÷òä=ÀsÙä¬ +s‡Q¼*飂‚¥ãà³jcÁãkñÖ¼îtX?²tp÷ü ±Ìv=qr¡ñJÀ“*C—G qíûìæñ1¹Ð䃆GÒ!ŽÝª¤ ¬äZ²a\ÿ‘,‹– 1耪x ]ª6ë’j¡C²<Ã`Å:9’v²{¨’>gÏO±ò ÕÞ ƒ¾w¦Ò €;Üw zÕsÓoN°<wñ;’î1LvîÁÅŸÉA$ÉÍt]×\O“ì^¿ê“ê¦fO¼9˜æ~¥Ñ3Òi>:šëêZ8ÆKš|69enMö8ýÑÕðGZ׿zûj{[Ÿ‚V•žÙNóÑÑœð´—DR÷³+~DÒq-¹h½"W9ã~Tcýô3\‰ô£J @sª¤m\<`&ª¤ ¬o‰f0(Pш½ö‘´•õ=ǤRàåDÒ§ úÛè߸1C$mO¡x9§7S%;æîŠ{}Î0=-Æ¥ýR0juwP¹rú•2û¸%z˜Ã¹6Ü[+†ŒÞ‡^UÒ.|ûîÇÝŒ€+Þ¼H^/x0YZ[ú êÁøDR€§½6€Æâ8׃i‚„j8>ψ7¼Vk2+‘ô9vš@Λ ¥kÁ‡ûX|\<&wk›êèB$}Ta[9½‡zûƒ mƒ~V!•r‡8wæ¶±dBµA^á"POsܪB—=wÛßT—&¶ÉÓDR€.XçøÐèGÃ[…OÐi ’>ä¾mÅîÚ:Ô5زàp¾¹çýLoª´PåÒsŒ%툡QðÚø"9ünJ×ÊEUÕe:´lºÚû~")@/Þö»tç‰#›È{>1˜˜Ž{€ú+áÏñSV$}ÂÛ p‘]¼%û‰¤}9±í:ÂA[Ú LCsnH$¸‹BéN")@G½`>Úõ"éðlåЖ6ÓМr¨Û9¡øØðVÉ«ÌÆÏÆç ,­l™m<ýd-1Ø·äÞoüÉ3Ù|êS,÷^ÿTUI»£¼À¾iiV¾ÿ_?¸‘…‰—ƒcðàòg°è`þë‰ç>ÈÆŸOü¹/Y>ù8¬ŸJÎ?7ÏDR€¾ø]Ê|mÕG›À¡[y%ƒ×›EÖõ‡Sx*9ÁE:î€[¬Kt›ÙåÐĹnèß^oöðŠ•»éO‹ÇEÔšóG$½ÛÛ_‡ ^eOìó XúÊ÷4äÄÉaŽA6 frºÅMÓN ŸO­ùÿüüÔݳé¸îu¥ã>ËOœ|\×ü×}£ªÌI{d˜ ÈËÖ¯G.ÖZJ¹t÷ª^‹Â§š¼(ÁúÃÙL#uS鋾•&No÷û_øª¦ÚÓO´SM› äF4n^Ý)÷HüÚ@°”d¨*´¬[ÛÝM3?q¨à©õ³Éóñ×Óßw(cI€[êmÜ9Yyéü´{ìGæÑ›ÏOÝ÷.tÜÜK½`“Hz#Ç!€=DÒN9à ^ÎNx‘tx ±Ð–6ÓМInd ß")ÀŠöIïòØqÈh3hK„iÜÑœý.ÝI$í—“`hòè~")@§ü.ÞC$¨Ï¾CDR€»è³ƒ×j~Ñá¸Çý-ž<M³- ´A˜F•æü­YÚ3¥JÚ»9ªñðßš” ÏÚ\$’ÜBT… =å„Ê")@Ö’;·t+‘´>¥˜Ï÷à.˜ÞÄéM½sr¼œtÅ õ7Q%ž†miƒ0ò<͹!‘˜Á‰þ„92è·/eô÷"’Ô7Ááú±3kÆÎ òI+{~È—#´¥ Â{Ž›;›X<«ä 9UsnH$­ÏÖ §K›§çŸ›ÕÎËÝ;|·%’`Ž1"Àivô¦bióô÷ÌÏ›‚ËŽÒŠH üÏÝ¥ÍÓ«qt‰'®ê‚km Is[jùV³…Ê|ðÂÜ©DìÑaEÓn“O‹ÒæN×h9šЭ>w›U(mž3n 8øNcÈHúZɆ§)B[ÚàM”6+š&€ÞÍÇÒH:ŒßüýQxLЙëî\Oü™œrs>…É6g¸^Õx‚¸s6×WKs\äe¡´y+”‰¤°×úRá˃ÁÅÃ×O% …SFróɽ|™>Ž)ñƒëè,%˜x¦ÜÃó”6Ÿ'€ò5úÞ[$ÞÐÛß –f¿gÅž/èôxŒëW©´ý\—û™1¥Í ·š» wN$…½–Bc2ŒÊõÎÏ…_\£=*ùF y3¥Ín  eäLDÒ:Î_"è^ß©<¢ôÜ:\œOüF*®!]Qڈʛ‰¤ÕØq¼ÇÎþî\XâàÅß÷×79÷CSÚ k"éHˆ¶öøqY4wÒ_èzÌ@²¤j+ê_ùîJßÿøÇ"€B™#Sã…‰†rƒ8ËQÚ¹¥Í¨‘\¤ö\sÊE êÚl¡×K›v£È5@*ÒC ¯zWDRªSSÎ3£6íú¤Á¶¢EÆý@tÜC§ݧLiÿù^¾µ—@éÖ¸›¢HZAÛ_$ãþ‚\/mn^ð‹Ñ@Ý^î:‡Ô†DR`N³žî4Ç»@n/ÇèDÒÁhðq­Mê4€üº½cIŽÌZÚ¤óÐÅï[o/'¹NC$•Èpæ.mj’š8€&Å$ÞÃíåè„H:’äŽfî=,CxyisÖ÷5œ·МUÙn/W±PšœIaxnAyǘ Ùñù^W.hœ7vQݹÅDÒ«ž,¬€Ã3æ.m2.4°ÿ`—Esg Ýq€[H–TÏ-ô¾îûäzž ʹHW8ß+bqè|¯Š±!ùÅÕ%’I!×½¼´ÉXÐ=’)™l çà'ÏšÏ=/=y9§ÜÙý¹§ñE ö¯FÏ®ĶCfŸ¹èH:˜xƒ´er+¥Í'=ßßp>G° Ïýf«(ÔÉvþ¹ÊÏî …/îú Ç/¼Þ.’u͸º™;?,^úÅó½’'{ÅËÅk²Ì6þ¬ÀÑu>M$…‘(mt#Îñ]+‚§Ï/¨ú¯µÍ$‡@,O^‡xÎåx6ùùï’±ñ[XÿùðXA‘z¡´9®i¢á3®Ðܹ¹¹%‹^ñ”›ó)L¶9Ãø„•à:a…²uÒj½.\\ŸŠi;Y ía'&’ާâhež¡´IEñèºÎU¬€&‹7A ),:WÓ*Ì'÷òeúdm,wÂJ¼”B™Šî> §¢ÜÖo*u—õJ›b"é%Í¿?šSÚ„µgºà—ÐϹ}j —Ø•LLi“ýÞôÓ±ûüÐBÈ Ò›çæ“\Å¡YÅ!¦0r ù—;d΋ŸŠŸÝ?ÿ7|Yå†S…*é%oØ §¤´ÉÐZÞé!6;(–G”ž[‡‹ó‰ßHÅ5dmçkŸ¿Ç­[¦HÊT”6¡–hÎÎþîÜa‰ƒë=×?÷‹ãéŬÝW|ßà­oS$ÞZ—Ò&}jÒ«J{ ‹ýu\ÍÛqÇ×—ìâ λêðã}Âwqô«ñ=V$’ÒžÒ&¤pþGrÊåÁä sKOM~¤¹V‚7R^®+ígpöËÙâ‡×ùnKi“éuÞ?#ж|n8Ô;_¥+ŸÓTI9IiºUåž²V¹>Y ­Ûp÷6ZñÜ2à„äh6îóL—½ˆy«^"iR®Âºl·žRJkýüúdRoaMª;Z¹ÙùBx¹ |²9r ÅžÃ÷Ç`Ök.}v{ÆYæF‹Æ'¿æ\Ï~næÉ¡ë?7_,´ðÂõ{O¥ Ö<þ”rƒVs ú¤<Ù”W!VÞww»¯>U>[ã¾ÓNÞéö4ó’_ qx}~ÑÍ ¬¼“-ª>²N›m¥ëŽûá4eÒOã1€w*MŽ+ú¤©6tï¯A`E&¦w¥//9ШâYŒ¹L;GCÅ3 æ\±Gµ° KQ›ƒïq÷VIm%졼ÊôâmuçÔä™{¶üä0÷äkã)ÿ^59Xtîºx¹uØ\Õ=NŽ’W`:î‡7ý·ðî”WéÁ¹6X(+ÞäJ@¬x¡½ö‡`¸ÈÖÕHÊÀN—W7_ M$‡¥®·äò¥9ʵØs¹y©­î9 ¹0A°&÷ªÏðçÚòè™9‘”iÀ肊àºó:w]¼`ëÍ]GïÐ:Äs.ÇÓx°AðÈ:#Fœ[çøê<…Âê•u5ˆ¤‡®`zèò¥›—öüHüÇx†äÔe£-_FñÊâj•£’ÕÐê×ñ©5+F´¹2Æ'?€ûй}É•)Wès3Ü{roªlÿ`žg|·GÒä¯öä/æä`ü“­'.œj?ÅÆ0œd-3.¦’%ØÏj#¯˜sÇ‚å?U–س&L&'6I ‡Îí æðù{ìH6½Ü¯µOªçQîíÇ3I¾©ägU«ynjßqŸëoŠû‰vþú_>Êøã›r74å›ê™ñîûfã¡É°˜,®äº˜‚Mt}`+W\‚™äþ\æ¶^J0ÿ`ß^XÉ=Â`rk•{#ûĸö¹É°q®— câY•ëqU$ç–[D.;=ÐF*DÒ`/W¹¯¿x¯ï:×?¸íYh¥°í)¯rT2º•'+Ÿ'›ù2Ðe_Gyn7M\]µŽûõæ{´Z™ûÜwΡb9:tº¼ºùZF÷|(\붘ZWn¨_rAá,ˆ`àY0ñž£Xrqüx0˜-˜Ãç5çÊlÚŒ+×3÷ÆHÅO#9·C‹¸>‡ÓªUI×t.¤.~þ6§õ½~mТâÉâuXO,®‡Õ@+SŸqñ«¡$Ãn.Ç¡ðÊ ’sË-"Wç»õ ljO><¬sK¼Îqa;Háå7’Œ1Áãñ.i*^í=oj3&Å¿m6çðL+®IãŸYÉg ¿øòÂãð…í_y•;ÌZL½~x^BÆú@¹¿[<9Ãý ýÌ{®L2—''+Ìaùaå ~‰ ëÁŸåðèÇ»?S5q)’æ:€þ)¯òŒ9Š©CdÐ_¿<{ßa´Ð1¯FÒ¡·ÿÂ`ÐùÖ;Š gÜÿãûhÅ'Ï”W÷ëí-çÒ^²ü“UžÃæ”›Æ-¦^_¥s÷—Ôxbn7M|EÅ´ÃMî=.ERß¼Ðéòêæk9aó|”%Ž”G–'[OœL±…ºÓèÅÔä¹2ëõw®Ì¹9ìôpÐç¾?à9ÆT·óŸ “›fÏ<ï>¦àùbêfu9x6x*WΕœ÷ÿäKvúÇ‹[§åOoÿ/ŠÜ:ïœÃMß—H::ßßð4Bæ0nyõî6Xˆ2¹s4’æÌ<8Û·°¸‡ TLÝc²·ó¤ë›bóùÍÚßãàc<@F¡r§ÌÓ §Ýºéû1îÈÔ¤QÖ³Cºï‡&’ò^›}L›§€$ûÈî+¿çµ”åúûr‹»ÃéÀ:ÙÁéùÃíæàÅ>>2•Ó~WÛj½.#’ò^¹³=6ÏùüMiÁ¹ÁÄ{Žß{·XþŸ ˆë]püªo*9“ä›J~VËù1…wz“ÂB'(¯Æ¿ŽV+÷œ³ùòþcè¦ÉŠ©ät[ΧL$…ÿwqÿuî° »¹‡Â++HÎ-·ˆdĹc­.š¦¼ºþu‘ ©ËŸŸ¿omýëbýÚàçMTðòZVoÖ‹@Ù“ÏáÚÒyÀ•XiûÜOsnHÇ=ÜN©ÊDRØ&S­¨x™z¦J ÀdJ˜˜H ÀCdJ G$žÓ¡­·µA§~3±·5箈¤ï¢T tH$ŒL Ra½H$xšL I 2åш)Sˆ‚Û..ÿ_ ¥ë;–oO<ûæƒH:¼7o¾pš:%+7ío|Œ§YhðTJ×7µnyo—òIq9õh¥âÈÑur­2ÃA‰¤@3J•À¸‚núæóÝ«ßü^¾Ó–LùÑa"åæô³'ÿ ž ”Ç•~fÙ+ž£J ¯&SìÍýYØ7Úm戤06™€ ˆ¤Ð˜L ")TàÔo¸Â¨|ø|”* )UR&!SÀ¸DRz!SÀk‰¤ÃË]Díùk%Ê”¼“ë’Â44ç†DÒÙ\¹a®L 4!’N"N“'ò¥L 4!’Î`>ÅJ`8"é Ö1T<†c/ýÓzx;‘€ÆŒ%ÞÅ+7 [Ig ý@C®­ ÓPåiHÇ=‰¤4&’ИH p‰¤×‰¤4&’ИH p‰«Æ\'’ИH @c")‰¤4&’\⺤׉¤4&’ИH p‰ë’\÷oëøÿT÷—™ yë!›???ÁŸ;´¼*·ˆàñä8ÑõÒ¿ÓÇÄ *¼µÓk0 ‘hlÉsë¶<?U¬ëé¿©®\“‹^Ì!9“Â#ñúçÖ*ùHyMf¢ãèÅæâì?ç×òôÁƒ;_’³Lã™ç&¸¸&sP%['ÑÍ4–¬›–§_/èЊ%K¤¹¥ÇåÏOþ­%ëÁ…õT"¦'’í}è;홾«ª¹÷ TçÚ~xÚ´¥ Â44ç†tÜИH @c")‰¤—ypH @c")‰¤—¸‚=Àu")‰¤4&’ИH @c")À%®K pH @c")‰¤—¸.)Àu")‰¤4&’ИH @c")À%®K pH @c")ýÛzÆöóó£ï¦\X«¯N$H[’hA%Ôêü¸ž;Ç@-ë\´¿ä{,–©®P%½ÅÃyÚ4Ô¤ãÞh¸ÃéÚçwJÁô ‘ô.À­ìd 7K0Ýl›¹ì›ìù\çÝøÏOˆw6H®@C"éðzØŒàÍ´A˜F•漄ÂÂܾӬ“è÷ÿÁãŸÿräòà:nS®—›˜k]ïIDAT§Ïõ*%Ñœ‹@Õ×ÉW ÌÊN:÷ûû›Ì…çfü¹¶;§ìäwµ*)@3qGÜ)¿®€îLëðú]Äz¶ñ"šS%­ìùÑ]ê%ÐÖÃmÐR¸OÝæ¼YÑüùùÉ £ç¹Êknúä"ÚInQ‘K(,ÇC3L¾¼ü;¶ŸT*’Ö§†ÜÊNfRN¥ËéM…gãÿÒIw«ýZ}-œ ­&Ãu´z¸ÃM«0Z´œG—irC?““Šg¾Y[ía—âô&€%/ØL°9‡ý'gÞCè,Óq?¼þ72˜›6ÓМIhL$­Ì/àVv2À”DÒáur¢¼–6ÓМIîUëþ¢IkÒ¡ÜÊNæ#ª~‰¤-I¥‘´"Õ àVv2ÀÄDÒá9DA[Ú Lãùæ¬>ºIë°I·²“¡éåØ$’VcSne'“‘S×DÒ ÚnRj'ÐÖmÐq žáÚH Іœ ‘ô*p+;˜ÕÒ´µñH p+?,÷I/±‘·²“^âßÖ+ð?ëÅßýï÷‘õ=a—ýrnôñú…ÁK’Ë ž ^X^J?z[xm¦qGsöÃr§Ž>¦ø;[YâiðTð’õô¹÷µ~jóÿ¹ rTd'£+·bm|m˜ŽûBÙr¿à»_æ™{<÷B*&’~2©ôg¥bj\G“sî'¤ºˆ´uSìg'ïQ·9kŇŒI?©TúûŸû–ø‰¶Qp+;Ýf+Ö̃EÒ¯ä˜*=ûIÉT «Þsû}EÒuì;zÂ{!2®O±ñÛfœ]§RÛp+;×·ýn6aÍ<ÖËE >ÿåÂÜÕ Ö_ÞzÊÂô…¥¬§ÿü=óiýrµRx­Üu$—§ÍÓ„ôclpÀ­ìd +ñ,¹g÷7[Í<©£*é(lCÀ­ìd ñ¿àÙÓ³½´Z3R%=ÀÈàVíd‚îËÜY§É‰ãéËwÚËIÎ-yƒ•ÜÚ&ÿ„œ*ÕÍšùÃú:½‰ o…¶^Ø“'p,O-ÇCùãǃ®§YþŒ?˜\xúäŸ¼Ùæ¹Îñy/Ô"’îåg p«w2KšŒ×|)@N$=±¸Ó¯½¾tøÚsB}ΈÍü1Æ’pÌ:‰Þ}|=wúÈZnmÅÓ7ð-D$ÝÅÏàVÃíd’EУö âÌ ?=$W²×„Þ\üjr›Vn4ó­[ÂpÍüa")'} Ïœt|ýX~qm9ç¦LyqMlI·u¾ ëyÝà ®·ÁÎw2±ý+¤À;Þé2Ï`A'Ö–¤~2å åkAzêúšØË|@ÛlFÀ­†ÛÉäªM¹ŽøòE öt­–ûU—H?˜œÏÛ.5t¦áщ¤%6qàVv2O’)‡³Ìq·ßŽ6¾ŸH ÀdÊ78t©&o&")‘)‰•{䇿 ‡øy‘å·p«Aw2Nýæº*W­ï¿õ¿†]Q%x¥Jš¨{ó¤þÓ^ÿkØ‘`02%C˜¸Gž;ˆðiý¸hUaJ'Ú Lɬž¹ü}¾Zsvh>A•à° S˜ÁßahÏÄÐõâ´)‰¤ÀU¬S:@ò6zäËìÎIlL0„qOý¶“a8—By!‘hÆJè™z‚œ§‰¤Ã³éÓLù™å]ÀGü+ßr?DRx5™^n¬R¨äÄDÒͱȔñ“¡¹±bè@´î+DÒái£“)GW± ÆAáûÈ÷¢QëÇ?ù-gýÂà%ÉeÏ/,/…Qè‘ßÉ!µ!‘*÷Ôoº_¯t …q:\&^òý³|éÓõS…ÿÇ«ä€=¥Ð‡i‰¤ðù(U2ˆ*רO¦Þ\vÖ±ˆ¡ŒK$e2%/‘L¥7‘¸bZkÎÔò¶yw>›˜HJ/dJØ)YÑüþçb;*/Qè„Rh‡´ŽëDÒ?FܤöŒ{†LÉ;Úto:j½27µ}©´-1ô>Û†DÒÙä΢=ôÚs´d¨">Ç(–;¾ÐŠ—ÙæÎXÚŒ³Ré“ÞÖ#ö,Œ»«­Ò[7è{‡ìÙÉ$³Hò×f®PZ~*x­‹@õC)tÔ®Òˆ|ˆŒ¸Uù% mÚoŒ¸“áVbè ÷µ£ssÖ®«Ðq?ƒ\ÕD è:$‰¤³±wèbA]]] *·&nÆv”H õ‰¡·ê*•&¹ÛQ")T GžCÜŒ- ’¯·M ÞF|3¥Ð†î(”>ü%ºÛšH ˆ¡ýhÞ}}énƶI D|ÏÖ4[¯ËynÆöù|þi½\uÓ¯(`'mpJ?+¿µ^5BË @×gU½9¯gxô„÷òÍØ–׋Xߌ­°b…{j´¢J ŸùÁÊ¥Wnµ}}•rWƒZ)×S¦/,e=}üøúÏ`>ýÔJER^JüdrWñÌÎ|àO.bÿƒ›O•'ï| Ix¥ÐéåîhX˜Œˆ¤LN ]½¹Ók’ÇÓoÞ¾()9·àFGåµMþ™[±êgAõÓå=‘txÚ´¥ vH|Ù’¥’Y0x*y¿Ÿ=÷Ê]ž=ž8wi÷ÜÚÓož™¾?ŒÚTI˜Rè K˜ËÕ7ottq§WõÊÒmCICêðÀ­ìd*CÏYŸœ~÷çvý;Ê­mW0â:‘ôZ6{’ôu¨ ޏ“éŠùZ’EУö\\)7üô\É6^“‹RIèšRè}žìˆ¿þÝ]\[:'’Ð1ôVûƒÝžÓ˜j­L° kËÐDRÚÓ#ÿ°dŸ{|W¡Oææ@ëû徬ä4¹ånt”\Û`=“«ÍpDRÚP meçÍ~ Ç$SæþE\Yºg"éð4EhK^¥`ú¡kÞБtxv…ÐÖmðùù%Ìå.„žLŠÏÜ=ùrÑó |Å ‰¤/Õd`èRƒL†Ñº®¿ÁÜÚn à%üP©H$x‹NΑOAÚ3ˆ37üô\É6^Þf=ƃëDÒáù‰muÞ»=GþÉŽøëo\ÇýKø–IfÓm ý9äï9©ÖÊ :±¶ÀE")Àð:é‘ß)Ùçþ}°pò{ƒK5%‘¼œSÜÓú]J.Ç'¯úœ‰/¹ÂuZQÚ@û—V¦t® ^o¹=—B–L™MÖ„Ñm˜Á©’ C MòQÀDÒáÙC[·¶Á±zäatšXC")@_”BIÚC—I‡gl5´uº Š¡Ð‡Ô†DR€ú’6Ca2î¡P‘H PSòZ˜_îB #’¦ùÝÜõçûŸÜnÄN`M$¸Ê¨P€‹DÒá9þA[Aw¼x ãÒfIªq<8çŸÖ+ÀÛ‰¤ÃsÞ.´¥ Â44ç†DRI³¾—hi½À´ìd`r-"i‰3€[ÙÉ|‰¤4æÞ!ç¹[ª¤4&’ИH:<'úA[Ú LCsnH$ 1‘à<—&­B$ 1‘€Æ\I à—&½îß¶‹_½ø~—{¾ÔòˆõË“óþ¿óÏä:ÔÚþêÎ hÎÎ펹kê×;è£?/‚é {½xÎßG–Ç «Q^«Z?‰’kŒËÎ-˜Ó³‘_7ÉXÒ+ÛAÛåÖñ¹5q–´uk|óÎ žgCm¨YÇý÷[ ë_ÕëéãýrÜouȲ‹üýýÍu‡Ö¼°&Ël×½cñ«üœ‚)ٹٹñBë ÎiI7¿¼`‚xâõS§×!žsy¬RüÈÒq¿… CíÜ:³s;·Î0:yô¢:î/n{” ç eç¼Ç ‘4i½¯Lþ¿âÎ4Ùoµ>F=õعSj¶7Ù3)7 *~<¹ Îu~åf¾^¥äŸ›/ Zxáú½ï? èŸÛÇÎ 8®ë}ÄKvaê ð6/iìvnÀ~ÓvÜǨ,`JvnÀýþr º–ÈQ~€¶Ž¶A;7è–CjCo(Z`›¦dçÓq@c")‰¤4Ö2’ž8 Ó™›1ãÒ ­òMêw²sƒ8¤6Ôi•ÔÞ˜’@R§‘€÷èâ"PÉ›Ý}¯ _ÀÏ%ý.¢mÚ ŒÅ!µ¡."i|Ûåe—o…û>tÅÎ `§."éÇ^˜”ŒnÝŠ—ŸŽ›ÅÔrÛ_¿<9ÿOê7íæŸÉu¨U÷­;·Xû±¤???׿Z€ÞعÁ¾­økypO,ûNó»²<¼|=e0ÿeéñÄÉ?“ëPŲ&÷í²šEÒeLUòÏÏj¸Õ²[_¦‰'è„t% Þš7­]ß·&Í:îË_LüCaó%¯åc¶’͉íÜ CAc\ÿJ ºÚ“ƒ¿ËW)>ÑÒ—üÔJ7Ca ³ù5YuÜ$Ä··X‡Åò}.âq¨…ó—t¸~$9ó #>þ3y^|0ÿä€äJ>™SÝ¥`xÍÖÀËiƒ0Íù%{€Š©:îØvëÀ‘àŒ÷\K8¾žu¯¨6Ð3URIhL$Þ†°@Ï´A˜†æÜH v"¤Êµçˆ¤‡‰žu‰¤4憢 7öŒoÔ™¼u'e"éðlîЖ6Ó(4çuýüw×øåßòÄì¡ã`›ˆy+‘ äçç'Y '{f}¦ä†¢ÃÛÓH€ûhƒ0 9ç"æº_>K¿Äþa'cIÊiryv=™zšŽ{IhL$ 1£òhL•€ÆDRI‡ç¼Ж6ÓМIhL$ 1‘€ÆDRs]RS% 1‘€ÆDÒṈ´¥ Â44ç†DRIhL$ 1‘€Æ\—€ÆTIhL$ 1‘tx.¢miƒ0 ͹!‘€ÆDRIhL$ 1×% 1URIhL$ž‹¨A[Ú LCsnH$ 1‘€ÆDRIhÌuIhL•€ÆDRI‡ç"jЖ6ÓМIhL$ 1‘€ÆDRs]RS% 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ 1‘€ÆDRIhL$ ±ÿÙK~ã'¿IEND®B`‚‰PNG  IHDR†<"7"ý pHYs M MÒέNtEXtSoftwareESP Ghostscript 815.02—v ¹IDATxœíÖ1 À0À¿çMF={gæ@çÕüÎ’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤Ä,)1K @Ì’³¤ÄÔûu ÌþVIEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/design/SimpleFlowControl.txt0000644000175000017500000000777011366547366024552 0ustar twernertwerner // Version: $Id: SimpleFlowControl.txt,v 1.2 2007/01/05 15:51:58 belaban Exp $ // Author: Bela Ban Simple Flow control (SFC) ========================= SFC is a simple flow control protocol for group (= multipoint) messages. Every sender has max_credits bytes for sending multicast messages to the group. Every multicast message (we don't consider unicast messages) decrements max_credits by its size. When max_credits falls below 0, the sender asks all receivers for new credits and blocks until *all* credits have been received from all members. When the receiver receives a credit request, it checks whether it has received max_credits bytes from the requester since the last credit request. If yes, it sends new credits to the requester and resets the max_credits for the requester. Else, it takes a note of the credit request from P and - when max_credits bytes have finally been received from P - it sends the credits to P and resets max_credits for P. The maximum amount of memory for received messages is therefore * max_credits. The relationship with STABLE is as follows: when a member Q is slow, it will prevent STABLE from collecting messages above the ones seen by Q (everybody else has seen more messages). However, because Q will *not* send credits back to the senders until it has processed all messages worth max_credits bytes, the senders will block. This in turn allows STABLE to progress and eventually garbage collect most messages from all senders. Therefore, SFC and STABLE complement each other, with SFC blocking senders so that STABLE can catch up. Variables: ---------- - current_credits: number of bytes available to send multicast messages - lock: to wait until all credit responses have been received - credit_responses: list of all credit responses - received: hashmap of bytes received from all members for multicast messages. Note that initially, all members need to have max_credits received bytes for all members, otherwise new members would not send credit responses ! - pending_credit_requesters: set of senders which requested credits but from which we haven't yet received max_credits bytes On sending a multicast message M: --------------------------------- - acquire lock - if no credits available: - wait on lock until credits become available - else - decrement current_credits by length of message - if current_credits <= 0: - add all current members to pending_credit_requesters - send credit request to all members - wait on lock until credits become available - release lock On receive(M) from P: --------------------- - if P is not in received: - add an entry for P with value of max_credits for P (see above) ! - increment P's value in received by M's bytes to new_val - if P is in pending_credit_requesters: - check if P's new_val is now >= max_credits - If yes: - send credit response to P - remove P from pending_credit_requesters - reset P's value in received to 0 On reception of credit request from P: -------------------------------------- - check if we received max_credits from P (received map) - if true: - send credit response to P - reset P's value in received to 0 - else: - add P to pending_credit_requesters On reception of credit response from P: --------------------------------------- - remove P from pending_credit_requesters - if pending_credit_requesters is empty: - set current_credits to max_credits - signal lock On suspect(P): -------------- - if pending_credit_requesters is not empty: - remove P from pending_credit_requesters - if pending_credit_requesters is empty: - set current_credits to max_credits - signal lock On view change(V): ------------------ - for each member L which left: - if pending_credit_requesters is not empty: - remove all Ls from pending_credit_requesters - if pending_credit_requesters is empty: - set current_credits to max_credits - signal lock - for each member J which joined: - add an entry for P with value of max_credits for P (see above)libjgroups2.6-java-2.6.15.GA.orig/doc/design/Multiplexer.txt0000644000175000017500000003637711366547366023447 0ustar twernertwerner Multiplexing functionality =========================== Author: Bela Ban Version: $Id: Multiplexer.txt,v 1.18 2006/07/11 12:36:37 belaban Exp $ JIRA: http://jira.jboss.com/jira/browse/JGRP-119, http://jira.jboss.com/jira/browse/JGRP-112 Overview -------- In JBoss we have multiple JGroups channels, one for each application (e.g. JBossCache, ClusterPartition etc). The goal of the Multiplexer is to combine all stacks with the *same* configuration into one, and have multiple apps on top of that same channel. To do this we have to introduce multiplexing and demultiplexing functionality, ie. each app will have to have a unique application ID (a string), and when sending a message, the message has to be tagged with that ID. When receiving a message, it will be dispatched to the right app based on the ID attached to the message. We require special handling for VIEW and SUSPECT messages: those need to be dispatched to *all* apps. State transfer also needs to be handled specially, here we probably have to use thread locals, or change the API (TBD). When deployed into JBoss, the Multiplexer will be exposed as an MBean, and all apps that depend on it will be deployed with dependency injection on the Multiplexer. Of course, the old configuration will still be supported. The config of the Multiplexer is done via a config file, which lists a number of stacks, each keyed by a name, e.g. "udp", "tcp", "tcp-nio" etc. See ./conf/stacks.xml for an example. An app is configured with the name of a stack, e.g. "udp", and a reference to the Multiplexer MBean. It will get a proxy channel through which all of its communication will take place. The proxy channel (MuxChannel) will mux/demux messages to the real JGroups channel. The advantage of the Multiplexer is that we can reduce N channels into M where M < N. This means fewer threads, therefore fewer context switches, less memory consumption and easier configuration and better support. Design ------ The Multiplexer is implemented by extending the JChannelFactory class. When an app wants to fetch a channel, it calls Channel createChannel(String stack_name, String id) on the factory. The arguments are the stack name (e.g. "udp") and the application ID. The factory then returns a MuxChannel (a subclass of Channel). This channel is the handle for the application to send and receive messages. There can be multiple MuxChannel instances per JChannel, but each application has only 1 MuxChannel. The application can choose between the pull or push style of receiving messages; it can call Channel.receive() to pull messages out of the channel, or register as a Receiver, in which case messages are pushed to the application when received. All calls made against the MuxChannel are redirected to the factory for multiplexing and de-multiplexing. Each JChannel has a Multiplexer class sitting on top of it (an UpHandler impl). This instance maintains the mappings between application IDs and MuxChannels and, upon receiving a message from the JChannel, looks up the correct MuxChannel according to the application ID stripped from the message header, and dispatches the message to that MuxChannel. TBD: we may buffer messages internally in the Multiplexer before dispatching them into the MuxChannel, should be enabled or disabled via a configuration option (or programmatically). This may be desired if the application registers as a Receiver with the MuxChannel; a receive() callback taking some time to be processed would block all other applications from receiving messages until completion. The configuration should be per channel. The buffering should be handled in the MuxChannel; MuxChannel.up() would then simply put the event into a queue, or - if a Receiver is configured - directly convert the event into a callback for Receiver. The subsequent sections describe the various use cases, e.g. life cycle for channel creation, message sending and reception, channel close/disconnect, view reception and state transfer. Configuration ------------- Configuration is via XML, example is ./conf/stacks.xml. The file defines a number of stacks, each is given a unique name. This name is used in JChannelFactory.createChannel() as stack name. Structures ---------- In JChannelFactory: - channels: Map. A hashmap of channels and their associated MuxChannels. The keys are the channel stack names, e.g. "udp", or "tcp". The values are ChannelEntries, which contain both the JChannel and associated Multiplexer. The Multiplexer maintains a hashtable of application IDs and MuxChannels (Map), see Multiplexer below. We need to keep the stack names *and* channel names, because multiple applications residing on top of the same channel stack could be connected to different groups, e.g. Appl-1 uses the "udp" stack and connects to group "group-A", whereas appl-2 uses the same "udp" stack, but connects to "group-B". Since a channel can be connected to only *one* group at a time, this requires 2 different channel instances. Only if 2 applications use the same channel stack *and* group can we share the same channel. We do provide this flexibility, however, we expect that all applications sharing the same channel stack will use the same group name ! This is not needed, as "udp" as stack name implies that all applications which use "udp" will use the *same* stack and join the *same* group. If an application wants to use the same stack, but join a different group, it has to create its own JChannel and cannot use the Multiplexer mechanism. In Multiplexer: - apps: Map. A hashmap of application IDs as keys and MuxChannels as values. Used for dispatching incoming messages. The Multiplexer implements UpHandler and registers with the associated JChannel (there can only be 1 Multiplexer per JChannel). When up() is called with a message, the header of the message is removed and the MuxChannel corresponding to the header's application ID is retrieved from the map, and MuxChannel.up() is called with the message. JChannelFactory initialization ------------------------------ JChannelFactory has a reference to the configuration which is set either on creation (e.g. via "MultiplexerConfig" JMX attribute) when used as an MBean, or via programmatically calling one of the config() methods. The JChannelFactory MBean has to support the JMX life cycle methods (create(), start(), stop(), destroy()). On redeployment, a JChannelFactory re-reads its configuration file. The method to read the config file (config()) can also be called via JMX to force a re-read without redeploying the factory. When a JChannelFactory is destroyed or redeployed, all of its dependent MBeans (usually all applications) will be redeployed/destroyed as well. We recommend dependency definition/injection between application MBeans and their underlying shared JChannelFactory MBean. MuxChannel creation ------------------- When JChannelFactory.createChannel() is called, the following happens: - If the JChannel already exists: - Create a MuxChannel (only if the application ID doesn't yet exists), referencing the JChannel, add it to the Multiplexer's apps hashmap, and return it to the application - Else: - Create a new JChannel, add it to the channels hashmap - Create a new MuxChannel, referencing the JChannel, add it to the Multiplexer's apps hashmap, and return it Note that a newly created JChannel is not connected yet. MuxChannel connect ------------------ - Calling JChannel.connect(stack_name). If the JChannel is already connected, this is a no-op. The name of the group is always the name of the stack, e.g. "udp" or "tcp" Sending a message on MuxChannel ------------------------------- - The MuxChannel adds a MuxHeader, with the application ID and then calls JChannel.send()/down() Receiving an event on Multiplexer (up() method) ----------------------------------------------- - The Multiplexer removes the MuxHeader (if present) - For SUSPECT and VIEW: pass it on to all MuxChannels. - For messages and state transfer events: fetch the MuxChannel associated with the MuxHeader's application ID and pass the message to it - A MuxChannel can be configured to use a queue when the pull model is chosen (no Receiver present). In that case, up() simply places the event into the queue, and MuxChannel.receive() dequeues the event. The queue can be bounded. If a Receiver is set, we can either directly invoke the callback, or use a QueuedExecutor to queue all events and invoke them in FIFO order on the Receiver. Note that we *cannot* use a PooledExecutor since the delivery order might be destroyed by a thread pool. MuxChannel disconnect/close --------------------------- - Remove the MuxChannel from the Multiplexer's apps hashmap - If the apps hashmap is empty: - Disconnect and close the JChannel - Remove the Multiplexer from the channels hashmap in Multiplexer State transfer for *all* applications ------------------------------------- - If we transfer the state for each application separately, we have to run the stop-the-world (FLUSH) protocol N times, once for each application - This could be improved by fetching *all* states after the last application has been started - It could be implemented by adding a getState(Address coord, long timeout, Object state_id) method to Channel: (same as partial state transfer) - the state transfer event would have the state_id as parameter - a new method Multiplexer.getAllApplicationStates() would call JChannel.getState() with a state_id of "ALL" - when the Multiplexer receives the state event, and the state_id is "ALL", it asks all the registered applications for their state, and returns all appl states as one state. That state would contains all substates, prefixed with the substate ID (which is the application ID) - on the receiving Multiplexer, we demultiplex the state into individual appl states and call setState() on the application corresponding to the substate ID - this has the advantage that the digest would have to be transferred and set only *once*, not once per application - Actually, transferring all substates as *one* single combined state is a bad idea, this might become too big. For example. if we have 2 substates A and B, each 500MB, and assuming we have to generate a byte[] array for the serialized state, which is 500MB as well (simplified assumption), then if we combine the state, we have 500MB for A and 500MB for B, for a total of 1GB. This 1GB byte[] buffer can only be discarded once we have fragmented it and placed all fragments on the wire. If we send the substates individually, then we only have to generate 500MB at a time. Preventing multiple FLUSH phases -------------------------------- - When we have multiple applications on top of the same channel, each application will call MuxChannel.getState() after connecting. Let's say we have 3 applications, which all require state transfer, then we will have 3 FLUSH phases when starting. This will slow down the startup of JBoss - The goal is to reduce this to just a single FLUSH phase (the use case is JBoss) - Let's assume there are 4 applications that reside on the same JChannel. 3 of them require state - All 4 applications are MBeans which have a dependency to the JChannelFactory MBean - Each application which requires state has to do the following: - On create(): register with the JChannelFactory for state transfer. This basically tells the factory that application X is interested in state transfer - An application must *not* call MuxChannel.getState() after it connected, but should rely on the JChannelFactory to push the state to it when all applications that require state have connected. As an alternative, we could simply ignore MuxChannel.getState() if the state push model is chosen. - When all applications that registered for state transfer have connected (we can find out because all MuxChannel.connect() calls go through the JChannelFactory), the factory tells the Multiplexer to fetch *all* substates for that JChannel. This is essentially the same as fetching the entire state, but the returned state contains a list of pairs, so the Multiplexer will call setState() on each application NOTE: this design doesn't work in JBoss as the dependency management does *not* guarantee that all create() methods will be called before all start() methods. Currently, this is disabled in JBoss Service views with the Multiplexer ================================== Issue ----- When switching to a services-oriented architecture, where multiple services can run on the same JGroups channel, then we need to adapt the notion of 'view' slightly. The problem is that if not all services are deployed on all nodes in a cluster, certain services might run into problems (JIRA issue: http://jira.jboss.com/jira/browse/JGRP-247). Consider a cluster {A,B,C,D}. If service S1 is deployed on A and C only, then - when that service wants to make a cluster-wide synchronous call - it cannot wait for responses from B and D because S1 is not deployed on those 2 nodes. Solution -------- S1's view should therefore be {A,C}, rather than {A,B,C,D}. This is called a *service view* (as compared to a cluster view). A service view is always derived from the current cluster view, except that the hosts not running the given service will be excluded from the service view, so the service view acts like a filter for the cluster view. A service view is always applicable only to a given service, e.g. service S1 might see a different service view than S2. Design of Multiplexer --------------------- Data structures --------------- - Every Multiplexer maintains a Map>, where it keeps track of which hosts are currently running a service. This is called the *service state* New JChannel is connected (JChannelFactory.connect()) ----------------------------------------------------- - Gets service state from coordinator, blocks until state is available (doesn't block if coordinator) - Loops until state has been received, or no members available New service S is started (JChannelFactory.connect()) --------------------------------------------------------- - Multicast serviceUp(S,H). The message includes the host H (JGroups address) on which the service was started On Multiplexer.disconnect()/close()/shutdown() ---------------------------------------------- - Multicast serviceDown(S,H) for the service whose MuxChannel called disconnect()/close()/shutdown() On Multiplexer.closeAll() ------------------------- - Multicast serviceDown(S,H) for all services currently registered with the Multiplexer On reception of serviceUp(S,H) ------------------------------ - Create new service view SV (based on view V and service state) - If service S is affected (hosts joined or left): call viewAccepted(SV) on S On reception of serviceDown(S,H) -------------------------------- - Create new service view SV (based on view V and service state) - If service S is affected (hosts joined or left): call viewAccepted(SV) on S On view V --------- - If coordinator: - for each host H which left: - multicast serviceDown(S,H) for each service S that was running on H - Else: - if viewChange() has not yet been called: call viewChange() in all services which have hosts in V On MergeView ------------ - If coordinator: - for each new host H: - multicast serviceUp(S,H) for all services that are running on S libjgroups2.6-java-2.6.15.GA.orig/doc/design/ConcurrentStack.txt0000644000175000017500000001216711366547366024234 0ustar twernertwerner Concurrent stack ================ Author: Bela Ban Version: $Id: ConcurrentStack.txt,v 1.1 2006/12/27 10:02:04 belaban Exp $ JIRAs: http://jira.jboss.com/jira/browse/JGRP-180 (harden stack) http://jira.jboss.com/jira/browse/JGRP-181 (concurrent stack) http://jira.jboss.com/jira/browse/JGRP-205 (out-of-band messages) Concurrent stack ---------------- We will get rid of all queues in the protocols, and their associated threads. This is the same as setting all down_thread and up_thread vars to false, but now it is the default. On the receiver's side there will be 2 thread pools (Executors): one for regular messages and one for OOB messages. Whenever an OOB message is encountered, the receiver thread (which called receive()) will call Executor.execute() on the OOB threadpool with the message as argument, and if it is a regular message it will call Executor.execute() on the regular thread pool. The type of Executor is chosen by the user (through configuration): DirectExecutor means we will *not* use a separate thread, PooledExecutor uses a real thread pool plus a queue in front. Both thread pools have their own instance of SchedulingPolicy (whose main method is handle(msg)). The purpose of the SchedulingPolicy is to schedule handling of messages with respect to others. For example, we could have a sender-based policy, which uses queues and places all messages from the same sender into the same queue. Combined with a message priority, after placing the message into the correct queue, the processing thread could then pick the message with the highest priority first. Other policies: longest queue first, round robin, random, FIFO etc. Out-of-band messages (OOB) -------------------------- Could be the same as priorities, e.g. prio=0 is always OOB Adding priorities to messages ----------------------------- Reliable events --------------- Implementation ============== Message ------- We add a flags byte to a message. Values are - OOB (if not set, we have a regular message) - HIGH_PRIO (if not set, the messsge has a default priority) - (LOW_PRIO): TBD Unmarshaller ------------ Executor (DirectExecutor or PooledExecutor (default)) which - unmarshals byte buffers into messages - performs the version check and - discards messages from different groups When done, the Message is passed either to the OOB or regular thread pool for passing up the stack. The decision is made based on whether or not the OOB flag is set in the message. Buffer pool ----------- Unicast and multicast thread have access to a buffer pool with a fixed size. Before every receive(), they get a buffer from the pool (blocking until they get a free one). The buffer is locked and passed into the queue of the first thread pool. (Ideally, there are as many buffers as max threads in that pool). The alloted thread from the thread pool then unmarshalls the buffer into a Message and returns the buffer to the buffer pool, releasing the lock so it can be used for a new packet. Advantage: we don't need to *copy* the packet (unlike in the old solution where we copied the byte buffer into the incoming queue) Todos ------ - Unmarshaller should reuse a pool of InputStreams, not create a new one for each unmarshalling task - In TP.receive() we unconditionally *copy* the buffer. This doesn't need to be done if - we use a BufferPool or - Remove TP.use_concurrent_stack - Thread naming in thread pool: better naming (e.g. for Unmarshalling threads sender of message that is being unmarshalled). Don't use increasing numbers for threads (?) - Append (channel="") to each pool thread's name - Dynamic resizing of thread pools, e.g. according to number of nodes in a cluster ? - Enable setting the thread pools programmatically - Remove configurability of rejection policy ? Done ==== - Thread pool shut-downs: correct ? Should we use something else rather than shutdownNow() ? - Use of shutdown() rather than shutdownNow() ? - We continue using shutdownNow() - Handle dispatching of messages from Unmarshaller to OOB or regular thread pool based on flags. Currently not done; we dispatch all messages to the regular thread pool. - Add flags to Message - Review which messages should be flagged as OOB - Switch all queues in thread pools from number of elements to number of bytes Created JIRA issue - How are OOB messages handled by UNICAST and NAKACK ? Are these 2 effectively ignoring OOB flags ? Does it make sense to use OOB messages in protocols *above* UNICAST or NAKACK ? Exampe: GMS sending VIEW_ACK messages. Logic in UNICAST: - Receive OOB message, place in receiver table - Pass up the OOB message *immediately*, regardless of whether it is in sequence ! - When message is finally removed, do *NOT* pass it up when marked as OOB ! JGRP-379 (NAKACK) / JGRP-377 (UNICAST) - Check UNICAST and NAKACK for correct synchronization: block messages from the *same* sender, other messages can be processed in parallel [JGRP-378] - Expose JMX information for all 3 thread pools - Allow resizing of thread pools (and other changes) via JMX - Thread pool queues are always showing 0 size although they *should* be filled: CORRECT libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-join.fig0000644000175000017500000001056611366547366023131 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 3037.500 2175 2775 2550 2925 2175 3300 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 3787.500 2175 3525 2550 3675 2175 4050 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 4387.500 2175 4125 2550 4275 2175 4650 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 6862.500 2175 6600 2550 6750 2175 7125 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 2287.500 7537.500 2175 7275 2550 7425 2175 7800 0 0 1.00 60.00 120.00 6 1425 1200 3000 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 1425 1200 3000 1200 3000 2100 1425 2100 1425 1200 -6 6 5925 1200 7500 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 5925 1200 7500 1200 7500 2100 5925 2100 5925 1200 -6 6 10725 1200 12300 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 10725 1200 12300 1200 12300 2100 10725 2100 10725 1200 -6 6 7125 3825 8100 4050 4 0 0 50 -1 0 12 0.0000 4 180 945 7125 3975 get DIGEST\001 -6 6 225 3675 1200 3900 4 0 0 50 -1 0 12 0.0000 4 180 945 225 3825 get DIGEST\001 -6 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 2175 2100 2175 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 6675 2100 6675 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 11550 2100 11550 9150 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 2325 2175 2625 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 3450 6675 3600 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 6675 3825 2175 4875 2 2 0 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 5 2175 5025 11550 5025 11550 6075 2175 6075 2175 5025 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 7200 6675 7500 2 1 0 1 0 7 50 -1 -1 4.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 7200 11550 8100 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 6686 3448 6986 1423 7286 3448 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 1528 3375 1828 1350 2128 3375 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 1350 7650 1200 6750 1650 6075 1650 5850 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 6975 7650 6825 6750 7275 6075 7275 5850 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 11850 7875 11700 6975 12150 6300 12150 6075 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 12525 3150 12375 2250 12825 1575 12825 1350 0.000 1.000 1.000 0.000 4 0 0 50 -1 0 12 0.0000 4 135 795 1800 1725 A:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 6300 1725 B:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 11100 1725 C:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 405 8250 2250 JOIN\001 4 0 0 50 -1 0 12 0.0000 4 135 615 7500 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 825 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 12525 2475 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 7125 3750 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 225 3600 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 135 840 2700 3075 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 4050 3300 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 2700 3825 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 2625 4275 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 3825 4725 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 135 840 975 4875 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 105 540 975 5100 returns\001 4 0 0 50 -1 0 12 0.0000 4 135 1860 4875 5550 VIEW INSTALLATION\001 4 0 0 50 -1 0 12 0.0000 4 135 885 600 6375 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 885 7050 6900 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 885 11925 7050 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 2625 7725 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 4125 7200 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 525 7950 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 525 8175 latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 6750 7950 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 6750 8175 latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 11625 8250 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 11625 8475 latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 8850 8250 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 135 765 2775 6750 RESUME\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 11625 2775 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 11625 3000 latch\001 libjgroups2.6-java-2.6.15.GA.orig/doc/design/NAKACK.txt0000644000175000017500000000614011366547366022046 0ustar twernertwerner Design of new NAKACK with only 1 retransmission table ===================================================== Author: Bela Ban Date: April 3, 2007 JIRA: http://jira.jboss.com/jira/browse/JGRP-281 Version: $Id: NAKACK.txt,v 1.9 2007/04/19 21:01:17 belaban Exp $ Motivation ---------- Merge of sent-table, received-table and delivered-table into one single retransmission table. This should reduce complexity, and increase performance, because we don't need to handle 3 tables. Plus, sent-table maintained a sorted key set (TreeMap) which made insertion costly when many keys were present. Design ------ Variables: - xmit-table: Map of senders and their associated NakReceiverWindows. Each NakReceiverWindow contains a Map of sequence numbers and the messages associated with them. Each NakReceiverWindow maintains the seqnos of (1) the highest message received, (2) the highest message delivered and (3) the lowest message. The latter is the seqno that was last purged through a stability message (see STABLE protocol) On sending of M (sender=P): - M is added to xmit-table - The NakReceiverWindow for P adjusts its highest received message counter On reception of M (sender=P): - If P == local address: NOP - Else: add M to xmit-table - Remove as many messages from xmit-table as possible and pass up - For each remove: NakReceiverWindow adjusts the highest delivered message counter for P's NakReceiverWindow On GET_DIGEST_STABLE: - For each sender S in xmit-table: - Get low seqno, highest devlivered seqno and highest received seqno and add it to digest D - Return D On SET_DIGEST(D): - For each sender S in D: - Create a new NakReceiverWindow in xmit-table with low seqno, highest devlivered seqno and highest received seqno On reception of stability message D: - For all highest seqnos in D: - Call stable() in the NakReceiverWindow. This will adjust the lowest message counter in the NakReceiverWindow On creation of a new NakReceiverWindow with digest D: - Create the NakReceiverWindow with - lowest seqno = D.low_seqno - highest delivered seqno = D.highest_delivered_seqno - highest received seqno = D.highest_received_seqno NakReceiverWindow ----------------- - received-msgs and delivered-msgs are merged into msgs - msgs is a map of seqnos with their associated messages - low is the seqno that was last purged (by stable()) (old 'head' variable) - highest_deliverable is the seqno that will be returned by the next remove() - highest_received is the seqno of the highest received message (old 'tail' variable) - E.g. for 1,2,4,5,7: low=1, highest_deliverable=2 and highest_received=7 - NakReceiverWindow.remove() does *not* remove a message (unless discard_delivered_msgs = true), it instead simply moves the highest_deliverable marker - Messages are *only* purged from NakReceiverWindow on reception of a stability message (stable()) - Note that the highest delivered message is the highest seqno actually *removed* by the application, not the highest *deliverable* message ! libjgroups2.6-java-2.6.15.GA.orig/doc/design/SEQUENCER.txt0000644000175000017500000001305311366547366022451 0ustar twernertwerner Design of SEQUENCER, a total order protocol using a sequencer ============================================================= Author: Bela Ban Date: Dec 29 2005 Version: $Id: SEQUENCER.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ Motivation ---------- The TOTAL protocol works by asking the coordinator for a sequence number for every group message, but each member sends the message itself. This means a round trip to the coordinator, then a multicast. In the design below, we want the coordinator to directly multicast the message on behalf of the member, now we have 1 unicast to the coordinator and 1 multicast. The downside is that all message are now routed via the coordinator, which makes the latter the bottleneck. However, since it is only the coordinator who sends multicasts, we have no contention. Overview -------- When a member multicasts a group message, it sends it to the coordinator. The coord then wraps the message into its own message and multicasts it to the group. The SEQUENCER protocol (somewhere above NAKACK) unwraps the message so that the original sender is seen. Example: group is {A,B,C}. B wants to multicast a message M to the group. It does so by sending M to A. A then adds a header which records the original sender (B) and replaces M's sender address with its own (A). When a member receives M, everybody thinks the message is from A, and NAKACK will possibly retransmit from A in case M is lost. However, SEQUENCER removes the header and replaces M's sender address with B. It has to do so on a (shallow) *copy* of M, otherwise we would change the original message and therefore retransmission requests would go to B, rather than A ! Reliability on failover of coordinator -------------------------------------- When sending messages to a coord, and that coord dies, then we simply resend the unacked messages to the new coord (on a VIEW_CHANGE). Each message has a unique tag, consisting of the sender's address and a monotonically increasing sequence number (seqno). The coord simply broadcasts the message (making sure that the tag is still in the message, as a header). When a member receives a broadcast message, it checks whether it already received that message, by comparing the message's seqno to the highest seqno it received for that sender. If received, the message will simply be discarded, otherwise the member will update its highest seqno received and deliver the message. So if a member C sends messages 1, 2, 3, 4, 5 and 6 to the coord, and the coord is able to broadcast all messages, but dies and C doesn't receive brodacasts 5 and 6 (therefore 5 and 6 are still in the forward-table), then C simply re-sends messages 5 and 6 to the new coord. The new coord broadcasts 5 and 6, but all members who received 5 and 6 before will simply discard them. C, who originally sent 5 and 6, will deliver them and remove 5 and 6 from its forward-table. The unit test that verifies this behavior is org.jgroups.tests.SequencerFailoverTest Design ------ Variables: - forward-table: Map, list of messages to be forwarded (sorted by seqno) that have not yet been received (as bcasts). Access patterns: add at end (frequent), remove by seqno (frequent), iterate through it in order of seqnos (infrequent): therefore a Map was preferred over a linked list - received-table: Map which keeps the highest seqno seen per member. This table has N entries, where N is the number of members in the group. Access patterns: lookup of message by seqno, update by seqno. Therefore a map was chosen On send(M): - Pass down unicasts and return (handle only multicasts) - Add a FORWARD header to M with the local_addr and a monotonically increasing seqno (use a ViewId) - If not coordinator: - Add message to the forward-table (when a broadcast is received, remove the corresponding message from the forward-table) - Send M to coordinator - Else - Multicast(M, local_addr) On stop(): - Stop handling new down messages - Wait for all replies until forward-table is empty, or a timeout (?) ** Comment: I decided not to implement this, because this implies flushing ** On reception of M from S: - If header is not present: - Pass up - Else (header is present) - If header is FORWARD: - If not coord: error and return - Else - Multicast(M,S) - If header is DATA: - Take sender S from header and call Deliver(M,S) Multicast(M,S): - Reuse M (no problem since UNICAST's retransmission buffer will have remove M when passed to SEQUENCER) - Replace FORWARD type in header with DATA type - Multicast M Deliver(M,S): - If M was sent by us (local_addr == S): - remove corresponding message from forward-table - If M was already delivered (M's seqno is <= the one in received-table): - Discard M - Else: - Update the received-table with M's seqno with key=S (needs to be 1 higher then prev seqno) - Shallow-copy M into M-tmp - Set M-tmp's sender address to S - Pass up M-tmp On view change: - If the coord leaves/crashes: - For each message M in forward-table (*in increasing order of seqnos* !): - Change M.dest to the new coord - Send M to the new coord - Remove entries for left members in received-table Comments -------- When forwarding we don't care about whether the message was forwarded before, e.g. by the previous coordinator. We simply forward, but discard duplicate messages when receiving the bcast. This is not overly efficient when failing over between coordinators, but makes the implementation simpler. libjgroups2.6-java-2.6.15.GA.orig/doc/design/AUTH.txt0000644000175000017500000002246411366547366021666 0ustar twernertwerner Design of authentication protocol (AUTH) ======================================== Author: Roland Raez, Bela Ban, Chris Mills Version: $Id: AUTH.txt,v 1.1 2006/01/27 14:46:12 belaban Exp $ Goal: to prevent random members from joining a group. Members have to pass authentication to join a group, otherwise they will be rejected [pasted from JGroupsAUTH wiki] Definition AUTH is used to provide a layer of authentication to JGroups. This allows you to define pluggable security that defines if a node should be allowed to join a group. AUTH sits below the GMS protocol and listens for JOIN REQUEST messages. When a JOIN REQUEST is received it tries to find an AuthHeader? object, inside of which should be an implementation of the AuthToken? object. AuthToken? is an abstract class, implementations of which are responsible for providing the actual authentication mechanism. Some basic implementations of AuthToken? are provide in the org.jgroups.auth package (SimpleToken?, MD5Token and X509Token). Effectivly all these implementations do is encrypt a string (found in the jgroups config) and pass that on the JOIN REQUEST. When authentication is successful, the message is simply passed up the stack to the GMS protocol. When it fails, the AUTH protocol creates a JOIN RESPONSE message with a failure string and passes it back down the stack. This failure string informs the client of the reason for failure. Clients will then fail to join the group and will throw a SecurityException?. If this error string is null then authentication is considered to have passed. Example Configuration In the above example the AUTH protocol delegates authentication to an instance of the org.jgroups.auth.X509Token1_5 class. The only parameter that AUTH requires is the auth_class attribute which defines the authentication mechanism. All other parameters defined in the configuration are passed in to the instance of the auth_class. This allows pluggable authentication mechanisms, abstracted from the core of JGroups, to be configured to secure and lock down who can join a group. Creating an AUTH module 1. Create a class that extends org.jgroups.auth.AuthToken 2. You must have an empty constructor 3. Implement the public void setValue(Properties properties) method to recieve properties from the JGroups config. 4. Implement the public String getName() method to return the package and class name 5. Implement the public boolean authenticate(AuthToken token) method to provide the actual authentication mechanism of clients. 6. In the jgroups config XML for AUTH set the auth_class attribute to your new authentication class. Remember to include anyother properties your class may require. Example Failure When authentication fails a SecurityException? is thrown on the client trying to join the group. Below is an example stack trace: org.jboss.jgroups.fileshare.exception.FileShareException: org.jgroups.ChannelException: connect() failed at org.jboss.jgroups.fileshare.FileShare.(FileShare.java:28) at org.jboss.jgroups.fileshare.FileShare.main(FileShare.java:55) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:78) Caused by: org.jgroups.ChannelException: connect() failed at org.jgroups.JChannel.connect(JChannel.java:425) at org.jboss.jgroups.fileshare.FileShare.(FileShare.java:21) ... 6 more Caused by: java.lang.SecurityException: Authentication failed at org.jgroups.protocols.pbcast.ClientGmsImpl.join(ClientGmsImpl.java:132) at org.jgroups.protocols.pbcast.GMS.down(GMS.java:738) at org.jgroups.stack.DownHandler.run(Protocol.java:120) On the coordinator the following is displayed for every failed membership join event: 21125 [WARN] X509Token.authenticate(): - X509 authentication failed 21125 [WARN] AUTH.up(): - AUTH failed to validate AuthHeader token [pasted from Roland's email] Recently I discussed with Bela Ban about a new Protocol called AUTH to authenticate the joining of members. I would like to implement this protocol. Here are my thoughts. AUTH is the next layer under pbcast.GMS. When pbcast.GMS sends a join request (a GMS.GmsHeader.JOIN_REQ header) AUTH just places the credential for the authentication in its own header. The join message will be sent to the coordinator and before the join will reach the GMS protocol the AUTH protocol checks the auth header. When the header authenticated successfully, it just passes up the join request and pbcast.GMS will accept the join and sends the header GMS.GmsHeader.JOIN_RSP containing a pbcast.JoinRsp back. When the authentication is not successful the AUTH protocol answers the GMS.GmsHeader.JOIN_REQ itself and sends back a GMS.GmsHeader.JOIN_RSP containing a pbcast.JoinRsp object with an error message. The pbcast.ClientGmsImpl receives in both cases the GMS.GmsHeader.JOIN_RSP. In the case where the join request could not be successfully authenticated the message in pbcast.JoinRsp can be used to throw a RuntimeException. Requirements for other Protocols: - pbcast.JoinRsp needs a new attribute (String), e.g. errorMsg - pbcast.ClientGmsImpl should use the attribute defined above and throw an exception when there was an error. - Optionally ENCRYPT can be extended so that AUTH provides the symmetric key I think it would be fine when AUTH supports two authentication methods: Token: A simple token (String) based authentication using a configured String or a String read from an external file which can be protected so that only certain proceses can read the credential. Each member in the group needs to have the same token else the authentication will not succeed: 1. AUTH sends along with the GMS.GmsHeader.JOIN_REQ the hash of the credential including its address (each address will produce another hash). 2. The coordinator compares it's own hash with the one from the client. When they match the JOIN_REQ is passed up, else negative JOIN_RSP (with errorMsg set) is sent back (--> no further steps). 3. Along with the positive response JOIN_RSP from the GMS protocol the coordinator sends the hash of the credential including its address. 4. The client verifies the hash of the coordinator. When the verification is ok, he passes up the JOIN_RSP else he sends down a LEAVE_REQ. Should the following LEAVE_RSP be discarded by the AUTH protocol layer? Certificate: Implementing Certificate based authentication is more secure because spoofing IP addresses doesn't impact the security when additional message encryption is used. I think that it is a requirement that each member in the group can have the same certificate. The following algorithm allows the usage of the same certificate for each member in the group. 1. The joiner (GMS client) sends with the JOIN_REQ message its certificate and a nonce. 2. The coordinator verifies if he trusts the certificate of the client by checking the certificate chain. The authentication of the joiner is performed in a later step! When he does not trust, he sends back a negative GMS.GmsHeader.JOIN_RSP (--> no further steps). 3. The coordinator create a AUTH.REQ message containing its certificate, the nonce from the joiner encrypted with the private key of its certificate and a new nonce 4. The joiner verifies if he trusts the server by verifying the message (decrypts the nonce with the public key from the coordinator (retrieved from the certificate)) and optionally by checking the certificate chain. When the verification is ok, he creates an AUTH.RSP message containing the nonce from the coordinator encrypted with the private key of the joiner. When the verification is not successful AUTH.RSP message containing an error message. 5. The coordinator verifies the AUTH.RSP message. When it contains an error message a negative JOIN_RSP is sent down else the joiner is authenticated by decrypting the nonce with the public key of the joiner. When this is ok a JOIN_REQ is sent up to GMS else a negative JOIN_RSP is sent down. 6. The GMS protocols answers with a JOIN_RSP. When ENCRYPT and AUTH work together the AUTH protocol or the ENCRYPT protocol sends along this JOIN_RSP the new symmetric encryption key encrypted with the public key of the joiner. This protocol uses a lot of public private key encryptions (4 or five when AUTH provides the key for ENCRYP) but I think this is ok (members usually join not very often). The session key created by the coordinator could be used in the ENCRPT protocol for the message encryption. For me there are the following open questions: - Are the two AUTH methods reasonable? - Should ENCRYTP be extended so that the AUTH protocol distributes the new symmetric encryption key? - How would AUTH "send" the key to the ENCRYPT protocol? Using a header which is cleared in the ENCRYPT layer would need that the ENCRYPT must be below AUTH because of fragmentation. This would be ok for me. - Would it be ok that when a member leaves the group that still the same symmetric key is used?libjgroups2.6-java-2.6.15.GA.orig/doc/design/flush-state-transfer.fig0000644000175000017500000001147611366547366025135 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 11737.500 2437.500 11625 2175 12000 2325 11625 2700 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 0 1 0 11737.500 6112.500 11625 5850 12000 6000 11625 6375 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 1 1 0 11362.500 6787.500 11475 6525 11100 6675 11475 7050 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 1 1 0 11437.500 3112.500 11550 2850 11175 3000 11550 3375 0 0 1.00 60.00 120.00 5 1 0 1 0 7 50 -1 -1 4.000 0 1 1 0 11437.500 3712.500 11550 3450 11175 3600 11550 3975 0 0 1.00 60.00 120.00 6 1425 1200 3000 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 1425 1200 3000 1200 3000 2100 1425 2100 1425 1200 -6 6 5925 1200 7500 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 5925 1200 7500 1200 7500 2100 5925 2100 5925 1200 -6 6 10725 1200 12300 2100 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 10725 1200 12300 1200 12300 2100 10725 2100 10725 1200 -6 6 7125 3825 8100 4050 4 0 0 50 -1 0 12 0.0000 4 180 945 7125 3975 get DIGEST\001 -6 6 225 3675 1200 3900 4 0 0 50 -1 0 12 0.0000 4 180 945 225 3825 get DIGEST\001 -6 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 2175 2100 2175 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 6675 2100 6675 9150 2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2 11550 2100 11550 9150 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 2775 2175 3525 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 2775 6675 3600 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2175 3525 11550 4500 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 6675 3600 11550 4350 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 4575 2175 5025 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 2175 5100 11550 5100 11550 5850 2175 5850 2175 5100 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 6450 2175 6900 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 11550 6450 6675 7575 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 6686 3448 6986 1423 7286 3448 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 3 0 0 1.00 60.00 120.00 1528 3375 1828 1350 2128 3375 0.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 1350 7650 1200 6750 1650 6075 1650 5850 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 6975 7650 6825 6750 7275 6075 7275 5850 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 11850 7875 11700 6975 12150 6300 12150 6075 0.000 1.000 1.000 0.000 3 0 0 1 0 7 50 -1 -1 4.000 0 1 0 4 0 0 1.00 60.00 120.00 12525 3150 12375 2250 12825 1575 12825 1350 0.000 1.000 1.000 0.000 4 0 0 50 -1 0 12 0.0000 4 135 405 8250 2250 JOIN\001 4 0 0 50 -1 0 12 0.0000 4 135 615 7500 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 825 3000 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 615 12525 2475 BLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 7125 3750 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1890 225 3600 flip FLUSH.down() latch\001 4 0 0 50 -1 0 12 0.0000 4 135 885 600 6375 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 885 7050 6900 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 135 885 11925 7050 UNBLOCK\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 525 7950 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 525 8175 latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 6750 7950 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 6750 8175 latch\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 11625 8250 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 11625 8475 latch\001 4 0 0 50 -1 0 12 0.0000 4 135 795 1800 1725 A:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 6300 1725 B:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 780 11100 1725 C:Channel\001 4 0 0 50 -1 0 12 0.0000 4 135 1170 11025 1950 State Receiver \001 4 0 0 50 -1 0 12 0.0000 4 180 1065 1725 1950 State provider\001 4 0 0 50 -1 0 12 0.0000 4 135 840 10650 2475 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 3975 3150 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 3750 4050 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 9075 3900 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 135 1560 5700 5475 STATE TRANSFER\001 4 0 0 50 -1 0 12 0.0000 4 135 765 12075 6000 RESUME\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 10125 7200 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 7875 6525 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1125 8175 7425 STOP_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 8475 3450 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1245 9900 3300 START_FLUSH\001 4 0 0 50 -1 0 12 0.0000 4 180 1785 11250 3825 FLUSH_COMPLETED\001 4 0 0 50 -1 0 12 0.0000 4 180 1470 11625 3375 flip FLUSH.down()\001 4 0 0 50 -1 0 12 0.0000 4 135 375 11625 3600 latch\001 4 0 0 50 -1 0 12 0.0000 4 135 840 11700 4650 SUSPEND\001 4 0 0 50 -1 0 12 0.0000 4 105 540 11700 4875 returns\001 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.5.txt0000644000175000017500000002077111366547366022546 0ustar twernertwerner Release Notes JGroups 2.5 ========================= Version: $Id: ReleaseNotes-2.5.txt,v 1.11 2007/07/03 12:28:05 belaban Exp $ Author: Bela Ban JGroups 2.5 is still API-backwards compatible with previous versions (down to 2.2.5). However, there are some changes: - JDK 5 is required - some older protocols were tossed out, e.g. if you used protocols listed in vsync.xml, they won't be found anymore The biggest new functionality is the concurrent stack, which allows for concurrent processing of unrelated messages. Below is a summary (with links to the detailed description) of the major new features. Concurrent stack ---------------- [http://jira.jboss.com/jira/browse/JGRP-181] The concurrent stack is a major performance improvement for clusters where multiple nodes are sending messages at the same time. Up to and including 2.4.x, all messages from all senders were placed into a single queue and delivered in order of reception (FIFO) to the application. This means that, for a given message M, all messages ahead of M had to get processed before M could get processed, even if some of those messages were from different senders. Now, messages from different senders are processed concurrently. This is done through 2 thread pools, one for default messages and another one for out-of-band messages. Both pools can be configured through XML, e.g. core and max number of threads, rejection policy ("run", "discard", "discardoldest" etc), whether to use a queue and if so, queue length etc. The concurrent stack will improve performance dramatically when - there are multiple senders and/or - the processing of a message takes some time. In a cluster of N with N senders, X messages and T think time/message, we have seen total processing time of all messages drop from X * N * T to (X * T) + ! Out-of-band (unordered) messages -------------------------------- [http://jira.jboss.com/jira/browse/JGRP-205] In some cases, messages do not need to get delivered in the order in which they were sent. For example, if a sender A sends messages M1 --> M2 --> M3 --> M4 --> M5 (--> means followed by), and all messages except M3 (heartbeat) and M5 (discovery request) are regular messages, then all 5 messages will be delivered sequentially. This means that M3 has to wait for M1 and M2 to get processed, and M5 has to wait for all 4 messages ahead of it, until it gets processed. An out-of-band (OOB) message is one that is tagged: Message msg; msg.setFlag(Message.OOB) An OOB message is reliably transmitted, that is if the network drops it, JGroups will retransmit it. However, the ordering defined by the stack is ignored for an OOB messages, e.g. in the above case, M3 and M5 can be delivered out of sequence with regard to the other messages. This is perfect for messages like heartbeats or discovery requests or responses, which do not need to be delivered in FIFO order with respect to other messages from the same sender. If a message is tagged as OOB, it will be handled by the OOB thread pool rather than the regular thread pool. Concurrent Multiplexer ---------------------- [http://jira.jboss.com/jira/browse/JGRP-415] A similar problem that the concurrent stack solved, was encountered in the Multiplexer: requests to different services, but sent by the same sender, were processed sequentially (even with the concurrent stack, where messages from the same sender are processed in FIFO order). This was solved by adding a thread pool to the Multiplexer, which handles requests to different services concurrently, even if they were sent by the same sender. Simplified and fast flow control (SFC) -------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-402] SFC is a simple flow control protocol for group (= multipoint) messages. It is simpler than FC, but as the performance report (http://www.jgroups.org/javagroupsnew/perfnew/Report.html) shows, it has about the same performance as FC, so we may combine the 2 in the future. Note, however, that SFC does not apply flow control to unicast messages, whereas FC does. Every sender has max_credits bytes for sending multicast messages to the group. Every multicast message (we don't consider unicast messages) decrements max_credits by its size. When max_credits falls below 0, the sender asks all receivers for new credits and blocks until *all* credits have been received from all members. When the receiver receives a credit request, it checks whether it has received max_credits bytes from the requester since the last credit request. If yes, it sends new credits to the requester and resets the max_credits for the requester. Else, it takes a note of the credit request from P and - when max_credits bytes have finally been received from P - it sends the credits to P and resets max_credits for P. The maximum amount of memory for received messages is therefore * max_credits. The relationship with STABLE is as follows: when a member Q is slow, it will prevent STABLE from collecting messages above the ones seen by Q (everybody else has seen more messages). However, because Q will *not* send credits back to the senders until it has processed all messages worth max_credits bytes, the senders will block. This in turn allows STABLE to progress and eventually garbage collect most messages from all senders. Therefore, SFC and STABLE complement each other, with SFC blocking senders so that STABLE can catch up. Full support for virtual synchrony ---------------------------------- [http://jira.jboss.com/jira/browse/JGRP-341] The FLUSH protocol in 2.4.x supported virtual synchrony ([1]), but the flush phase didn't include a message reconciliation part. This has now been added in 2.5. FLUSH is very important for the Multiplexer, where a flush phase is run whenever a member joins, leaves or crashes, or when a new member acquires the state from an existing member (state transfer). Simple failure detection protocol (FD_ALL) ------------------------------------------ [http://jira.jboss.com/jira/browse/JGRP-395] Failure detection based on simple heartbeat protocol. Every member periodically multicasts a heartbeat. Every member also maintains a table of all members (minus itself). When data or a heartbeat from P are received, we reset the timestamp for P to the current time. Periodically, we check for expired members, and suspect those. Example: In the exampe above, we send a heartbeat every 3 seconds and suspect members if we haven't received a heartbeat (or traffic) for more than 10 seconds. Note that since we check the timestamps every 'interval' milliseconds, we will suspect a member after roughly 4 * 3s == 12 seconds. If we set the timeout to 8500, then we would suspect a member after 3 * 3 secs == 9 seconds. FD_ALL is interchangeable with FD. Better naming of threads ------------------------ Almost all threads have the cluster name and local address appended to their names. This is good if we have multiple clusters in the same JVM (e.g. in JBossAS), allowing for more meaningful stack traces (knowing which thread belongs to which cluster). No need for escape characters in stack configuration ---------------------------------------------------- Now backslashes are not needed for protocol attribute values, e.g. to define an IPv6 mcast_addr, the following works: The colons in mcast_addr do not need to be escaped with a backslash any longer. Note that '(', ')' and '=' are still reserved characters and cannot be used as part of an attribute name or value. Switch to java.util.concurrent classes (JDK 5) ---------------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-391] We switched from concurrent.jar to java.util.concurrent, resulting in slightly better performance and we could also drop a JAR (concurrent.jar). Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Performance ----------- We measured the performance of the 2.5 stack in a cluster of 4, 6 and 8 nodes. The results are discussed in http://www.jgroups.org/javagroupsnew/perfnew/Report.html. We'll present more comprehensive performance numbers (sepecially for the concurrent stack) in 2.6. Bug fixes --------- The list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland, June 2007 [1] Reliable communication in presence of failures. Kenneth P. Birman, Thomas A.Joseph. ACM Transactions on Computer Systems, Vol. 5, No. 1, Feb. 1987 libjgroups2.6-java-2.6.15.GA.orig/doc/Announcement-2.4.txt0000644000175000017500000001330411366547366022600 0ustar twernertwerner // $Id: Announcement-2.4.txt,v 1.2 2006/10/31 12:44:45 belaban Exp $ Finally, after almost 5 months, JGroups 2.4 is here ! There are some cool features that I'll describe in more detail below. Over 80 JIRA issues were resolved in 2.4, mostly bug fixes and new functionality. The good news is that 2.4 is API-backward compatible with all previous versions down to and including 2.2.7. So, for those folks who are using JBoss 4.0.x, which ships with JGroups 2.2.7 by default, this means that they can simply replace their JGroups JAR file with the one from 2.4 and benefit from the performance enhancements and bug fixes that went into 2.4. For details on the JBoss/JGroups combinations see http://labs.jboss.com/portal/jbosscache/compatibility/index.html. I'll now describe the new features briefly, check out the documentation (URL below) for a full discussion. FLUSH ----- Flush is a feature that - whenever the group membership is to be changed, or a state to be transferred - we tell every node in the cluster to stop sending messages and then join/leave a node, or transfer the state. When done, we tell all members to resume message sending. Why is this needed ? In 2 cases: (1) when we use a Multiplexer (see below) and have multiple services sharing the same channel that require state transfer and (2) when we want virtual synchrony (see below). So, if you don't use the Multiplexer or don't need virtual synchrony, you don't need the FLUSH protocol in your configuration. FLUSH is quite expensive because it uses multiple rounds of multicasts across the cluster, so remove it if you don't need it. Note that JBoss 5 requires FLUSH because it uses the Multiplexer: all cluster services share one JGroups channel. Multiplexer ----------- The Multiplexer was mainly developed to accommodate multiple services running on top of the *same* channel. JGroups channel are quite expensive in their use of resources (mainly threads) and sharing a channel amortizes a channel over multiple services. This is beneficial in JBoss where we had 5 clustered services (in 4.0.x), each using its own channel. In JBoss 5, we switched to the Multiplexer, and all 5 services use the same shared channel. Startup time in JBoss 5 ('all' configuration) was 43s on my laptop before the change, and 23s afterwards ! If multiple services sharing a channel require state transfer, we run into the problems described in JGroups/doc/design/PartialStateTransfer.txt. FLUSH is required to prevent those problems. The Multiplexer is described in chapter 6.3 of the documentation (see below). Streaming state transfer ------------------------ So far, state has always been transferred using a byte[] buffer. This forced a user to serialize the entire state into a byte[] buffer at the state provider and unserialize the byte[] buffer into the application state at the state requester. If the state is 2GB or more, this might likely result in memory exhaustion. Streaming state transfer uses input and output streams, so users can stream their state to the output stream in *chunks* and don't need to have the entire state in memory as required by a byte[] buffer. On the receiving side, an input stream is provided from which the user can read the entire state and set the application state to it. Streaming state transfer is essential for large states ! Partial state transfer ---------------------- This allows a programmer to transfer a subset of the entire state, identified by an ID. We use this (via JBossCache) in JBoss HTTP session replication/buddy replication, where only the state represented by a buddy is transferred. Virtual Synchrony ----------------- Virtual Synchrony is a model of group communication, developed by Ken Birman at Cornell, which has the following properties: #1 All non-faulty members see the same set of messages between views #2 A message M sent by P in view V must be received by P in V, unless P crashes #3 All non-faulty members receive the same sequence of views With FLUSH, we re-implemented the old virtual synchrony implementation of JGroups (vsync.xml), which I wrote in 1998/1999, but which has never been tested rigorously. We will phase out the old implementation in 3.0, along with other reorganizations of the protocol stack packages. Note that we have not yet *fully* implemented virtual synchrony as flushing only flushes out messages *sent* by member P, but not those *received* by P. Therefore, if member A sends a message M to {A,B,C}, and crashes immediately afterwards, and only B received M, then C will *not* receive M (violating rule #1 above). We will fully implement this in JGroups 2.5 (http://jira.jboss.com/jira/browse/JGRP-341). View bundling ------------- When a large number of nodes join or leave a cluster at about the same time, we can collect all JOIN/LEAVE requests and create only 1 view. View installation is quite costly, especially if FLUSH is used, which requires some round trips across the cluster, and so we minimize them. Failure detection ----------------- We have 2 new protocols: FD_PING and FD_ICMP which allow for scripts to be run in order to check the health of a node (FD_PING) and ICMP to ping a machine (FD_ICMP). These can of course be combined with other failure detection protocols, such as FD or FD_SOCK. Updated documentation --------------------- The new features have been added to the documentation at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html JIRA issues ----------- The issues that went into 2.4 can be found at http://jira.jboss.com/jira/browse/JGRP. Acknowledgments --------------- I'd like to thank Vladimir Blagojevic for writing the FLUSH and streaming/partial state transfer features, and for testing them thoroughly ! Enjoy ! Bela Ban, Red Hat Inc Kreuzlingen, Oct 31 2006libjgroups2.6-java-2.6.15.GA.orig/doc/bugs/0000755000175000017500000000000011621261107020017 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/bugs/Digest.txt0000644000175000017500000000232711366547366022027 0ustar twernertwerner Digest on view changes ====================== Author: Bela Ban Version: $Id: Digest.txt,v 1.1 2006/01/26 09:23:16 belaban Exp $ With STATE_TRANSFER we disable message garbage collection with SUSPEND_STABLE for the duration of the state transfer and later resume it with RESUME_STABLE. However, we don't do this for regular view changes, e.g. when a new member joins. The problem is: - Member A is in the group - Member B joins - Member A (as coord) sent 5 messages and therefore returns 5 as digest (assuming it has received all 5 messages yet) - Member B receives the JoinRsp and installs the digest of A:5 - In the meantime, member A sent another 10 messages, which triggered garbage collection of messages A:0 - A:13 (just as example), so A has only messages 13-15 in its sent_table - A sends another message (A:16) - Upon reception of A:16, B requests retransmission of A:6 - A:16 - A will not be able to serve that request because it garbage collected messages up to 13 SOLUTION: - Same as with STATE_TRANSFER: - On JOIN request, send down a SUSPEND_STABLE - When JOIN request handling has processed (e.g. after receiving all VIEW_ACKs ?), send a RESUME_STABLE down - Does this need to be done only on JOIN or also on LEAVE ?libjgroups2.6-java-2.6.15.GA.orig/doc/bugs/MergeProblem.txt0000644000175000017500000000563111366547366023171 0ustar twernertwerner Merge problem in GMS/MERGE2 =========================== Author: Bela Ban Version: $Id: MergeProblem.txt,v 1.1 2006/01/27 14:46:11 belaban Exp $ Symptom: first merge works, subsequent merges don't work Unit test: MergeStressTest The problem is that when many members join, we increment the VID (ViewID) every time we multicast a new view. Say we have subgroups {A,B,C} and {D,E}. The merge is now started by MERGE2, detecting 2 coordinators (A and D). The VID are 4 for {A,B,C} and 6 for {D,E}. The merge coordinator (A) therefore picks VID=7 for the MergeView. It now sends a request to both A and D, to multicast the new MergeView to their respective subgroups. However, let's assume that in the meantime, other members have joined one of the 2 subgroups, so that the VIDs are now: {A,B,C}: VID=9 {D,E}: VID=5 When the MergeView is received, it will be rejected by {A,B,C} because its own VID of 9 is greater than the MergeView's VID of 7. However, subgroup {D,E} will accept the MergeView because its own VID of 5 is smaller than the MergeView's VID of 7 ! So now the A, B and C have a membership of {A,B,C} with VID=9 but D and E have a membership of {A,B,C,D,E} with VID=7 ! One of the consequences of this is that D will cease sending out MERGE2 requests, because it thinks it is not the coordinator anymore ! This means we will not get any MERGE2 events into the GMS layer anymore, which would cause another merge, this time with a higher VID for the MergeView. Possible SOLUTIONs: #1 Pick a VID for MergeViews that is high enough to be accepted by all subgroups #2 Suspend generating view changes while a merge is in progress #3 All JOIN, LEAVE and MERGE requests need to be handled by ViewBroadcaster (rename this!), check out ViewHandling.txt for details Comments: - #1 doesn't work well because then all previously generated views will be discarded, but clients already *have* the views ! - We have to process VIEWS and MERGE requests in order, possibly we need to flush the ViewBroadcaster and suspend view generation until the merge has been handled, then resume view generation - Therefore #2 looks more promising: on merge() we flush the ViewBroadcaster (send out all pending views) on *all* coordinators involved. Then we process the merge, generate and mcast the new MergeView, then resume ViewBroadcaster. Probably JOIN and LEAVE requests need to be handled by ViewBroadcaster too Design: ------- On MERGE_REQ: - suspendViewHandler() On CANCEL_MERGE: - resumeViewHandler() On firing of MergeCanceller: - If MergeCancellers merge_id == current merge_id: - resumeViewHandler() On INSTALL_MERGE_VIEW: - Install view - resumeViewHandler() suspendViewHandler(): - Complete current task, remove all other requests from queue - Make view handler discard all new requests from now on - Start MergeCanceller (if not started yet) resumeViewHandler(): - Stop MergeCanceller - Make view handler accept all requests from now onlibjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.10.txt0000644000175000017500000000255411366547366022765 0ustar twernertwerner Release Notes JGroups 2.6.10 =========================== Version: $Id: ReleaseNotes-2.6.10.txt,v 1.1.2.1 2009/04/28 14:25:36 vlada Exp $ Author: Bela Ban JGroups 2.6.10 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6, 2.6.7, 2.6.8 and 2.6.9. Below is a summary (with links to the detailed description) of the major new features between 2.6.9 and 2.6.10. Bug * [https://jira.jboss.org/jira/browse/JGRP-958] - Support Setting of Bind Address With TCP-based Stacks * [https://jira.jboss.org/jira/browse/JGRP-959] - coord.stopFlush() and coord.close() called in sequence cause flush lockup Feature Request * [https://jira.jboss.org/jira/browse/JGRP-960] - FILE_PING: new discovery protocol * [https://jira.jboss.org/jira/browse/JGRP-963] - FILE_PING: return immediately if there are no files around in the location directory (optimization) Task * [https://jira.jboss.org/jira/browse/JGRP-953] - FRAG2: set overhead to 0 by default Manual ------ The manual is online at http://www.jgroups.org/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada April 2009 libjgroups2.6-java-2.6.15.GA.orig/doc/ProtocolReentrancy0000644000175000017500000000210411366547366022657 0ustar twernertwerner Author: Bela Ban Version: $id$ A typical protocol in JGroups can be assured that only up() and down() methods can be called concurrently, however up() and up() methods, and down() and down() methods cannot be called concurrently. This is due to JGroups using an up and down queue, and 1 thread per queue continually dequeues messages and invokes up() or down(). However, with the introduction of down_thread=false and up_thread=false, we don't use queues anymore. If, in this case, we have multiple threads calling Channel.send() concurrently, then the above guarantee cannot be provided anymore. As a result, down() in any given protocol *can* now be called concurrently, by different threads, and so each protocol has to be made thread safe (reentrant). The focus here has to be on the down() side; multiple threads can call send() simultaneously. However, the up() methods are probably not much of a problem because they are called by a single thread (the receiver thread). We *cannot* use a thread pool for receiving messages, as this would violate the ordering previously established.libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.1.txt0000644000175000017500000000341011366547366022675 0ustar twernertwerner Release Notes JGroups 2.6.1 =========================== Version: $Id: ReleaseNotes-2.6.1.txt,v 1.1.2.2 2007/11/28 12:00:21 belaban Exp $ Author: Bela Ban JGroups 2.6.1 is still API-backwards compatible with previous versions (down to 2.2.5). JGroups 2.6.1 is *binary backwards compatible* with 2.6. Below is a summary (with links to the detailed description) of the major new features. FLUSH simplification and bug fixes ---------------------------------- [http://jira.jboss.com/jira/browse/JGRP-624] [http://jira.jboss.com/jira/browse/JGRP-622] [http://jira.jboss.com/jira/browse/JGRP-623] We simplified FLUSH in 2.7, and fixed a number of bugs, which were all backported to 2.6.1 Reincarnation prevention ------------------------ [http://jira.jboss.com/jira/browse/JGRP-637] [http://jira.jboss.com/jira/browse/JGRP-130] Reincarnation can be prevented by saving ports in persistent storage and reusing them only after some time. See http://wiki.jboss.org/wiki/Wiki.jsp?page=HandleJoinProblem for how to enabling persistent ports (off by default). Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Performance ----------- Links to performance tuning: http://wiki.jboss.org/wiki/Wiki.jsp?page=PerfTuning Bug fixes --------- Discovery: sometimes, a discovery process would not get interrupted, leading to a long join phase and some handleJoin() warning messages. [http://jira.jboss.com/jira/browse/JGRP-621] RpcDispatcher with incorrect membership passed as argument to callRemoteMethods() hangs. [http://jira.jboss.com/jira/browse/JGRP-620] The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Nov 2007 libjgroups2.6-java-2.6.15.GA.orig/doc/perfgraph.plt0000644000175000017500000000145211366547366021604 0ustar twernertwerner#This is a gnuplot file used for plotting performance test result graphs set term png set out "picname.png" set data style boxes set style fill solid border -1 set logscale xy set yrange [1:100000] set xrange [1:1000000] set style fill solid 1.0 set boxwidth 0.10 set title "Throughput in msg/sec(cluster of 8 machines,udp.xml,JGroups 2.3)" set xlabel "Message size" set ylabel "Number of messages per sec" set xtics ("100B" 100,"1K" 1000, "10K" 10000, "100K" 100000) plot "perfdata.dat" using ($1):($2) lt rgb "#6495ED" t "1 sender",\ "perfdata.dat" using ($1*1.27):($3) lt rgb "#BA55D3" t "4 senders",\ "perfdata.dat" using ($1*1.63):($4) lt rgb "#FFF8DC" t "8 senders" #where perfdata.dat looks something like this #100 29754 30873 27161 #1000 15886 11354 15918 #10000 2013 314 624 #100000 183 77 54 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.7.txt0000644000175000017500000000250311366547366022705 0ustar twernertwerner Release Notes JGroups 2.6.7 =========================== Version: $Id: ReleaseNotes-2.6.7.txt,v 1.1.2.1 2008/11/12 11:20:43 belaban Exp $ Author: Bela Ban JGroups 2.6.7 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5 and 2.6.6. Below is a summary (with links to the detailed description) of the major new features between 2.6.6 and 2.6.7. GossipRouter: made socket queue size configurable ------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-861] Replacing the previously hard coded size of 50. The new default is now 1000, but there's a setter so this value can be changed. Check whether we receive our own multicasts ------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-857] Bug fixes --------- FLUSH: problems with concurrent startup --------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-855] Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Nov 2008 libjgroups2.6-java-2.6.15.GA.orig/doc/todo.lst0000644000175000017500000010171211366547366020576 0ustar twernertwerner$Id: todo.lst,v 1.22 2005/06/06 07:47:57 belaban Exp $ Todo List --------- - Make TUNNEL/GossipRouter use Streamable, plus GossipRouter should use a ConnectionTable - FC: on a fast network, we can still run out of memory because received_msgs/delivered_msgs becomes very big. Instead of basing FC on the speed of the network, we should also take the number of messages processed (a.k.a delivered_msgs/received_msgs) into account, and only send credits when that number falls below a certain threshold. First: look at the number of received/delivered msgs in the perf test ! --> Look into memory-based FC: criteria for sending credits are - available free memory - outstanding retransmission requests (sender sends with each message number of messages sent, receiver keeps track of how many messages received, if diff is > threshold --> pause) - [optional] latency - Performance tests: - Fix TcpTransport: doesn't currently work (connection exception) : make TcpTransport runnable on single machine (define different ports) - Test m-m mode (everyone sends to everyone) with 4 nodes - Expose statistics (e.g. retransmission counter, flow control stats), maybe use AOP to do so - Create common TransportProtocol (subclass of Protocol): handles all message bundling, loopback etc, real transports (UDP, TCP, TUNNEL) have to extend this class and provide a send() and receive() method. Avoids code duplication - Add list of joined, crashed and left members to View - Support for IPv6 addresses - Convert TUNNEL, TCP, GossipRouter to Streamable - Logical IpAddress: use any number of NICs, and fail over between NICs, but always have the same logical address - TCPPING: make generic, e.g. STATIC_PING. Can apply to UDP transport as well. - Add pinging over socket established by FD_SOCK - RpcDispatcherSpeedTest is very slow when using tcp.xml - BSH: use CONFIG event from transport (UDP) to Channel and back for gathering of diagnostics information. - org.javagroups.tests.Bsh: use Channel with only unicast properties to connect to remote member(s). Advantage is that we can include fragmentation. todo: add connect(Address) to Channel. - SMACK: introduce negative acks (currently positive acks). This should be configurable. - TOTAL protocol: when B mcasts a message M, it will be unicast to coord A first, who then mcasts it on behalf of B. However, when A crashes before mcasting M, B will wait forever for responses (if unicast responses are expected). However, if we can request retransmissions from *any* member, not just the original sender of the message, M can be retransmitted to the members who did not receive the message. - Replace List/Queue/Stack with equivalent classes in java.util or Trove (trove4j.sf.net). Use concurrent.jar - ThreadPool: should release unused threads after some time. --> Replace ThreadPool with Executor in concurrent.util - Modify all building blocks to run on top of PullPushAdapter. Multiple building blocks should be able to run on the same PPA. - Correct implementation of merging protocol for org.javagroups.protocols.GMS: current merge protocol is simplistic and doesn't preserve vsync properties (bugid=556850) - Unification of ./protocols/pbcast and ./protocols directories: merge same functionality where possible or abstract out common functionality and create shared classes for these. There is currently a lot of redundant code in both directories. Also, there is the danger of fixing bugs in one branch, but not the other. Starting point is pbcast.GMS: to add vsync, do the following: - Flush protocol before installing view (ack-based) - Discard messages with view different from current view - LinkingPing Channel (LP) - Channel which acts as a 'linking pin' between 1 parent group and one or more subgroups. The LP always joins the parent channel and all child channels and forwards messages between the parent and all child groups, and vice versa (ie. acting as a router). - Provision of a mechanism to join members behind a firewall to a regular (e.g. UDP-based) group. Requires a new MASQ protocol, which hides the real addresses of members behind the firewall and provides the address of the LP instead. Upon reception of such a message, LP will rerieve the real address and forward the message to the correct member. - Multiplexer building block. Allows multiple building blocks on top of the same channel (could extend PullPushAdapter) - Make building blocks work over the same protocol stack (Channel). E.g. register building blocks with PullPushAdapter, mux/demux messages. - Documentation (DocBook ?) - pbcast.STATE_TRANSFER: allow multiple simultaneous state transfers to take place (use state transfer IDs) - pbcast.STATE_TRANSFER: allow for multiple chunks of state data due to large states - Debugging tool: shows layers with number of messages in each layer. Instrumentation of protocol layer should be as non-instrusive as possible (no penality for not running the stack in debugging mode). Possibility of attaching to running (possibly remote) stacks. Filter views: from only number of messages down to single messages (events) and their contents. Possibility to step (accept a message), drop, inject, modify messages. Possibility to see only certain types of messages. Assign different colors to different kinds of message to easily follow them through the system. - Configurator for protocol stacks: graphical interface, user chooses set of QoS (e.g. reliable transmission, fragmentation, TCP etc). Configurator does dependency checking, asks for parameters (e.g. UDP.mcast_addr etc). Output is a valid properties string. - TimedWriter: replace own thread with ReusableThread - Multiple Routers communicating with IP MCAST; clients connect via TCP. Tunnels through links that don't support IPMCAST. - Replace TimedWriter with TimedExecutor; more generic API for timed executions. Current implementation is hacked for writing to a file and creating a socket. - Adapter classes making use of Channel assume the channel is already connected when starting. However, if this is not the case, they should automatically connect instead of throwing an exception. Maybe this can be integrated into EnsChannel directly: whenever there is a Send() or Cast(), and the channel is not connected, do a Connect(). - (Ken) Allow objects outside the group to multicast messages to the group SharedSocket (CLIENTSERVER protocol): the coordinator of a group maintains a socket (UDP or TCP) and allows clients to send/mcast messages and read messages sent to the group via the socket. This way, clients do not have to join the group. - Distributed Shell: a number of xterm windows, each on a different machine. The screen is split: the upper half is for commands that are bcast to all members, executed there and the output sent back to the originator (output from each host in a different color). The lower half is local to each host: commands typed in there will be executed on that host and the result sent back to the originator. Done ---- - Add exception chaining (only available in JDK 1.4) - NAKACK: send xmits also to other members if no response from original sender (e.g. because it crashed). All members therefore have to respond to an xmit request by not only searching the sent-table, but also the received-table. Requires that members store *received* messages as well, not just *sent* messages. - Send xmit request to (a) sender (default) (b) random member (c) all members using multicast. Staggered replies, when 1 member sends response, other members suppress reply - 2.2.8: UDP/TCP doesn't work with 127.0.0.1 (works with 2.2.7) - April 2005 (bela) Retain only n messages (bounded storage) in NAKACK/NakReceiverWindow rather than unlimited. Discard newest messages when buffer is full - April 2005 (bela) TCP: instead of sending to each receiver in turn, use 1 queue/thread per receiver. This prevents the server from hanging of a receiver hangs - March 2005 (bela) FD: add similar mechanism as for FD_SOCK to accommodate for lost SUSPECT broadcasts - FD_SOCK: use membership to establish logical failure detection ring, rather than multicast - Feb 2005 (bela) NAKACK/NakReceiverWindow: - NakReceiverWindow.Retransmitter.remove(): linear search for removable element plus linear removal: 2 linear access patterns ! Replace with log(n) access pattern, e.g. TreeMap - updateHighestSeqno(): linear search - Feb 2005 (bela) NakReceiverWindow: make storing of *delivered* (delivered_msgs) messages optional. The pbcast version of NAKACK for example requests retransmission only from the sender directly, therefore we don't need the delivered messages. - Feb 2005 (bela) UDP should bind to *all* available network interfaces for receiving messages (JDK 1.4 specific) - Oct 6 2004 (bela) FD_SOCK problem on multi-homed systems - April 2004 (bela) UDP and bundling: error "ERROR 21:15:21,660 [UDP.IncomingPacketHandler thread] - message does not have a UDP header". Only occurs with fc.xml and bundling enabled. - 2003 (bela) ConnectionPool: garbage collect sockets over which no activity occurred for n minutes - Jan/Feb 2004 (bela) UDP: currently there are 3 sockets: 1 mcast in/out, 1 ucast in and 1 ucast out. Combine them into 1 or 2 sockets (e.g. 1 mcast in/out, 1 ucast in/out). The reason for this was that under Linux, because sockets were not BSD-compatible, a ucast send socket has to be closed and re-opened after every send ! This error may have been fixed in the meantime. --> Implemented in UDP and UDP1_4 protocols - 2003 (bela) Add reliable unicast communication, e.g. server channel and client channel. Add Channel.connect(Address) (overloading Channel.connect(String)) - 2003 (bela) MERGE: as an alternative to periodically mcasting merge packets, we could simply wait for mcasts from a member that has the same group name but is not in the membership. In this case we'd attempt a merge. --> Done (MERGEFAST protocol) - 2003 (bela) Peer-To-Peer protocols: - Simpler GMS, where joiners can ask anyone in the group to join it. That member will add the new member and mcast a ViewChange message. Receivers of the ViewChange message make the new membership the union of the existing mbrship, plus the newly joined members, minus the removed members. Membership may not be exactly the same on all members, but will converge over time. The advantage of this scheme is that the botleneck of the single coordinator is eliminated. - Simpler mcast retransmission protocol (SACK=Simple ACK). Ack-based scheme, mcasts with seqnos are sent to the group, acks are expected from each member. Outstanding acks from (potentially) crashed members can be reset by either (a) suspect messages, (b) view changes, or (c) by verifying whether the outstanding member is dead. --> done, added SMACK protocol - 2002/2003 (bela) Look into whether NAKACK/UNICAST/STABLE need to store copies of messages, or whether references are okay. Now that we use hashmaps instead of headers, this might be fine. (Immutable messages) - 2003 (bela) IpAddress: mainatain own cache to prevent unnecessary DNS lookups IpAddressFactory: too many instances of the same IpAddress In the future, we may remove this because InetAddress has its own internal cache as well. - Jan 2003 (bela) RpcDispatcher/MessageDispatcher: ship destination list with call and discard msg if local address is not part of destination list. - Dec 10 2002 (bela) Protocol/stack initialization: init(), start(), stop(), destroy() - Nov/Dec 2002 (akbollu) FLOW control protocol - Aug 21 2002 (bela) GMS-less reliable message transmission (see protocols/DESIGN) - SMACK and FD_SIMPLE protocols (see smack.xml for a sample protocol spec) - Aug 2002 (bela) Maintain bounded queue of suspects in GroupRequest - Aug 2002 (bela) Test program that tests whether IP multicast can be used - McastReceiverTest and McastSenderTest - June 1 2002 (bela) UNICAST: - UnicastTest has too many retransmissions for 10000 messages even with -loopback ! - Exponential backoff for message retransmission (similar to NakReceiverWindow) - variable retransmission configuration (currently only 1 value (2 secs)) - April 5 2002 (bela) Bug in NAKACK (change to hashmap-based headers): WRAPPED_MSG headers don't work because 2 NakAckHeaders are added, which doesn't work in the hashmap-based case --> Added header for WRAPPED_MSG under a different key ("NAKACK.WRAPPED_HDR") - March 2002 (fhanik) Configuration of protocol stack specified using a configuration file - XML as config file format - March 31 2002 (bela) Message: hashtable for headers. This allows direct access to protocol-owned header. Also prevents removing the wrong header. - pbcast.NAKACK: retransmit in bundles (new feature). Because we xmit multiple messages in one large message, the xmit message might become too big. The assumption was that there would be a FRAG layer below this protocol, however that is not the case in the default setup March 20 2002 (bela) - Nov 1 2001 (bela) Convert GUI demos to Swing for JDK >= 1.3 --> Preliminary port, more work needs to be done - Oct 25 2001 (bela) Revert to Thread.interrupt() bug in Linux JDK 1.3.1 (FD_SOCK) once we use JDK 1.4. SUN bug id=4514257 (new bug) --> Using socket.close instead of Thread.interrupt(), more portable - Oct 24 2001 (bela) The Draw demo triggers a lot of retransmissions if drawing extensively. This causes the drawing to block for short intervals. --> Was caused by mcast_{send,recv}_buf_size being too small (8k). If we assume that a message is ca. 0.5k, then 16 messages would already overflow the send buffer size. Therefore the size was increased, which causes fewer message to be dropped, hence fewer retransmissions. Note that the behavior was always correct; ie. message were indeed retransmitted (no msgs lost). See "Setting of recv/send buffer sizes in UDP (FRAG problem)" in JavaStack/Protocols/DESIGN for details. - Oct 2001 (bela) pbcast.GMS.InstallView(): if member is not in view, generate an EXIT event. However, for new joiners, this might be wrong. Either remove or correct -> CheckSelfInclusion() might be wrong - Oct 17 2001 (bela) Start 3 members (using FD_SOCK): kill 3rd: socket connection kill is not noticed under Linux: membership is 3 instead of 2. When other members leave, membership will be correct --> Fixed with FD_SOCK fix (see history for version 1.0) - Oct 12 2001 (bela) Retransmissions: [ERROR] NAKACK.Up(): XMIT_REQ: range of xmit msgs is null --> was caused by a bug in the externalization of pbcast/NakAckHeader (was correct before) - Oct 10 2001 (bela) Problem with Thread.interrupt on Linux/JDK1.3.1: thread waiting on input (e.g. System.in.read() do *not* get interrupted !) --> Fixed. See history.txt - Oct 10 2001 Check whether is_linux quirk is sill needed with Linux JDK 1.3 (UDP.java) --> Removed - May 15 2001 (bela) readFully(): don't allocate a byte buffer for every message, just allocate buffer once and only increase size if too small. (ConnectionPool and Link) - May 15 2001 (bela) Test UNICAST layer: with 15 members there are retransmissions - Rewrite the retransmission thread (user Timer) --> Caused by small UDP send and receive buffers (dropped large messages which were fragmented, retransmission was okay). See JavaGroups/JavaStack/Protocols/DESIGN for details. - May 11 2001 (bela) Change FD.java to use TimeScheduler - May 10 2001 (bela) IpAddress: printing of hostnames in dotted-decimal notation is wrong (e.g. 228.1.2.3 is printed as 228) - May 9 2001 (bela) Test new AckMcastSenderWindow and NakReceiverWindow classes (submitted by John) --> Integrated into JavaGroups - May 8 2001 (bela) Use ProtocolStack.timer (Timer) for recurring non time-critical tasks (saves some threads) - May 8 2001 (bela) Replace SortedList with SortedSet - May 4 2001 (bela) Trace: set flag 'trace' in Trace and initialize via Trace.init() - May 2 2001 (bela) FragTest doesn't work any more: UDP.SendUdpMessage(): java.lang.IOException: message too long --> see ./JavaStack/Protocols/DESIGN (frag problem) for details - April 3 2001 (bela) Fix "last msg lost" feature in pbcast/NAKACK (possibly same solution as for regular NAKACK) --> See pbcast/DESIGN for details - April 2001 (bela) Optimizations (OptimizeIt / JPprobe). Remove some of the down threads - March 30 2001 (bela) Replace all System.err/out() with Trace.print() --> Replaced most (still have some left to do, but not important ones) - March 9 2001 (bela) Property file for tracing: defines all modules/methods to be traced plus the desired tracing level. Will be read by JChannel before starting. The location of the property file needs to be defined via a -Dproperty_file= option at startup. If the file cannot be found, no tracing will be enabled (other than the tracing set programmatically). - March 9 2001 (bela) PBCAST.java: dynamically adjust the 'subset' and 'gossip_interval' variables. E.g. when the group size increases, decrease the variables, and increase them when it decreases - March 2001 (bela) UDP: specify IP address to bind to - March 2001 (jmenard) Tracing for inner classes, e.g. Trace.println("FD.PingerThread.run()", ...) - March 2001 (bela) outOfMemory error when sending 'wrong' messages to a JavaGroups process. Wrong messages could e.g. be a telnet connection. - March 2 2001 (bela) FD: suspect member P, followed by UNSUSPECT P. This has to result in P being removed from suspected_mbrs in ParticipantGmsImpl --> Done for ./pbcast/GMS - Feb 15 2001 (bela) PBCAST: bounded buffer. Discard PBCAST-related messages when the buffer is full. This prevents flooding of buffers when subset or pbcast_interval are chosen too high. - Feb 15 2001 (bela) PBCAST: singleton members never garbage-collect their messages - Feb 2001 (i-scream) Gianluca Collot: disconnecting a channel is not very clear (QueueClosed Exceptions,GMS implementation dont switch to Client ...) so reconnecting a disconnected channel is inpossible. - Feb 15 2001 (bela) Double-check whether suspected members are really dead (e.g. in GMS, before bcasting new view) --> VERIFY_SUSPECT - Feb 13 2001 (bela) FD: create A, B, C, D. Kill A, B and D simultaneously. C will not become new coordinator - Feb 12 2001 (bela) Header as a typed class - Feb 12 2001 (bela) Address as a typed class - Feb 13 2001 (jmenard) Tracing module to enable/disable certain error messages (similar to syslog). - Feb 9 2001 (bela) Gialuca Collot: replace Objects as addresses with typed equivalent (e.g. Address). Subclasses are IpAddress, ATMAdress etc. - Feb 7 2001 (bela) FD: if a member is suspected and subsequently receives a view in which it is not a member, currently that member remains in the group. However, it should leave the group and possibly rejoin it (EXIT event). --> Implemented in FD_SHUN - Feb 2001 (jmenard) Use javac instead of jikes in Makefile: use jikes when available, else javac --> Added configure program - Feb 7 2001 (bela) Find out why UNICAST over TCP does not work --> Problem was that UNICAST did not remove members once they were excluded from the group. Usually, this does not matter as new members in UDP have different addresses (different ports). However, in TCP members may have the same port, therefore the same address. When a connection from such a member was received, the connection table returned the entry for the old (stale) member, which of course contained wrong seqnos. Therefore, messages accumulate in UNICAST's up_queue and not be propagated up by the AckReceiverWindow. - Feb 7 2001 (john georgiadis) TOTAL protocol - Feb 5 2001 (bela) PERF protocol: adds header with identity/seqno and removes at receiver. Logs time for each message. Can also be used to measure time for an event in the same protocol stack. - Jan 22 2000 (bela) Problem with TCP/ConnectionPool in PBCAST: start A, start B, kill B, restart B: B times out attempting to reconnect --> Due to UNICAST problem (see UNICAST problem). Removed UNICAST protocol (not needed over TCP anyway) from demo program - Jan 22 2000 (bela) TCP/UDP/TUNNEL: local messages should be sent back up the stack immediately --> Done only for TCP - Jan 22 2000 (bela) UDP: receive(packet): each time a new packet of 65000 bytes is allocated; however, we can reuse the buffer by doing the following: for(int i=0; i < buf.length; i++) buf[i]=0; // clear the buffer packet.setLength(buf.length); sock.receive(packet); - Dec 10 2000 (bela) MessageDispatcherTest: if using 100ms instead of 2000, the app crashes: problem with ReusableThread / Scheduler ? - use no Sleep(): app hangs ! - MessageDispatcherTest / RpcDispatcherTest: hangs when closing channel (number of threads still running) --> Due to bug in ReusableThread, AckMcastSenderWindow - Dec 11 2000 (bela) NotificationDemo: NotificationBus.Stop() does not release all threads --> Due to bug in ReusableThread - Dec 10 2000 (bela) Replace all occurrences of Thread.stop() with the recommended way of stopping threads. Also replace suspend()/resume(). to be modified: - Scheduler.java - AckMcastSenderWindow.java - AckSenderWindow.java - EnsChannel.java - NakReceiverWindow.java - PullPushAdapter.java - JavaStack/Protocol.java - JavaStack/Protocols/FD.java - JavaStack/Protocols/FD_RAND.java - JavaStack/Protocols/GMS.java - JavaStack/Protocols/MERGE.java - JavaStack/Protocols/PIGGYBACK.java - JavaStack/Protocols/STABLE.java - JavaStack/Protocols/TUNNEL.java - JavaStack/Protocols/UDP.java - Nov 29 2000 (bela) STATE_TRANSFER for PBCAST - Nov 29 2000 (bela) Make timeouts for UNICAST message retransmission user-configurable - Nov 26 2000 (bela) XMIT_REQs in PBCAST currently ask for more messages than is actually necessary. Correct so that only the actual missing messsages are requested for retransmission. E.g. my digest for A is +2 -3 +4 +5 +6. If I receive a digest with +5 as the highest seqno for A, then the XMIT_REQ will be [3-6] rather than [3-3]. - Nov 25 2000 (bela) PBCAST.SetDigest(): do we really need to explicitely set the initial digest, or could we just create a new NakReceiverWindow with whatever seqno is sent ? Problem: if P:16 is received first by the new member S, but P:15 was dropped, then we would lose 1 message. I think it would be best to leave it as it is and always set the digest we got from the coordinator as result of the Join() call. --> Currently, setting initial digests is disabled. We just take the *first* seqno sent to us by a new member to be its *initial* seqno (which may or may not be true) --> Therefore, we may lose some message --> However, as long as gossiping is not implemented (to retransmit those messages), we won't change anything --> Currently, I prefer not to block because some member is missing a message, but won't get it since retransmission (gossiping) is not yet implemented --> When gossiping works, remove the comments and put setting initial digests back in --> Therefore, the client currently does not set its digest received as a result of the Join request. This has to be uncommented once gossip is available ! ==> see ./JavaStack/Protocols/pbcast/DESIGN for an explanantion of the solution to this issue - Nov 19 2000 (bela) Draw2Channels.java: 2 channels with the same properties don't work --> Fixed by making the GMS non-singleton - Nov 8 2000 (bela) GMS: CreateInstance() creates a singleton. This prevents multiple channels in the same JVM (GMS cannot be instantiated multiple times) --> Removed singleton - Aug 23 2000 (bela) MessageDispatcher.CastMessage(GET_ALL): if local delivery is turned off, this will hang waiting for the message from the local channel. Solution: CastMessage() needs to check whether local delivery is enabled or not in the channel to determine whether to wait for the local msg or not. Workaround: the first parameter to CastMessage() takes the members to which the message is to be sent; set it explicitely. - July 12 2000 (bela) Bug: DistributedHashtable demo hangs when MembershipListener is set in RpcDispatcher(). This is inside DistributedHashtable.java --> This was due to a bug in the FLUSH protocols: when a client joined, it received FLUSH messages as well (although not yet a server). This blocked the whole process (STOP_QUEUING). Now FLUSH.HandleFlush() contains the set of processes that should be flushed. If the current member is not in it, it will simply discard the message. This bug did not show up when using TCP because there, the FLUSH message is really only sent to the current membership - July 7 2000 (bela) Link: look at problem of 'wrong' peer addresses; these will be rejected. What happens when a connection request is rejected ? --> Peer will be rejected and tries anew (until NIC is okay again) - July 7 2000 (bela) Link: look at CTRL-Z cases. Socket connections can still be made to a process which is CTRL-Z'ed (this is normal). Therefore, the ConnectionEstablisher will think it has re=established connection, while the heartbeat will later fail again. - July 3 2000 (bela) Bug: TCP between habutterfly and dragonfly: members don't detect each other. Interfaces hme0 are better than hme1 (slower). Timing problem (set timeout differently) ? --> It was a simple timeout problem. Increasing the timeouts in TCPPING, FD and GMS helped. Traffic going across hme0 is very slow (routed via Belfast). - June 30 2000 (bela) TCP: specify IP address to bind to - June 27 2000 (bela) Fix memory bug (FD, STABLE ?). Occurs only with TCP (ConnectionPool) as bottom protocol. --> The problem was in ConnectionPool: socket.GetOutputStream().writeObject() and socket.getInputStream().readObject() wrote/read a whole graph of objects (Message plus all referring objects). Now we just send/receive byte buffers. - June 21 2000 (bela) Removed UNICAST protocol in stack (still have to find out what the problem is with UNICAST). But it is not needed over TCP anyway (neither is FRAG). Bug: UNICAST does not work with TCP as bottom protocol Scenario: start P, start Q, kill Q, start Q: UNICAST does not pass up the HandleJoin() msg to the GMS - June 19 2000 (bela) ConnectionPool / TCP: outgoing connections were only removed when a member failed (SUSPECT). However, they weren't removed when a member left regularly. Therefore incarnations of a member on the same port used the old socket (which failed). Change involved adjusting the outgoing connection table in ConnectionPool when TCP receives a VIEW_CHANGE: remove connections to processes that are not members any more. - June 19 2000 (bela) Bug fixed: when only the coordinator is left, a leave hangs until the leave_timeout has expired. This was due to deadlock waiting for the same mutex (leave_mutex) in CoordGmsImpl. - June 18 2000 (bela) Fixed bug in join/leave protocol (GMS). This bug only showed when using TCP instead of UDP/IPMCAST. The leaving member would not get his 'last view' because the TMP_VIEW was not set correctly before mcasting the view. - Dec 14 1999 (bba) Put JavaGroups on Gamelan - Dec 1 1999 (bba) Modify view change protocol (GMS): - When a HandleViewChange() message is sent after the FLUSH protocol, members that receive it install a new view, thus resetting message retransmission. This means, that also the coordinator resets its retransmission table, resulting in the following problem: when a member on a lossy link does not receive the new view, it would usually be retransmitted until he receives it. But since the coordinator installs a new view, message retransmission is stopped, and that member may never receive the new view. Therefore we have to make sure that all non-faulty members have received the new view before resetting retransmission ! - Solution: see ./design/ViewChangeRetransmission.txt - Nov 30 1999 (bba) Complete FLUSH protocol: Resending of outstanding messages has to be modified: we have to wait until all ACKs from all members for all outstanding messages have been received (for REBROADCAST). Otherwise, since we reset message retransmission upon a new view, slow members (or members on a lossy link) may never receive outstanding messages due to stopped retransmission. - Nov 99 (bba) Add objects, ints etc to Message (not as Header, but to message itself !) - Nov 99 (bba) Modify layer FD to *not* send out are-you-alive messages to a member P while other (e.g. data) messages from P are received. Further improvement: use gossip-like failure detection as described in the "GSGC: Efficient Gossip-Style GC Scheme" paper - Nov 99 (bba) Paper on implementation of RpcGMS (synchr. group calls plus state pattern) - Nov 99 (bba) Phase out MessageCorrelator - Nov 99 (bba) Remove classes not needed any longer !!! - Nov 99 (bba) Remove IDs for Messages. IDs are added by MNAK/NAK layers - June 9 (bba) Paper on RpcProtocol - May 11 (bba) Paper on state transfer - April 29, 1999 (bba) Synchronous Group RPC (GRPC) and deadlocks: investigate use of concurrency to eliminate the problem while preserving ordering properties (- Implementation of priority scheduler) - April 13, 1999 (bba) Each protocol has certain prerequisites; GMS for example needs PING and FD to be present somewhere below it. Add a sanity check mechanism to the protocol stack (configurator) that allows each protocol to abort stack creation if it cannot find the protocol layers it requires. - April 1 (bba): Paper on protocol stack (how to write your own layer) --> user's guide - Feb 23, 1999 (bba): Wrong parameters in any protocol should cause error messages Null protocol will be created, which causes later abort - Jan 13, 1999 (bba): Change Channel according to Channel interface document - Provide unicast point-to-point channel: Connect(new Address("janet", 3456)) Implemented and then dropped again ! Connection-less group mcast and connection-oriented ucast don't mix very well in the same model. That's why there are DatagramSockets and MulticastSockets... - Dec 98 (bba): IP multicast: problems with GMS (PingMembers) Fixed - Dec 98 (bba): GossipServer and GMS: membership is not correct - Dec 11, 1998 (bba) Added multicast ack (MACK) layer - Aug 19, 1998 (bba) Dispatcher: use a factory to create a channel instead of creating an EnsChannel directly (no hardcoding) -- Introduced ChannelFactory, EnsChannelFactory and JChannelFactory - Jun 18, 1998 (bba) MessageCorrelator: stable messages should be purged periodically -- Changes in ./channel/MessageCorrelator.java - Jun 19, 1998 (bba) Create 1 outboard process per Hot_Ensemble instance. Currently, ejava implements 1 outboard process per Java VM -- Changes in ./Ensemble/Hot_Ensemble.java Rejected -------- - May 2005 (bela) Handle MergeViews in DistributedHashtable, ReplicatedHashtable etc --> these classes are deprecated, not maintained anymore - 2003 (bela) Since message IDs are ever increasing with PBCAST, we have to reset them (e.g. when they reach 2 E9; 4E9 is the current max size of a long on Solaris 8). Write a protocol that resets the message IDs in all members at the same logical point in time. Alternative: create new SequenceNumber class, use it instead of long for seqnos --> longs are 8 bytes, so we have 2E10 -1 numbers, that is more than enough. - 2003 (bela) EventPool: pool for Event instances. Since a lot of Event instances are used, we should reuse them to avoid constant instance creation. Use OptimizeIt to measure number of Events created. --> JDKs 1.4 and higher now manage object pools internally, this is not needed anymore - 2003/2004 TransactionalHashtable: when async replication, provide option to periodically replicate changes (e.g. using a queue) --> done in JBossCache, currently no plans to backport to JGroups - Aug 2002 Integrate latest version of EJAVA (comes with 0.70 distribution of Ensemble) --> Nobody uses the EnsChannel, so forget about this project - Summer 2001 (bela) Reduce message overhead for small messages by reducing headers (static array of header names). Suggested by Gianluca. --> Reduce IpAddresses as well (use InetAddress.getByName() with dotted-decimal notation for unserialization to avoid reverse DNS lookup) (suggested by John) --> Remove dest and src from Message serialization altogether in UDP: just send payload and headers. Reconstruct dest and src from addresses in DatagramPacket --> Experimented with various schemes, none of them was elegant libjgroups2.6-java-2.6.15.GA.orig/doc/RULES0000644000175000017500000000425011366547366017721 0ustar twernertwerner # $Id: RULES,v 1.6 2004/06/30 17:12:49 belaban Exp $ Rules developers should adhere to --------------------------------- - The decision when to move a version number is taken by the project administrator (currently bela). Just drop me an email if you want to increment a minor version number and I'll ok it. This prevents multiple developers from incrementing the same version number. With minor relases this is usually not a big deal and I'll do it immediately. - Do not modify core functionality (e.g. Protocol, Scheduler, CoordGmsImpl etc) before consulting with one of the project administrators. - The history file should be updated if someone made a modification/contribution. Not every detail, just a short note. - New tarballs are done by the project administrator. I may delegate this in the future (to a number of people), but I decide when to add a new tarball to the download section. - Don't use any functionality found in JDKs higher than the one JGroups is currently using - Don't use any functionality not found in the reference JDK (e.g. from Microsoft's proprietary JDK implementation) - There are NO restrictions with respect to programming style. Just be coherent. SUN advocates use of fooBar() method style. - If you modify a file 'owned' by someone else, try to adhere to his/her style. Don't try to impose your style on that person. Ownership is basically who created the file. Once you obtain the ownership you can change the file if you want to. - Try to write test drivers for your module(s) and add them to JGroups/tests. We use JUnit (www.junit.org) for regression testing. It is pretty simple to add a new test to the directory, e.g. by just copying an existing test driver and modifying it. - Add the Id identifier to the top of all your files ( is a $, it cannot be written out otherwise it would get expanded by the CVS) - Use spaces instead of tabs, or make your IDE expand tabs to spaces. In Emacs this can be done via the following code: (setq-default indent-tabs-mode nil) Bela Ban (bela@yahoo.com) San Jose, March 13 2001 Dec 11 2002 April 5 2004 libjgroups2.6-java-2.6.15.GA.orig/doc/history.txt0000644000175000017500000023355011366547366021355 0ustar twernertwerner History List ============ Revision: $Id: history.txt,v 1.192 2007/04/04 05:23:34 belaban Exp $ [For current version, see file Version.java (or invoke 'java org.jgroups.Version')] bba = Bela Ban, bba@cs.cornell.edu bela = Bela Ban, belaban@yahoo.com i-scream = Gianluca Collot, gianlucac@tin.it igeorg = John Georgiadis, i.georgiadis@doc.ic.ac.uk jmenard = Jim Menard (jimm@io.com) fhanik = Filip Hanik (filip@filip.net) vlada = Vladimir Blagojevic (vladimir@cs.yorku.ca) rrokytskyy = Roman Rokytskyy (rrokytskyy@acm.org) akbollu = Ananda Bollu (akbollu@users.sf.net) whizkid_bay = Mandar Shinde (whizkid_bay@users.sf.net) ovidiuf = Ovidiu Feodorov (ovidiuf@users.sf.net) romuald = Romuald du Song yaronr = Yaron Rosenbaum (yaronr@mercury.co.il) publicnmi = Robert Schaffar-Taurok (robert@fusion.at) ossiejnr = Chris Mills (chris.mills@jboss.com) *********** THIS FILE IS NOT UPDATED ANYMORE AS OF MAY 2 2006, I SWITCHED TO JIRA FOR THE ROADMAP AND HISTORY. http://jira.jboss.com/jira/browse/JGRP *********** Version 2.3 ----------- - Changed method signature of RpcDispatcher.callRemoteMethod() to throw a Throwable. Previously it returned the exception as an object, now the exception will be thrown. Callers of these methods have to change their code, so this is an incompatible change. However, these calls are not used in JBossCache and JBoss Clustering. (http://jira.jboss.com/jira/browse/JGRP-154) (bela Feb 16 2006) - Added encryption of entire message to ENCRYPT (http://jira.jboss.com/jira/browse/JGRP-190) (bela Feb 9 2006) - Converted Word prog guide to docbook (./doc/progguide). Done by gdvieira@ic.unicamp.br (gdvieira@ic.unicamp.br Jan 26 2006) - Created global JGroups thread group (all threads belong to it) (bela Jan 19 2006) - Added AUTH protocol, samle configs and documentation. http://jira.jboss.com/jira/browse/JGRP-164 (ossiejnr Jan 12 2006) - Fix in ReplicatedHashtable where state transfer notification is not emitted (http://jira.jboss.com/jira/browse/JGRP-175) (David Forget Jan 5 2006) - init() is now called after all protocols have been created (bela Jan 5 2006) - Fixed bug where srv_sock_bind_addr could be different from transport's bind_addr. This could cause incorrect suspect messages. (http://jira.jboss.com/jira/browse/JGRP-173) (bela Jan 3 2006) - Added total order SEQUENCER protocol. See doc/SEQUENCER.txt for details (bela Dec 30 2005) Version 2.2.9.1 --------------- - Created label JGROUPS_2_2_9_1 (bela Dec 29 2005) - Fixed bug "binding to same interface twice fails" (http://jira.jboss.com/jira/browse/JGRP-167) (bela Dec 23 2005) - Fixed merge bug occurring when members are joining during a merge (http://jira.jboss.com/jira/browse/JGRP-139) (bela Dec 23 2005) - Fixed ENCRYPT bug where down messages were queued unnecessarily (JIRA: http://jira.jboss.com/jira/browse/JGRP-166) (bela Dec 21 2005) - Replaced Vector for members and pingable_members in FD with CopyOnWriteArrayList. Fixes (http://jira.jboss.com/jira/browse/JGRP-161) (bela Dec 16 2005) Version 2.2.9 ------------- - Created label JGROUPS_2_2_9 (bela Dec 9 2005) - Rewrite of TCP_NIO (Scott Marlow and Alex Fu Nov 2005) - Added support for Receiver in JChannel (push based message reception) (bela Oct 2005) - Added JChannel.dumpStats(): returns information about the various protocols, and the channel itself, as a map. Currenty, only NAKACK, TP and FC have implementations (bela July 26 2005) - Eliminated creation of 3 input and 2 output streams *per message* in TP. These streams are now created at TP startup and simply reset when a message is to be received or sent (bela July 25 2005) - Improved performance of RpcDispatcher, MethodCall (is now Stremable), added unit tests (bela July 25 2005) - Improved javadoc: copied portions of User's Guide to doc comments in source files, fixed javadoc warnings; added project and package overviews; tweaked build target. (chrislott July 17 2005) - Headers are now always created in a Message, because we always add at least 1 header to a message (bela July 15 2005) - Changed org.jgroups.util.Digest to use a HashMap rather than arrays (bela July 12 2005) - Refactored UDP and TCP into extending a common transport (TP) (bela July 4 2005) - Completed first version of JMX instrumentation (bela June 14 2005) - Added STATS protocol, provides stats via JMX (bela June 7 2005) - Added org.jgroups.jmx package, contains various adapters to expose channel and protocols via JMX. Added jmxri.jar (can be removed once JDK 5 is baseline) (bela June 1 2005) - Replaced System.err.println() with log.error() in setProperties() of all protocols (bela May 30 2005) - Added xmit_from_random_member: retransmits will go to a random member, rather than the original sender of the message. This eases the burden on senders in large groups (bela May 25 2005) - Fixed http://jira.jboss.com/jira/browse/JGRP-79 (problems with nulling src addresses on loopback adapter in Windows). See JGroups/docs/NullingSrcAddresses.txt for details. (bela May 19 2005) - Fixed problem with PingWaiter/PingSender in applets (http://jira.jboss.com/jira/browse/JGRP-86) (bela Ma 19 2005) - Fixed STATE_TRANSFER stuck in Channel.queue bug (http://jira.jboss.com/jira/browse/JGRP-80) (bela May 10 2005) - Added support for multiple locked locks to the DistributedLockManager. (publicnmi Jun 08 2005) Version 2.2.8 ------------- - Created branch JGROUPS_2_2_8 (bela April 29 2005) - Fixed problem with deadlock detection (e.g. in RpcDispatcher). Caller always passed new call stack, rather than adding to existing call stack (bela April 25 2005) - Removed xerces JARs (bela April 25 2005) - UDP: fixed incorrect marshalling of IpAddresses with additional_data (http://jira.jboss.com/jira/browse/JGRP-63) (bela April 23 2005) - Replaced getClass().getClassLoader() with Thread.currentThread().getContextClassLoader(). JIRA issue http://jira.jboss.com/jira/browse/JGRP-34 (bela April 23 2005) - UDP: synchronization around message marshalling and sending, non-sync could lead to intermingled byte arrays at the receiver, due to concurrent access to out_stream (bela April 20 2005) - Fixed bug where IpAddress.additional_data was not marshalled (bela April 19 2005) - Removed RWLock, replaced uses with ReadWriteLock from util.concurrent (bela April 8 2005) - Removed TransactionalHashtable (obsolete, replaced by JBossCache) (bela April 8 2005) - GMS/CoordGmsImpl: added merge_leader flag. If enabled, the member can initiate a merge although it is not the coordinator (bela April 7 2005) - UDP: bind_to_all_interfaces now allows the multicast receiver socket to bind to all available interfaces, only supported under JDK 1.4 and up (bela April 1 2005) - MPING: binds now to all interfaces (bind_to_all_interfaces has to be to true), only supported under JDK 1.4 and higher (bela April 1 2005) - Fixed stopping outgoing packet handler (http://jira.jboss.com/jira/browse/JGRP-49) [fix by Steve Nicolai] (bela April 1 2005) - Added MPING. Allows for multicast discovery on a TCP-based stack (bela March 31 2005) - ConnectionTable: - Individual thread per Connection so send() doesn't block - timeout for socket creation (sock_conn_timeout) (bela March 24 2005) - TCPPING: remove myself from pinged members (bela March 23 2005) - Added patch by David Orrell (external addresses for TCP/ConnectionTable) (bela March 17 2005) - Fixed JGRP-42 (MethodCall doesn't correctly handle inheritance) org.jgroups.blocks.MethodCall was updated to walk the class hierarchy and locate method not only in the current class, but also in the superclasses and superinterfaces. There is a new test that contains use cases: tests/junit/org/jgroups/blocks/MethodCallTest.java (ovidiuf Feb 18 2005) - org.jgroups.util.Rsp: changed the sender's type from Object to Address. (ovidiuf Jan 19 2005) - Modified the RpcDispatcher's API to allow more than one ChannelListener to be registered to the underlying JChannel. (ovidiuf Jan 19 2005) - Added MessageDispatcher.getMessageListener() to make possible to multiplex more than one MessageListeners in top of an already configured MessageDispatcher/RpcDispatcher. (ovidiuf Jan 19 2005) - Implemented JGRP-15 (Concurrent startup of initial members without merging): when multiple members are started simultaneously, and no other member is running yet, they form singleton groups, and merge after some time. GOAL: elect a coordinator out of all concurrent (client-)members and avoid a merge. (bela Jan 5 2005) - Fixed JGRP-10 (incorrect computation of wait time in waiting loops) (bela Dec 31 2004, on suggestion from Zac Hansen) - Added system property ignore.bind.address, which ignores the system property bind.address (bela Dec 12 2004) - Changed Util.objectFromByteBuffer() to use ContextObjectInputStream. This uses the classloader of the caller rather than the system classloader (bela Nov 29 2004) - MethodCall: changed Class.getMethod() to Class.getDeclaredMethod(). This allows for invocation of non-public methods, e.g. private methods from within the same class, or package-private methods from within the same package (bela Nov 1 2004) - Added leading byte for Message to indicate which fields are null and non-null. Saves 5 bytes/msg (bela Oct 8 2004) - Dest address is not marshalled any longer; saves 10 bytes/msg (bela Oct 8 2004) - Implemented Streamable for more classes (headers) (bela Oct 8 2004) - Changed FD's BroadcastTask: this could *not* fire in the case where a task was stopped and immediately restarted (bela Oct 7 2004) - Fixed incorrect merging in TUNNEL/GossipClient (bela Oct 7 2004) - Added support for bind.address system property in FD_SOCK (bela Oct 6 2004) - Fix for hanging merge when coordinator/participant crashes or shuns-and-reconnects during merge (bela Oct 5 2004) - Removed object serialization almost entirely; replaced it with Streamable. Changed Message, IpAddress and some Header subclasses (not yet all) to support Streamable. UDP now uses Streamable both for Message and Message lists (used in bundling). Rough performance increase ca 30% (size of serialized data ca. 30% smaller to). (bela Oct 4 2004) - UDP: removed 1 copy operation per sent message by using ExposedByteArrayOutputStream rather than ByteArrayOutputStream. ByteArrayOutputStream.toByteArray() copies the buffer, ExposedByteArrayOutputStream.getRawBuffer() returns a *reference* to the raw buffer plus and offset and length. This is then passed to the resulting DatagramPacket (bela Sept 26 2004) - Added ExposedByteArrayOutputStream (exposes the raw buffer), avoids copying the underlying byte buffer (bela Set 26 2004) - Added Magic{Input,Output}ObjectStream. They use the magic numbers mapping table to provide efficient class descriptors on serialization (bela Sept 24 2004) - Added Simulator and FCTest (bela Sept 23 2004) - More IntelliJ Inspector changes (bela Sept 23 2004) - Various fixes for findbugs and IntelliJ's Inspector (bela Sept 22 2004) - Added CondVar and CondVarTest (bela Sept 22 2004) - Removed Ensemble (bela Sept 21 2004) - Removed some unneeded unit tests (bela Sept 21 2004) - Modified Promise.getResult(): timeout is now implemented correctly. getResultWithTimeout() now also throws a TimeoutException (bela Sept 16 2004) - pbcast.STATE_TRANSFER/STABLE/JChannel: modified state transfer; timeout is now passed down to the protocols (bela Sept 16 2004) - Added MERGE3 (bela Sept 15 2004) - FD_SOCK: added sending message to signal regular termination. Plus, each connection is handled on a separate thread (bela Sept 14 2004) - UDP: added code to prevent port reuse when using ephemeral ports, and ports are reused on some operating systems (e.g. Win2K). Use num_ports_used > 0 to enable this feature (bela Sept 13 2004) - FD_SOCK: added bind address for server socket (bela Sept 10 2004) Version 2.2.7 ------------- - Created distribution (CVS tag = JG_2_2_7_final) - Created distribution (CVS tag = JG_2_2_7) (bela Sept 8 2004) - Re-implemented Membership.sort (using Collections.sort()) (bela Sept 6 2004) - Fixed & simplified the discovery algorithm in TCPPING (bela Sept 4 2004) - Fixed NPE in castViewChange() caused by concurrent reception of leave() and handleLeaveRequest() methods (bela Sept 3 2004) - Added automatic reconnect feature for PullPushAdapter on shun-reconnect sequence (bela Sept 2 2004) - Fixed incorrect determination of coordinator/participant after a merge where new coordinator was *not* the one who executed the MERGE. Suggested by yaronr (bela Aug 31 2004) - Removed checks for self-delivery in FC: flow control needs to apply also for messages sent from X to itself (bela Aug 30 2004) - Added discard_incompatibe_packets to UDP (suggested by Noronha, Vijay" ) (bela Aug 17 2004) - Added checking for system property bind.address (set by JBoss) in TCP (like UDP) (bela Aug 14 2004) - Applied patches by Markus Puetz (reading of systems properties fails in JLNP) (bela Aug 12 2004) - Fixed NPE in MethodCall caused by null args/types (bela Aug 9 2004) - Reverted use of ContextInputStream in Message.getObject() (bela Aug 6 2004) Version 2.2.6 ------------- - Created distribution (CVS tag = JG_2_2_6) (bela Aug 4 2004) - Fixed upProcessingThread in MessageDispatcher: using org.jgroups.util.Queue now (bug#: 998920) (bela Aug 4 2004) - Fixed incorrect start()/stop() sequence of GossipClient in TCPGOSSIP (bela Aug 4 2004) - ChannelFactory: specified new createChannel() method that assumes protocol stack configuration information has already been set on the factory (e.g. at construction) - JChannelFactory: Implemented ChannelFactory.createChannel() and provided type specific constructors; a default constructor was provided for backwards compatability - ChannelException: updated to support exception chaining in a 1.4 VM and to provide 1.4-like exception chaining behavior for a 1.3 VM (jiwils July 31 2004) - Channel: protected access/empty constructors were removed (jiwils July 29 2004) - JChannel: added 5 type-specific constructors for JChannel; deprecated the JChannel(Object) constructor - this means JChannel(null) construction is not supported; use JChannel() instead (jiwils July 29 2004) - ConfiguratorFactory: modified to support JChannel constructor changes (jiwils July 29 2004) - MessageDispatcher: m_upProcessingThread now terminates correctly on stop() (bug#: 998920) (bela July 29 2004) - Added logic to ClientGmsImpl to abort join() when leave() or stop() is called before join() returned (bela July 29 2004) - Util.objectFromByteBuffer() and Message.getObject() now use ContextObjectInputStream. This ensures that the correct context classloader is used (bela July 28 2004) - Fixed bug that - when deadlock_detection was enabled - and scheduler was null (should not be the case), messages were discarded (bela July 26 2004) - Fixed a TUNNEL bug. (ovidiu July 12 2004) - Ran code through Intellij inspector, changed various things (e.g. redundant code etc). (bela July 4 2004) Version 2.2.5 ------------- - Created distribution (CVS tag = JG_2_2_5_0) - pbcast.NAKACK/NakReceiverWindow: messages that have been received in order are sent up the stack (= delivered to the application). Delivered messages are removed from NakReceiverWindow.received_msgs and moved to NakReceiverWindow.delivered_msgs, where they are later garbage collected (by STABLE). Since we do retransmits only from sent messages, never received or delivered messages, we can turn the moving to delivered_msgs off, so we don't keep the message around, and don't need to wait for garbage collection to remove them (bela June 24 2004) - Changed UDP: multicast messages are now sent via mcast_send_sock and received via mcast_recv_sock. This has to better performance when sending/receiving a lot of mcast msgs (bela June 24 2004) - Multicast messages now use the mcast_sock (bela June 12 2004) - Added system property resolve.dns: if set to false, IpAddresses will not attempt to resolve DNS host names, but just print addresses in dotted-decimal format (bela June 8 2004) - UDP: system property bind.address overrides configuration option. Start e.g. with -Dbind.address=192.168.0.10 (bela June 8 2004) - UDP: we cannot remove the header, because further retransmissions will fail. This lead to the 'missing UDP header bug'. Replaced removeHeader() with getHeader() (bela May 18 2004) - Removed all deprecated methods from MethodCall. - Changed all dependent classes (RpcDispatcher, RpcProtocol etc) - Removed MethodLookup, MethodLookupJava and MethodLookupClos (bela May 14 2004) - Properties for a JChannel constructor can now be regular files (bela May 12 2004) - Removed deprecated methods from RpcProtocol/RpcDispatcher - Made MethodLookupJava the default in RpcProtocol/RpcDispatcher - RequestCorrelator does now not use Scheduler when deadlock_detection=false, but calls handleRequest() directly (bela May 12 2004) - Created version 2.2.5 (bela May 12 2004) Version 2.2.4 ------------- - Created distribution (CVS tag = JG_2_2_4) - Updated NakReceiverWindow (used by NAKACK): replaced received_msgs and delivered_msgs (lists) with TreeMaps. This removes linear cost for traversals, and makes lookups constant. (bela May 6 2004) - Changed NAKACK.sent_msgs to use TreeMap (sorted seqnos as keys) (bela May 3 2004) - Removed log4j directory (bela May 2 2004) - Solved bug #943709 (yaronr April 28 2004) - Added class org.jgroups.util.ReentrantLatch (yaronr April 28 2004) - Solved bug# 939253 Updated MessageDispatcher.java (MessageDispatcher + ProtocolAdaptor). (yaronr April 28 2004) - Optimization in RpcDispatcher: before marshalling the args and handing the call over to the superclass, we make sure the destination list is not empty: if empty we return immediately. (bela May 1 2004) - Fixed bugs #943480 and #938584. STABLE handles SUSPEND_STABLE and RESUME_STABLE events, and STATE_TRANSFER sends SUSPEND_STABLE before fetching the state and RESUME_STABLE after receiving the state, to prevent message garbage collection from going on during a state transfer (bela April 28 2004) - Fixed bug #943881 (patch by Chris Wampler) (bela April 28 2004) - Fixed bug #938584 (NAKACK.retransmit problem) (bela April 27 2004) - Changed default format for XML properties to a simpler format (similar to JBoss) Modified all XML files in ./conf 'XmlConfigurator -new_format' can be used to convert an old XML format into a new one (bela April 26 2004) - Created new version (bela April 26 2004) Version 2.2.3 ------------- - Added -new_format flag to XmlConfigurator; dumps input XML file in the format used by JBoss (bela April 24 2004) - Added flag use_scheduler to RequestCorrelator, which allows one to bypass the Scheduler altogether. If deadlock detection is enabled, however, the Scheduler is needed, therefore use_scheduler is set to true. Use the method setUseScheduler() to enable/disble this (bela April 23 2003) - Changed handling of Protocol.setProperties(). Each Protocol has to call super.setProperties() if it wants to store the properties in this.props now (bela April 23 2004) - Made several protocols less verbose (info --> debug level) (bela April 22 2004) - Fixed NAKACK retransmission bug (#938584) (bela April 22 2004) - Minor change in Scheduler: replaced sched_thread.interrupt() with throwing of exception (bela April 15 2004) - Created distribution (CVS tag = JG_2_2_3) (bela March 31 2004) - Conversion of Trace-based logging system to commons-logging. Removed log4j subdir, removed org.jgroups.log package (bela March 29 2004) - Added commons-logging.jar (bela March 29 2004) - Created new version (bela March 29 2004) Version 2.2.2 ------------- - Made UpHandler/DownHandler threads in Protocol daemon threads (bela March 17 2004) - MessageDispatcher: handling SET_LOCAL_ADDRESS, cache local_addr correctly (suggested by Yaron Rosenbaum) (bela March 16 2004) - Added more trace information to RpcDispatcher, MessageDispatcher, RequestCorrelator and GroupRequest (bela March 9 2004) - Fixed state transfer for DistributedHashtable (should do it the same way for Channel.getState()) (bela March 8 2004) - Created new version (bela March 8 2004) Version 2.2.1 ------------- - Created distribution (CVS tag = JG_2_2_1) - Added bundling capability to UDP. Bundles multiple smaller message into a larger one and then sends the larger message. Waits with sending until n bytes have accumulatd or n ms have elapsed, whichever is first (bela March 1 2004) - Added first version of new fragmentation protocol (FRAG2). Main 2 advantages over FRAG: doesn not serialize message and does not copy message (deferred until the message hits the transport). FRAG2 is about 3 times faster than FRAG. (bela Feb 25 2004) - Added offset and length fields to Message, plus a new constructor and a new setBuffer() method. This allows for multiple messages to share a common byte buffer (avoiding unnecessary copying). The semantics of Message.getBuffer() have changed: previously it returned a reference to the buffer, now it returns either a reference (offset/length are not used) or a copy (offset/length are used). If you want the previous semantics, use Message.getRawBuffer(). (bela Feb 25 2004) - Changes to log4j.Trace: we now use the fully qualified classname and methodname of the caller, e.g. "org.jgroups.protocols.PING.down" (bela Feb 24 2004) - RequestCorrelator: catching exceptions caused by non-serializable return value, return them as exceptions. This fixes bug# 901274: group requests would *not* return (not even a null value) if the invoked method threw an exception, causing callers to hang for a timeout. Now the exception is returned. (bela Feb 20 2004) - Added NIO version of ConnectionTable1_4; works with JDK 1.4 or later. TCP1_4.java protocol uses ConnectionTable1_4 and tcp1_4.xml config file for NIO based TCP stack. (akbollu Fed 18 2004) - Added send_buf_size and recv_buf_size to TCP (bela Feb 11 2004) - Fixed problem in ConnectionTable/TCP: when a message was sent to a crashed member, the connection establishment would take a long time. Now we can skip messages to suspected members (bela Feb 9 2004) - Added tcpgossip.xml config file (bela Jan 21 2004) - Fixed bug in IpAddress: access to address cache is now synchronized (caused ConcurrentAccess ex). (bela Jan 21 2004) - ConnectionTable: new sockets now use the bind_addr (used to ignore it) (bela Jan 18 2004) - Added network optimization to PING and TCPPING for FIND_INITIAL_MBRS event when initial hosts is used, rather than send to all, skip send for initial members already in the view (thomas sorgie, Jan 17, 2004) - Changed default port_range from 5 to 1, auto port escalation is a nice feature, but it is wasteful and should be off by default (thomas sorgie, Jan 17, 2004) - Removed unused checkForResult() from util.Promise which has misleading results when result is null, replaced with hasResult() which returns a boolean indicating whether a result if available (thomas sorgie, Jan 17, 2004) - Removed exceptions from getObject()/setObject() again, replaced with IllegalArgumentException (bela Jan 16 2004) - Added exception(s) to Message(Address, Address, Serializable), getObject() and setObject() (bela Jan 15 2004) - Removed direct_blocking from FC; this is now default (bela Jan 8 2004) - Fixed failed merging when all up/down threads are disabled (default-minimalthreads.xml) (bela Jan 8 2004) - Removed send_sock from UDP and streamlined sending. Now there is only 1 socket for sending unicast and multicast messages and for receiving unicast messages, and 1 socket for receiving multicast messages (bela Jan 6 2004) - Added use_outgoing_packet_handler to UDP. If enabled, all outgoing packets will be handled by a separate thread. This results in great perf gains for multicasts sent to self (bela Jan 2 2004) - Added min_size property to SIZE (bela Dec 26 2003) - Replaced HostInfo with IpAddress in PING/TCPPING (bela Dec 22 2003) - Added get_interfaces script to 'bin' directory: discovery of all network interfaces on a box. Needs 1.4 to work (bela Dec 18 2003) - JChannel: fixed handling of additional_data: additional_data was not retained across shuns/reconnects, so the additional_data would not be available. Run AddDataTest for unit testing. (bela Dec 15 2003) - Added scripts for pinging all members given an mcast address and port (probe), and for determining the fragmentation size (frag_size). (bela Dec 12 2003) - Fixed bug #854887. When AUTOCONF determines the correct fragmentation size (e.g. 65507 on my WinXP laptop), fragments are cut down to 65507 bytes max. However, those fragments are regular messages, so we add some headers, plus serialization adds its own stuff, so we end up with more than 65507 bytes, causing UDP to fail (AUTOCONF measured exactly how many bytes could be sent over the UDP datagram socket). Solution: add a buffer to the frag size determined by AUTOCONF (e.g. 1000 bytes). This will be subtracted from the computed frag size, so we have enough space. The buffer is always more or less constant, as headers or serialization is constant too. (bela Dec 12 2003) - Added initial_hosts support for PING (copied from TCPPING) (bela Dec 11 2003) - Scheduler can now handle incoming requests concurrently (if concurrent_processing is true). By default, this is false: setting this to true can destroy the ordering properties provided by a protocol stack, e.g. in the case of total or causal order. FIFO (default) should be fine, but please know what you are doing ! (bela Dec 10 2003) - Modified SMACK protcol: handling of CONNECT/CONNECT_OK and DISCONNECT/DISCONNECT_OK was incorrect. Worked only on UDP. Now works on TUNNEL as well. Also added a new protocol config smack_tunnel.xml to conf. (bela Dec 10 2003) - ProtocolStack.startStack() and stopStack() now use START/START_OK and STOP/STOP_OK pairs (again). Protocol will handle these events internally. This prevents subtle timing problems, e.g. a CONFIG event being passed by start() invocations on the protocols. Fixes bug #854855 (bela Dec 5 2003) - Fixed bug in pbcast.GMS which incorrectly generated view when many members left at exactly the same time. Bug was reproduced in ConnectStressTest. We may have to revisit huge concurrent JOINs later (30 work fine in the test now) (bela Nov 20 2003) - Added ConnectStressTest unit test (bela Nov 20 2003) - pbcast.GMS and shunning: only shun if this member was previously part of the group. avoids problem where multiple members (e.g. X,Y,Z) join {A,B} concurrently, X is joined first, and Y and Z get view {A,B,X}, which would cause Y and Z to be shunned as they are not part of the membership (bela Nov 20 2003) - Added BoundedList to org.jgroups.util (bela Nov 20 2003) - Fixed NullPointerException in CoordGmsImpl (252) caused by null Digest (bela Nov 20 2003) - Fixed JChannel._getState(): when the state transfer protocol is just underneath the Channel, and is configured to use the caller's thread, then we always run into the state transfer timeout for first member. (bela Nov 13 2003) - GossipRouter can be managed as MBean under the JBoss JMX server. To build a dist/jgroups-router.sar service archive, run build.sh jboss-service. The router deploys as 'jgroups:service=GossipRouter' and it can be further managed from http://:8080/jmx-console (ovidiuf Oct 29 2003) - Added compression protocol (COMPRESS) and example stack (compress.xml) (bela Oct 17 2003) - Added GossipRouter. It is based on the Router code and it is able to answer Gossip requests too. Can completely replace Router/GossipClient. It is backward compatible, clients shouldn't be aware of change. (ovidiuf Oct 15 2003) Version 2.2 ----------- - JChannel.close(): queue will not be reset until re-opened (bela Sept 25 2003) - Added flushEvents() to ProtocolStack (bela Sept 24 2003) - New JUnit test case AddDataTest to test setAdditionalData() in IpAddress (bela Sept 22 2003) - Added remove_mutex to Queue: waitUntilEmpty() waits on the remove mutex, which is notified by remove(), remove(long timeout), removeElement(Object el) and close() and reset() (bela Sept 22 2003) - Added resource handling, e.g. instead of full URLs a simple XML file name can be passed. getResourceAsStream will be used on the thread's classloader to find the resource. Example: java org.jgroups.demos.Draw -props default.xml (default.xml has to be in the classpath) (bela Sept 11 2003) - JavaGroups was renamed to JGroups. Package is now org.jgroups. The final 2.1.1 version was tagged with the JG_FINAL_SEPT_8_2003 tag. (bela Sept 8 2003) Version 2.1.1 ------------- - Added MERGEFAST protocol: really simple implementation of a merge protocol which allows for faster merging. Coordinators attach info saying "I'm the coord", when other coords (this means we have a partition) see it, a merge protocol is initiated right away (bela Aug 25 2003) - Added additional_info handling for TCP (already present for UDP, added in 2.0.6) (bela Aug 4 2003) - Applied patches sent by Alfonso to replicated building blocks (only send mcast when more than 1 member, else apply change directly (bela Aug 1 2003) - Added adapttcp TCP-based tests (bela July 25 2003) - Added properties override to JChannel: use -Dforce.properties=file:/myprops.xml to force a given protocol stack spec to be used (bela July 24 2003) - Eliminated unneeded message copying in NakReceiverWindow.remove() when inserting delivered message into delivered_msgs (bela July 23 2003) - Fixed bug #775120 (channel not connected on first view) (bela July 21 2003) - Applied patch (submitted by Darren Hobbs) for incorrect voting to ClientGmsImpl (both pbcast and regular branch) (bela July 15 2003) - Added performance tests created by Milan Prica (prica@deei.units.it) under org.jgroups.tests.adapt (bela July 12 2003) - Created (bela July 12 2003) Version 2.1.0 ------------- - Created 2.1 distribution (CVS tag: JG_2_1) (bela July 5 2003) - Added timeout latency value to FD, fixes annoying (but not incorrect) "missing first heartbeat" problem (bela on behalf of Bas Gooren June 27 2003) - Fixed bind_port problem in UDP (bela on behalf of Bas Gooren June 27 2003) - Fixed deadlock (bug is #761804). Changes in RequestCorrelator. (bela June 27 2003) - Change to pbcast.STABLE: max_bytes property allows for more aggressive message garbage collection. It keeps track of the number of bytes received from everyone. When max_bytes is exceeded, a STABLE message is multicast. This can be used in *addition* to, or instead of desired_avg_gossip. (bela June 26 2003) - Moved pbcast.STABLE on top of UNICAST/NAKACK. The reason is that STABLE should be reliable (message retransmission). STABLE now assumes NAKACK is *below* rather than above it. This required changes to all default protocol spec XML files, plus hard-coded properties in demo programs. (bela June 26 2003) - Created (bela June 26 2003) Version 2.0.8 ------------- - First version of FC (flow control) protocol based on credit system. PerfTest works (no OutOfMemory exceptions), but it needs to be faster (bela June 25 2003) - On JChannel.open() a new ProtocolStack is created, previously the existing one was reused. This caused multiple timers to be created on shun-reconnect (bela June 12 2003) - CloserThread would not terminate in Draw demo. Fixed by running mainLoop() in separate thread (bela June 12 2003) - On shun-reconnect, the ProtocolStack's timer was not restarted (start() was not called), therefore FD would not restart its heartbeat. This fixes bug #753327. (bela June 12 2003) - Added new building block: DistributedQueue. Added unit and stress tests as well. (romuald June 4 2003) - Added diagnostics interface to query all members in a subnet using the same IP multicast address and port (UDP). Use org.jgroups.tests.Probe to execute (bela June 3 2003) - Fixed CONNECT_OK bug in TOTAL_TOKEN (bela June 2 2003) - Added PerfTest (bela May 27 2003) - FD: fixed problem when correct member was suspected, but didn't receive VERIFY_SUSPECT ack. Caused that member not to be pinged anymore. If the member crashed later on, the crash would not be detected. (bela May 16 2003) - JChannel: fixed bug in shun-reconnect sequence: channel_name was nulled, therefore reconnect created a unicast channel. Now CloserThread remembers the old channel_name and uses it to reconnect. (bela May 15 2003) - Fixed constructors of ChannelException so that ChannelException.getMessage() returns non-null value (bela April 17 2003) - Added exceptions to 2 of the 4 constructors of DistributedHashtable (bela April 17 2003) - Added up_thread_prio and down_thread_prio to Protocol. This allows users to set thread priority for each thread (up,down) in a protocol. (bela April 15 2003) - Added versioning to TCP. In addition to the cookie, we now also send the version. If the cookie doesn't match we discard the message. If the versions don't match, we log a warning message (bela April 6 2003) - Added versioning to UDP. Message with different versions will be flagged (warning), but not discarded. This change changes the wire format of message, so that previous versions will not work with this version (bela April 4 2003) Version 2.0.7 ------------- - Added serialVersionUID to MethodCall. Reason is that jikes and javac did not generate the same serialVersionUID, causing problems between JGroups instances not compiled with the same compiler. Currently MethodCall is the only instance where this problem occurred. However, I didn't do exhaustive tests on this, there are probably more instances. Since most users use the same codebase, this should not be a problem though. (bela March 30 2003) - Added way of defining method via (a) class types (Class[] array) or (b) signature (String[]) - Modified RpcDispatcher and RpcProtocol to provide 2 new methods for each - Added RpcDispatcherSpeedTest (bela March 30 2003) - Removed mcast_bind_addr again: I had thought 0.0.0.0 listens on *all* available interfaces, but this is not true. The semantics are that the ANY address (0.0.0.0) means that the first available interface is bound to. We could have multiple mcast sockets, each on a different interface, joining the same group though; this is possible. (bela March 28 2003) - Added mcast_bind_addr parameter to UDP, by default multicast socket will now listen on all interfaces (0.0.0.0 'any' address) - Send socket in UDP now doesn't take a bind_addr argument any longer; by default the IP code will dynamically choose the interface based on the destination address (bela March 27 2003) - Fixed bug in connect() - disconnect() - connect() sequence. Only occurred when reconnecting to a different channel name. Fixed by resetting all channel state (JChanel.init()). (bela March 21 2003) - Modified code for TCP/UDP: when a channel name is null, an outgoing message will not have a header. (bela March 20 2003) - Fixed bug which caused join() to fail (null address) on Channel.close(), Channel.open(), Channel.connect() sequence. (bela March 18 2003) - Added unicast capability for channels. The connect() method can now have a null channel name, which will *not* invoke the JOIN protocol. - Added org.jgroups.tests.UnicastChannelTest (bela March 16 2003) - Uncommented code in UDP.createSockets() which caused binding to all interfaces (bind_addr == null). This caused incorrect addresses (e.g. 0.0.0.0:1234), which were not identical across machines. (bela March 13 2003) - Added patch by Roland Kurman. If a protocol stack has only 1 protocol, then the JChannel.connect() method will fail as the connect_mutex will be notified before connect_mutex.wait(). Added some vars to check for this case. Dito for disconnect(). (bela March 13 2003) - Added BSH protocol. Allows processing of Java code sent from a remote member. Executes any Java code and sends back the result. (bela March 8 2003) - Added beanshell (www.beanshell.org) JAR file (bela March 8 2003) - Added optional ENCRYPT protocol (works with JDK1.4) using a 3rd party provider. Jar and protocol file with ".xml" being added on 03/06/2003. (whizkid_bay Mandar Shinde) - Removed synchronized from RequestCorrelator.removeEntry(). Not needed, and caused bug #690606 (bela Feb 28 2003) - Removed synchronized from GroupRequest.execute() (not needed as we synchronized further down the code anyway) (bela Feb 28 2003) - Replaced print statements with Trace calls in some of the WAN support classes. Modified log4j Trace wrapper to report also the module the log was generated for. (ovidiuf Feb 19 2003) - Fixed a bug in TUNNEL that caused the clients and the Router to deadlock under high-volume traffic. (ovidiuf Feb 05 2003) - A custom name/path for the magic number file can be specified using the property "org.jgroups.conf.magicNumberFile". Default is "jg-magic-map.xml". (ovidiuf Jan 30 2003) - Added contentsSet() and contentsCleared() callback to DistributedHashtable Notification interface (bela Jan 30 2003) - pbcast.NAKACK will not remove header when handling retransmitted message. Otherwise further retransmit requests would have failed (bela Jan 29 2003) - Removed unused casts (bela Jan 28 2003) Version 2.0.6 ------------- - Added FLOW_CONTROL protocol to throttle sender when receivers are lagging behind. (Ananda Jan 20 2003) - Added ChannelClosedException and ChannelNotConnectedException to Channel.getState() and Channel.getStates() (bela Jan 20 2003) - Various fixes related to the connect-disconnect-connect sequence: TUNNEL sends a null local address up the stack when disconnected, GossipClient provides a new timer and also cleans up the groups map when stopped, pbcast.NACKACK cleans up the NakReceiverWindows when disconnected and resets the seqno. (ovidiuf Jan 16 2003) - Added capability to stuff additional data into an IpAddress. Use the CONFIG event to do this *after* channel creation but *before* connecting. IpAddress.getAdditionalData()/setAdditionalData() can be used to manage that data. This is useful e.g. when we want logical addresses rather than IpAddresses (bela Jan 14 2003) - Updated concurrent.jar to version 1.3.2 (bela Jan 14 2003) - UDP: when multicasting a message, if loopback=true, we loopback a copy directly up the stack (but still multicast it). Once we then receive our own multicast, we discard it. This is supposed to solve some problems with Windows media sensing and disabling of interfaces (bela Jan 9 2003) - TCPPING: automatically add self to initial_hosts lists if omitted (bela Dec 26 2002) - Fixed bug in UNICAST/FRAG (causing merge protocol to fail sometimes): whenever we initiate a merge, we have to send a unicast message to a member not currently in the group. If we receive a view or get a retransmit() before we get the new view, the members will remove each other's connection table entry, and thus unicast messages to the other side will fail. We now use Util.determineLeftMembers() to correctly determine the left members between 2 views, and remove only those. (bela Dec 23 2002) - Channel.connect() will now throw an exception if the protocol stack couldn't be initialized correctly (e.g. local address is null). Will wait for LOCAL_ADDR_TIMEOUT ms until local_addr is non-null (can be overridden by setting system property "local_addr.timeout"). (bela Dec 23 2002) - Implemented RpcDispatcher.callRemoteMethods() correctly: destination list is now correctly observed (used to be ignored before) (bela Dec 15 2002) - Added persistence manager (org.jgroups.persistence) (whizkid_bay Dec 11 2002) - Replaced START/START_OK events with start() Replaced STOP/STOP_OK and CLEANUP/CLEANUP_OK events with stop() (see description of Protocol.java for details) (bela Dec 10 2002) Version 2.0.5 ------------- - Added Proxy1_4 to org.jgroups.util (bela Dec 5 2002) - Added concurrent.jar to lib directory (bela Dec 4 2002) - Made (almost) all threads daemon threads (bela Dec 2 2002) - Added first version of TransactionalHashtable (no functionality yet) (bela Nov 24 2002) - Added ReplicationManager, ReplicationReceiver and Xid to org.jgroups.blocks. This is the building block for transactional cache (bela Nov 20 2002) - Changed signature of state transfer in Channel (returnState()) and MessageListener (getState(), setState()) from Object to byte[]. Reason: classloader issues, some clients need to do the serialization themselves (e.g. in different classloaders) (bela Nov 15 2002) Version 2.0.4 ------------- - Fixed problem with client joining and immediately calling getState(): now pbcast.GMS immediately sends a TMP_VIEW so correct digest will be returned to client. This only happened when client's GET_STATE request reached coordinator *before* coord received its own VIEW_CHANGE multicast (bela Oct 28 2002) - DistributedHashtable: moved MethodCall creation from remote method invocation (once/call !) to instance level (once/instance) (bela Oct 17 2002) - Made TOTAL.Header public (needed by externalization) (bela Oct 15 2002) - Made serialization of UdpHeader more efficient (bela Oct 11 2002) - Moved connection reaper from Connection to ConnectionTable (bela Oct 10 2002) - Added log4j-1.2.6.jar (bela Sept 24 2002) - Version 2.0.4 created (bela Sept 21 2002) Version 2.0.3 ------------- - Version 2.0.3 released (bela Sept 21 2002) - Fixed bug in Queue which caused dangling tail on removeElement() (bela Sept 20 2002) - Added adapter from Trace to log4j ($JG_ROOT/log4j) (bela Sept 17 2002) - Fixed problem with hangs in RpcDispatcher.callRemoteMethods(): seems that Scheduler's thread pool of 10 was maxed out, causing hangs to to missing responses. Increased thread pool to 128. (bela Sept 16 2002) - Added connection reaping in ConnectionTable/TCP. Use of properties reaper_interval and conn_expire_time determines the reap times. (bela Sept 16 2002) - Removed SUSPECT events from TCP when ConnectionTable closes connection. Added FD to tcp.xml instead (bela Sept 15 2002) - Made MessageDispatcher ride on top of PullPushAdapter in addition to Channel (bela Sept 13 2002) - Version 2.0.3 created Aug 27 2002 (bela Aug 27 2002) - Upgraded Xerces jars from 2.0.0 to 2.1.0 (bela Aug 31 2002) Version 2.0.2 ------------- - Version 2.0.2 released Aug 26 2002 (bela Aug 26 2002) - Fixed bug in AckMcastSenderWindow/NAKACK: whenever we did a rebroadcastMessages(), and one of the members crashed in the meantime, the call waited forever. This was especially the case when we received a SUSPECT *before* the waitUntilAcksReceived() was called. Fix: we maintain a bounded list of suspects and - when there is a retransmit request to a member who is in that list - we remove that member from the retransmission table, causing waitUntilAcksReceived() to return. Additionally, one could set rebroadcast_timeout, so that waitUntilAcksReceived() always returns after some bounded time (bela Aug 22 2002) - Fixed bug in SMACK (retransmission to dead member did not stop) and reduced number of JGroups-related threads from 9 to 4 in smack.xml. (bela Aug 22 2002) - Fixed ClassCastException bug in PERF (bela Aug 22 2002) - Added SMACK, FD_SIMPLE and smack.xml. SMACK (= Simple Multicast ACK protocol) allows for membership-less reliable (positive) acknowledgment-based multicasting (bela Aug 21 2002) - Added VotingAdapter, TwoPhaseVotingAdapter as simple distributed agreement building blocks; LockManager interface and DistributedLockManager that uses TwoPhaseVotingAdapter to acquire and release locks. (rrokytskyy Aug 18 2002) - Modified GroupRequest to use Transport instead of RequestCorrelator. Added test program GroupRequestPull (bela Aug 16 2002) - Fixed bug in FD: num_tries was incremented twice in the same loop (bela Aug 15 2002) - Fixed UDP.sendDummyPacket() (bela Aug 15 2002) - Added capability to register multiple MessageListeners with PullPushAdapter (bela Aug 13 2002) - Added org.jgroups.tests.McastDiscovery1_4 (bela Aug 1 2002) - Added disable_initial_coord property to both GMS and pbcast.GMS: when set to true, new members will not become coordinators on empty initial membership, but rather retry fetching the membership. Is set to false by default (bela Aug 1 2002) Version 2.0.1 ------------- - Added unit tests to build.xml and fixed all unit test cases ( bela July 19 2002) - Fixed bug in FD which caused no SHUN message to be sent (bela June 25) - AckSenderWindow now is able to send messages itself, used by sliding window protocol (if enabled) (bela June 4 2002) - Removed Trace from TimeScheduler (caused significant delays in task execution times) (bela June 3 2002) - Added sliding window to AckSenderWindow (used in UNICAST) (bela June 1 2002) - Replaced retransmission thread in AckSenderWindow with Retransmitter (also used by NakReceiverWindow) (bela May 29 2002) - AckReceiverWindow and AckSenderWindow: replaced TreeSets with HasMaps. More efficient insertion of and access to elements (bela May 28 2002) - New version of UnicastTest: measures performance of sending unicast messages from a sender to a receiver (bela May 26 2002) - References of messages are now stored instead of copies (see protocols/DESIGN for details) (bela May 26 2002) - Added AUTOCONF protocol: automatically configure network buffer and fragmentation sizes (bela May 14 2002) - Added handling of null values to Marshaller (bela May 3 2002) - Added bare-bones.xml for protocol stack consisting only of UDP and UNICAST (bela May 2 2002) - Added use_packet_handler property to UDP (bela April 26 2002) - Converted headers from Stack to HashMap (bela March 31 2002) - Modifications to xmit algorithm in pbcast.NAKACK: large messages are now avoided (bela March 20 2002) - Added JMS protocol and jms.jar (required) (rrokytskyy March 20 2002) - Removed buggy loopback code from UNICAST and moved (correct) code to UDP. If 'loopback' is true in UDP, messages sent to self are not put on the network, but redirected up the stack immediately. (bela Feb 27 2002) - Added new version of JUnit JAR file. Replaced all occurrences of assert() with assertTrue(). 'assert' is a keyword in JDK 1.4 and cannot be used any longer. (bela Feb 13 2001) Version 2.0 ----------- - Added ReplicatedTree and ReplicatedTreeDemo (bela Feb 10 2002) - Added SpeedTest (bela Feb 2002) - Modified UDP to allow connect() followed by disconnect(). (bela Dec 23 2001) - Added BLOCK_SEND and UNBLOCK_SEND events: JChannel.send() will block after BLOCK_SEND and unblock after UNBLOCK_SEND event. Used by flow control protocols (bela Dec 18 2001) - Fixed bug in Scheduler which caused the 491327 bug (hang) (bela Dec 14 2001) - Fixed bug in pbcast.NAKACK which caused large state transfers to fail (bela Dec 13 2001) - Checked initial version of JGroups 2.0 into the CVS (bela Dec 12 2001) - Removed Timer and TimerTask (supported from JDK 1.3) (bela Dec 7 2001) - Moved to new directory structure - Adoption of SUN naming conventions (e.g. lowercase package names, method names starting with lowercase letter etc) (bela Dec 4 2001) Version 1.0.1 ------------- - Added CAUSAL protocol (vlada Nov 16 2001) - Added DeadlockTest (igeorg Nov 15 2001) Version 1.0 ----------- - Updated some of the demos (bela Nov 1 2001) - Fixed bug in pbcast.NAKACK: seqnos are not reset to 0 on SetDigest. This caused a sender to start at 0 after a SetDigest(), e.g. sender was at 10 and got a digest. Its next 10 messages would be discarded (already seen) (bela Nov 1 2001) - FD_SOCK: fixed bug where lost SUSPECT can screw up membership. Now SUSPECT messages are transmitted reliably (retransmission if lost). Also removed Thread.interrupt() on thread that reads from InputStream (did not work correctly on some platforms, e.g. Windows and Linux JDK 1.3.1) (bela Oct 30 2001) - Modified Draw demo: background is now correctly repainted (using double-buffering) (bela Oct 30 2001) - Added magic cookie for connection sends/receives in ConnectionTable. Telnetting to the same port as the one the CT listens on will not have an effect, telnet will be closed by CT (bela Oct 26 2001) - Removed potential deadlock between synchronized Send() and Receive() in ConnectionTable (bela Oct 26 2001) - Fixed bug in ConnectionTable. Caused connection termination by Windows peers not to be detected. (bela Oct 26 2001) - Preliminary version of merge protocol to pbcast.GMS/pbcast.CoordGmsImpl. Used with MERGE2 protocol. (bela Oct 25 2001) - Fixed properties for Draw demo (see todo.lst) (bela Oct 24 2001) - Added -no_channel flag to Draw demo. If set, Draw doesn't use a channel. Can be used to see whether the drawing works, independent of JGroups. (bela Oct 22 2001) - Replaced {ViewId,Vector} in pbcast.GMS with View (bela Oct 22 2001) - Ported Draw to Swing. Still has a redraw problem, because we don't call super.paintComponent() to preserve points. Possible solution: keep all pixels in an array, update the array on reception of DrawCommand and just draw the array in paintComponent() (bela Oct 17 2001) - Added MERGE2 protocol. To be used in conjunction with pbcast/GMS (bela Oct 17 2001) - Fixed bug in JGroups.JavaStack.Protocols.pbcast.NakAckHeader which caused externalization to be incorrect (bela Oct 12 2001) - Fixed bug in FD_SOCK under Linux JDK 1.3.1 (used to work under JDK 1.2.2): pinger thread could not be interrupted using Thread.interrupt(). Now (only if Linux) the ping socker is closed to interrupt the thread. Test whether Thread.interrupt() works to interrupt thread during InputStream.read() is JGroups.Tests.InterruptTest. Addition: this bug is known by SUN (4344135) and currently occurs only in the Linux JDKs 1.3.0 - 1.3.1. Should be fixed soon. Once this is done, we can revert to interrupt() only. This is better because it doesn't cause a SUSPECT event to be generated from closing the peer socket. This event will be discarded by VERIFY_SUSPECT, but it is still additional work. Also, if VERIFY_SUSPECT is missing we are in trouble. (bela Oct 10 2001) - Removed is_linux from UDP.java (bela Oct 10 2001) - Added GossipServer, GossipClient. There used to be a GossipServer up to version 0.9.4, but was subsequently merged with JRouter into Router. The reason for putting it back is that we just want gossiping functionality, not gossiping and routing functionality. (bela Oct 4 2001) - Modified Common.Trace. Added a separate STDOUT/STDERR object for each level. Previously we had one singleton STDOUT and STDERR object. This caused multiple traces with different levels to overwrite each other's levels, e.g. default_output=WARN STDOUT trace0=Foo DEBUG STDOUT would cause either WARN or DEBUG to be printed, but not both. (bela Sept 27 2001) - Removed MessageProtocol.MessageProtocolHeader. Demux for msg/rpc traffic is done now whether RequestCorrelator.Header is present in the msg. (igeorg Aug 10 2001) - Modified pbcast.STABLE. Now stable task terminates after certain idle time (see max_gossip_runs) (bela Aug 3 2001) - Added SIZE layer, Measures the size of a message and prints it. (bela June 15 2001) - Added Tests/Ping.java. Discovers initial members on any group (bela June 12 2001) - Removed initial_mbrs_timeout from GMS. Now, we wait until FIND_INITIAL_MBRS returns (needs PING) (bela June 12 2001) - Added FD_SOCK protocol (bela May 28 2001) - Modified ConnectionTable to use 1 TCP connection instead of 2 between peers (bela May 14 2001) - Modified FD to use TimeScheduler instead of own thread (bela May 11 2001) - Integrated changes made by igeorg to AckMcastSenderWindow and NakReceiverWindow. Modified pbcast.NAKACK to provide TimeScheduler to *Window classes (bela/igeorg May 9 2001) - Added FLOWCONTROL layer (i-scream May 9 2001) - Removed ProtocolStack.Timer (use ProtocolStack.TimeScheduler) (bela May 8 2001) - Removed SortedList: replaced with java.util.TreeSet (affected classes: AckSenderWindow and AckReceiverWindow) (bela May 8 2001) Version 0.9.9.9.1 ----------------- - Fixed severe bug in NAKACK (bela May 11 2001) Version 0.9.9.9 --------------- - Changed if(trace) to if(Trace.trace) (bela May 4 2001) - Added setting of receive and send buffer sizes for sockets in UDP. This has an effect on FRAG (see DESIGN for explanation) (bela April 24 2001) - Made Address and Header Externalizable (changes to all subclasses of Header) (bela April 17 2001) - Moved TOTAL.java --> TOTAL_OLD.java and TOTAL_JOHN.java --> TOTAL.java - Added ./Tests/FragTest: uses ProtocolTester to test FRAG (bela April 13 2001) - Added bind_addr and bind_port properties to UDP (bela April 6 2001) - Modified NotificationBus: uses ucast to coordinator to fetch state instead of mcasting and then processing responses from each member (bela April 5 2001) - Moved NotificationInfo --> NotificationBus.Info (bela April 5 2001) - Reduced traffic generated by FD_PID. A new member used to mcast its query for PIDs and every member replied with its add:pid. Now, a new member asks the coordinator for the cache, updates its own cache and then mcasts its addr:pid, so other members can update their caches. (bela April 5 2001) - Fixed ClientGmsImpl.DetermineCoord(): now a majority is needed to determine the coordinator from the responses. This would join a new member to the majority group in case of a split group (e.g. caused by a network partition) (bela April 4 2001) - Added fix for "Last Message Dropped" bug in pbcast/STABLE and pbcast/NAKACK (see pbcast/DESIGN for details) (bela April 3 2001) - Added Tests/DigestTest (bela April 3 2001) - Added SetTrace() to Protocol. Allows a developer to dynamically turn tracing on/off in a running protocol stack (bela March 30 2001) - Removed Util.ShortName(). IpAddress now by default prints short form. More efficient and avoids a lot of string creation. (bela March 29 2001) - Modified Protocol.java: no the up_thread= and down_thread= can turn the up/down thread handler on/off (bela March 27 2001) - Added pbcast/STABLE.java. Slight modification of regular STABLE protocol, makes it more efficient for local groups (bela March 26 2001) - Added JavaStack/ProtocolTester.java (bela March 23 2001) - Added pbcast/NAKACK protocol. This is similar to the regular NAKACK, but doesn't include FLUSH with resetting of sequence numbers. Upon joining, a new member will get the highest seqnos of all members (from the coordinator) and set its NakReceiverWindows accordingly. (bela March 23 2001) - Modified pbcast GMS protocols: removed RpcProtocol. All GMS protocols now extend Protocol instead of RpcProtocol. This should make parallel startups of members faster (bela March 20 2001) - Added ANT based build system (fhanik March 20 2001) - Modified FD_PID (fixed bug in membership adjustment when most members die) (bela March 15 2001) - Added Trace.init(): read tracing properties from file (bela March 9 2001) - Added FD_PID: uses process IDs to monitor for process failure. For purely local groups. Version 0.9.9.8 --------------- - Added FD_PID protocol. Failure detection based on process ids. Works only for local groups (all members are on the same host), and only on /proc based systems (e.g. Linux, Solaris) (bela March 2 2001) - Fixed bug in PBCAST where singleton members did not garbage-collect their messages (bela Feb 15 2001) - Added bounded buffer to PBCAST: now PBCAST-related messages (like gossips, etc (not multicast msgs !)) will be discarded if buffer is full. This is not a problem since gossips in later rounds can make up for the loss of previous gossips. - Added simple FD protocol in ./pbcast. Requires PBCAST (bela Feb 14 2001) - Fixed problems where multiple members (including coordinator) crash simultaneously (fixed in ./Protocols/ParticipantGmsImpl.java and ./Protocols/pbcast/ParticipantGmsImpl.java) - Added probabilistic failure detection protocol (FD_PROB.java) - Added VERIFY_SUSPECT protocol. Verifies SUSPECT messages generated by the FD layer before passing it on to the GMS layer. Does this by attempting to contact suspected member. If attempt is successful, event is discarded, otherwise (a timeout occurs), event is passed up the stack. (bela Feb 13 2001) - Added new Header class: all headers have to extend this class and implement Size(). Replaced all occurrences of AddHeader(), PeekHeader() and RemoveHeader(). - Added additional Send() method to Channel, JChannel and EnsChannel - Changed addresses: now we have an Address interface and the old JGroups.JavaStack.Address is now IpAddress (bela Feb 9 2001) - Added Trace module in ./Common (jmenard Feb 9 2001) - Added unit testing using JInit (./Tests) (bela Feb 8 2001) - Added FD_SHUN protocol. Failure detection with shunning of members that are pinging but not in group (bela Feb 8 2001) - Fixed bug which prevented UNICAST to work over TCP (bela Feb 7 2001) - Added merging capability to GMS (not yet to pbcast.GMS) - Added ViewID control to FD - Correct a bug on STABLE - GMS now reverts to Client on leave - Removed interface JGroups.Comparable, substituted with java.lang.Comparable (i-scream Feb 6 2001) Version 0.9.9.7 --------------- - TOTAL protocol added (TOTAL_JOHN) (John Georgiadis Feb 7 2001) - Changes for partition-aware stack. Added simple merging protocol (Gianluca Collot Feb 7 2001) - Suspected members now leave a group voluntarily. Re-join may be automated by setting option Channel.AUTO_RECONNECT and possibly AUTO_GETSTATE. - Removed Header, new headers are just serializable objects that will be attached to a Message. This does not require code changes, but existing code using Header must be rewritten (tiny change). If a header implements interface Sizeable, FRAG will be able to figure out exactly how many bytes a message is (needed to decide whether to fragment or not). If Sizeable is not supported, a (preconfigured) estimated size will be taken. Version 0.9.9.6 --------------- - Modified UDP and TCP to give performance numbers for PERF (Bela Feb 5 2001) - Added PERF protocol (including PerfHeader): measure the performance of messages separately fro each protocol layer. Just add PERF on top of an existing stack (topmost protocol) and set trace=true. - Fixed bugs in NakReceiverWindow (caused PERF to lose some of the performance numbers) - Added LOOPBACK protocol. Can be used as bottom protocol. Simply swaps sender and receiver address and sends message back up the stack. - Implemented exit and reconnect mechanism (pbcast/GMS, FD and JChannel). Modified FD to shun members from whom we receive a ping, but which are not members of the group anymore. This allows e.g. to CTRL-Z a member and later, after member was excluded, do a 'fg'. The member will reconnect to the group (bela Jan 31 2001) - Added Open() to Channel: allows to reuse the same channel (re-connection) (bela Jan 30 2000) - Renamed ConnectionPool to ConnectionTable - TCP: don't send messages with dst == local_addr, but redirect to local queue - NotificationBus: accept local messages Version 0.9.9.5 --------------- - Added ./Demos/PartitionerTest, ./Demos/PartitionerTestFrame - Added PARTITIONER protocol (Gianluca Collot Dec 12 2000) Version 0.9.9.4 --------------- - Repackaged all demos: they are now in the JavaGroups.Demos package - Removed deadlock detection in pbcast.GMS (not needed, as there are no receursive synchronous calls) - Fixed bug in ReusableThread: now threads are released correctly - Converted Thread.stop() in all files - Fixed bug in Scheduler.java: added interrupt() to Stop(). We cannot just use queue.Close(), because the sched_thread might be in a different portion of the code (namely current_task.thread.wait()). Therefore we have to interrupt it as well to make sure it actually goes on to check its state (and whether it should terminate) (bela Dec 10 2000) Version 0.9.9.3 --------------- - Added pbcast.GMS: GMS protocol without FLUSH - Added pbcast.PBCAST protocol, implements Bimodal Multicast (Probabilistic Broadcast) - Added STATE_TRANSFER protocol for PBCAST (bela Nov 29 2000) - FD.java: removed FdHeaders from regular messages Version 0.9.9.2 --------------- - Removed iBus (bela Nov 27 2000) Version 0.9.9.1 --------------- - Added DistributedTree building block (bela Sept 12 2000) - Added DistributedTreeDemo (bela Sept 12 2000) Version 0.9.9 ------------- - Added LogicalLink, Link and WANPIPE classes. Theses can be used as bottom layer to have a TCP-based interconnect between 2 machines. LogicalLink provides a logical WAN pipe, bundling multiple physical links into one. As long as 1 physical link is up, the logical link is up as well. (bela June 29 2000) - Fixed bug in TCP/ConnectionTable: connections to failed members were still in the connection table, causing further requests to new incarnations on the same address to fail. Fix is two-fold: first 'double-writes' are used to detect when peers have closed their side of the connection and second, before we write to a peer, we check whether it is in the group. If not, we close the socket to it and re-open it. This is double-dutch, but I want to be on the safe side. (bela July 3 2000) - Link/LogicalLink work now. (bela July 7 2000) - Added Debugger (./Debug). Provided hooks in Protocol.java to interact with Debugger. (bela July 22 2000) - Modified Link: added interface for missing heartbeats and re-detecting them. Parameterized timeout and timeout_interval for each Link: AddLink() of LogicalLink. Also made initial socket connection *timed*: if an interface (local or remote machine) is down, we won't hang, but just continue, mark the interface as down and periodically try to create a connection to it. (bela July 25 2000) Version 0.9.8 ------------- - Fixed memory leak in ConnectionPool. Replaced readObject()/writeObject() on socket by write(byte)/read(byte[]). Version 0.9.7 ------------- - Added support for TCP as protocol (files TCP.java and TCPPING.java in .JavaStack/Protocols directory). (bela June 16 2000) - Added class ConnectionPool (./JavaStack directory). Takes care of establishing and tearing down TCP connections on demand. (bela June 16 2000) Version 0.9.6 ------------- - Modified some file to compile under JDK 1.2.2 (name collision with List) (bba Feb 9 2000) Version 0.9.5 ------------- - Modified tunneling (files TUNNEL, PING, Router, RouterStub): GossipServer and JRouter are now merged into one: Router. GossipClient was replaced by RouterStub. Both PING and TUNNEL can now access the Router on the same port. Removed files JRouter, GossipClient. (bba Dec 10 1999) - Fixed bug in Router: when a client reconnects under the same addr, but a different socket, we need to use the new address to send out messages (but the client still stays registered under its old address) (bba Dec 12 1999) - Moved UdpHeader class out of UDP.java (will also be used by Router) (bba Dec 16 1999) Version 0.9.4 ------------- - UNICAST: view change cannot remove all non-members: it may be the case that 2 new members P and Q want to join the group, P is admitted and Q has sent its JOIN request (causing a unicast connection to be created in UNICAST). Now the view change for P is sent, this removes the connections for those processes that are not members in the view (Q isn't yet official member). Therefore connections must be removed upon reception of SUSPECT rather than VIEW_CHANGE. (bba Dec 8 1999) - Modified UNICAST: retransmit thread (AckSender window) has to be killed when STOP event is received. (bba Dec 8 1999) Version 0.9.3 ------------- - New version of STABLE, based on GRPCs. Makes use of existing seqno information in NAKACK (using event GET_MSGS_RECEIVED) (bba Nov 19 1999) - Renamed RpcGMS -> GMS - Added SortedList (bba Nov 23 1999) - Fixed bug in AckSenderWindow(): addition of new message to empty window did not check seqno, nor wake up retransmission thread (bba Nov 29 1999) - Replaced SortedList.AddAscending(): more efficient version (bba Nov 29 1999) - Modified AckSenderWindow (uses SortedList now) (bba Nov 29 1999) - Modified demo apps: UNICAST layer has to be *above* NAKACK. Otherwise, NAKs and ACKs of the NAKACK layer are reliable too (causing way to many msgs, e.g. acking of ACKs themselves) ! (bba Nov 30 1999) - Removed PT2PT protocol layer, Pt2ptFsm, Pt2ptHeader: replaced by UNICAST layer (bba Nov 30 1999) - Set deadlock detection default to true (in RequestCorrelator) (bba Dec 1 1999) - Resending of unstable messages in NAKACK (REBROADCAST_MSGS): now an additional header (WRAPPED_MSG) is added which contains the same seqno as the original message, but also a field 'sender' to which the ACK has to be sent. This is so because the original sender of the message may be different from the coordinator who re-broadcasts the message. If ACKs were not sent to the re-broadcaster but instead to the original sender (who may have crashed), the coordinator would wait indefinitely for all ACKs. (bba Dec 2 1999) - FLUSH protocol: when soliciting unstable message, so far only the highest seqnos for received message were returned. Now, each sender returns the highest seqno it sent, which may be higher than received. This ensures that no messages are missing when re-broadcasting the message digest as part of the FLUSH. (bba Dec 3 1999) Version 0.9.2 ------------- - Modified STABLE's defaults: gossip has to be sent much less often; now gossip is sent every 100 msgs or 10 secs (which ever comes first). - Modifications in FLUSH/NAKACK/RpcGMS: the flush protocol now does not return all unstable messages (as determined by the STABLE protocol), but just those that are determined by consensus (fewer messages have to be re-broadcast). The advantage is that STABLE does not need to emit STABLE events frequently. See file FLUSH.java for details. (bba Nov 17 1999) Version 0.9.1 ------------- (bba Nov 13 1999) - Removed several superfluous fields from Message (e.g. id, rsp_id, oneway) - Modified FRAG.java (depended on Message.GetId()) - Modified FragmentBuffer/DefragmentBuffer in Util.java Version 0.9 ----------- - Adapted demos (bba Nov 12 1999) - Removed the following classes: - Dispatcher - ChannelEntry - MethodInvoker - RemoteMethodCall - LazyEvaluator - RepeatedUnicast - SyncCall - MessageCorrelator - Command, AndCommand, OrCommand - Conf - SlidingWindow - Subject - Observer - Timer - MNAK, GMS, GmsImpl Therefore this version will break existing code that is based on these classes ! Either use a previous version, or rewrite the code using the new classes (e.g. RpcDispatcher for Dispatcher) - Added ChannelListener (bba Nov 5 1999) Version 0.8.6 ------------- - Modified LEAVE protocol: leaving member has to wait until it has received all messages of the current view before it may leave. Otherwise it may receive fewer messages in its last view than the other members, which would violate virtual synchrony. Version 0.8.5 ------------- - Increased priority of drawing (receiver) thread in demo programs ./Demo/Draw.java, ./Demo/DrawIpMcast.java and ./Demo/DrawIpMcastX.java. The drawing thread seemed to starve on heavy drawing. - Replaced implementation of Queue: the old version was based on Vector, which scale badly when used as FIFO queues; removal/addition of element causes all remaining elements to shift. New version uses linked list. - Implemented interface Externalizable in Message.java, Header.java: huge performance improvement ! Header: in addition to the byte rep, we also store object directly (caching). Thus, Message.PeekHeader() does not always have to reconstruct an object from the byte rep ! (bba Sep 3 1999) - Implemented List and Stack: faster versions of their respective java.util.* companions (not based on Vector). - Address.Compare(): now uses hashCode() between InetAddresses instead of string form -> performance improvement - Modified NakAckWindow: replaced Vectors with Lists Version 0.8.4 ------------- - Modified NAKACK layer: GET_STABLE_MSGS not needed, STABLE layer sends stability msgs periodically anyway (bba Aug 13 1999) Version 0.8.3 Aug 13 1999 (bba) ------------------------------- - Added MessageDispatcher: equivalent of MessageProtocol for applications. This required changes to Channel/JChannel (UpHandler) - Added RpcDispatcher: equivalent of RpcProtocol for applications. Version 0.8.2 Aug 10 1999 (bba) ------------------------------- - Modified GroupRequest: not only suspect messages can cause an RMC to terminate, but also view changes (e.g. when an RMC is initiated after a suspect event has been received, but before the view change has been seen. Otherwise this would block forever). Version 0.8.1 July 05 1999 (bba) -------------------------------- - Added VIEW_ENFORCER: drops all messages until the new member is joined. Then acts as pass-through. Version 0.8.0 July 02 1999 (bba) -------------------------------- - Modified View/ViewId: View now contains ViewdId, moved ViewId to main directory Version 0.7.6 June 25 1999 (bba) -------------------------------- - Added PIGGYBACK layer: combines multiple messages into a single larger one - Modified STATE_TRANSFER layer: now target(s) of ST can be given (modifications also in Channel, JChannel). Version 0.7.5 June 18 1999 (bba) -------------------------------- - Work on RpcGMS and FLUSH layers - Added BLOCK processing to FLUSH Version 0.7.4 June 17 1999 (bba) -------------------------------- - Modified Channel/JChannel to be able to receive Events (applications may already send Events): this enables building blocks to communicate with protocol layers via Events. - Modified NAKACK: ACK scheme independent from NAK, e.g. numbering of messages Version 0.7.3 June 14 1999 (bba) ------------------------------- - First draft of FLUSH protocol - Added FD_RAND protocol layer: based on polling of randomly selected members Version 0.7.2 June 9 1999 (bba) ------------------------------- - Modified NakReceiverWindow: now ranges (e.g. [4-8]) in retransmissions are detected, this avoids having to send several retr msgs. Instead just 1 msg is sent for each range. - Added AckMcastSenderWindow: used for lossless delivery to group members in NAKACK (ACKer) Version 0.7.1 June 8 1999 (bba) ------------------------------- - Modified several files to remove warnings from Jikes compiler (pedantic flag activated) Version 0.7.0 May 27 1999 (bba) ------------------------------- - Added total order protocol (TOTAL.java). Written by Manish Sambhu (mms21@cornell.edu) Version 0.6.9 May 27 1999 (bba) ------------------------------- - Created NAKACK protocol. NAK-based scheme that can be switched to using ACKs on the fly (and back again). Uses 2 classes to do so. - Created NakReceiverWindow out of SlidingWindow (will be removed soon), thread is now created only when needed (ther might be many NakReceiverWindow instances !). Also, retransmitter thread does not check age of message any more, just presence or absence. Version 0.6.8 May 20 1999 (bba) ------------------------------ - Modifications in RpcGMS: simpler join algorithm (client side / coordinator side) Version 0.6.7 May 18 1999 (bba) ------------------------------ - Modified MethodLookupClos: better handling of primitive types - Renamed subdirectory 'Tests' -> 'Demos' and 'tests' -> 'Tests' Version 0.6.6 Apr 15 1999 (bba) ------------------------------ - Added classes Scheduler, ReusableThread, ThreadPool - Modified RequestCorrelator to use Scheduler. Now, RequestCorrelator can be set to prioritize requests that would cause a deadlock. - Added DEADLOCK layer. Uses RequestCorrelator with deadlock detection to enable 'recursive synchronous group RPCs'. Version 0.6.5 Apr 13 1999 (bba) ------------------------------ - Added requirements checking. Added 4 methods RequiredUpServices(), RequiredDownServices(), ProvidedUpServices() and ProvidedDownServices() to Protocol.java. Configurator now checks for each protocol layer whether all its requirements on other layers are met. Version 0.6.4 Apr 12 1999 (bba) ------------------------------ - Modified MembershipListener: added methods for getting/setting state - Modified PullPushAdapter: dito - Added JGroups/Tests/TotalOrderPull.java demoing PullPushAdapter-based state transfer Version 0.6.3 Apr 9 1999 (bba) ------------------------------ - Added DELAY protocol. Will delay all outgoing or incoming messages by n milliseconds, where n can be determined by the user Version 0.6.2 Apr 9 1999 (bba) ------------------------------ - Bug fix in GossipServer - Changes to GossipClient: will only register when both local address and group address are known Version 0.6.1 Apr 1 1999 (bba) ------------------------------ - Added state transfer interface to Channel; Channels stay backwards-compatible to existing applications - Added protocol STATE_TRANSFER - Updated documentation: STATE_TRANSFER, Channel interface Version 0.6.0 Mar 12 1999 (bba) ------------------------------- - Added MessageProtocol and RpcProtocol (subclasses of Protocol). These can be used when peer communication between the same layers in different stacks is required, e.g. for GMS. Both subclasses implement synchronous group messages/method calls. - Created new GMS layer: RpcGMS, RpcCoordGmsImpl, RpcParticipantGmsImpl and RpcClientGmsImpl. Uses state pattern (client, coord, participant). Also makes use of RpcProtocol. - Modified PING: removed merge functionality. Added merge functionality to separate layer (MERGE). - Created initial (simplistic) version of MERGE protocol. Only discovers out-of-group members, and sends up MERGE event. MERGE is not yet handled by RpcGMS. Version 0.5.9 Feb 26 1999 (bba) ------------------------------- - Modified SUSPECT handling in GMS: when FD suspects a member, it mcasts a SUSPECT message to all members. At each member, this will generate a SUSPECT message, which is sent up the stack, starting from the FD layer. The GMS layer then checks whether the coordinator itself is suspected. If this is the case, the members are sorted and a new coordinator is chosen deterministically by each member. The new coordinator then mcasts a new view. If the coordinator itself is not suspected, it removes the faulty member from its local view and installs a new view. The modified algorithms removes 1 round of multicasting plus a redundant check on who the coordinator is. Version 0.5.8 Feb 5 1999 (bba) ------------------------------ - Changed Ctor, Close, Connect and Disconnect in JChannel (only implementation): when disconnecting, a channel is not supposed to receive any more messages (this was the case). Also, a process has to acquire a new local address when re-joining (this was not the case). Version 0.5.7 Feb 1 1999 (bba) ------------------------------ - Added protocol UNI: uniform failure-atomic group mcast - Added protocol PING: get initial members and periodical sending of i-am-alive message (used to overcome partitions) - Changed GMS/GmsImpl to make use of PING Version 0.5.6 Jan 19 1999 (bba) ------------------------------- - Added ./Algorithms/GroupRequest, ./Algorithms/AndCommand - Added RequestCorrelator - Moved JavaStack/Event.java to ./Event.java. Internal channel implementations use now Event instead of QueueItem (code reduction) Version 0.5.5 Jan 14 1999 (bba) ------------------------------- - Renamed ./algorithms to ./Algorithms Version 0.5.4 Jan 13 1999 (bba) ------------------------------- - Back to square 1: dropped unicast mode altogether, essentially back to version 0.5.2 ! Version 0.5.3 Jan 4 1999 (bba) ------------------------------- - Changed unicast interface. Renamed Connect() to Join() and Disconnect() to Leave(). - A channel is now operational for unicast messages after the Channel() constructor has been called. Only when Join() is called will it be able to send/receive group messages. - The changes to the interfaces/classes require application programs usingt version 0.5.2 to be modified and recompiled. Version 0.5.2 Dec 29 1998 (bba) ------------------------------- - Modifications in the event sequence when creating a channel and when connecting to it (START, STARYT_OK, CONNECT, CONNECT_OK, DISCONNECT, DISCONNECT_OK, STOP, STOP_OK) - Got rid of LowLevelCommunication interface - Local address is set by SET_LOCAL_ADDRESS (sent up by lowest layer) - Peer address is set by CONNECT(peer_addr) Version 0.5.1 Dec 22 1998 (bba) -------------------------------- - Fairly extensive changes to the Channel interface: changed ChannelFactory, Connect() and Receive(). Receive() now returns Objects rather than Message. These might be Messages, Views or Blocks. Channel is now pure pull-oriented. Removed Cast() and several Send() methods. Version 0.5 Dec 21 1998 (bba) ----------------------------- - Added new protocol PT2PT: TCP-like reliable point to point connection - Changed Channel interface: separation of channel creation, Connect(), removal of Destroy() -> existing programs will need to be modified to use the new classes - The changes to the interfaces/classes require application programs written for version 0.4 to be modified and recompiled. Version 0.4 Dec 10 1998 (bba) ----------------------------- - Added several new protocols (MACK, MNAK) - Fixed IP MCAST bug (UDP.java) - Fixed gossip bug (UDP.java) Version 0.3 Oct 1998 -------------------- - First version (0.3) released (bba) libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.8.txt0000644000175000017500000000606711366547366022717 0ustar twernertwerner Release Notes JGroups 2.6.8 =========================== Version: $Id: ReleaseNotes-2.6.8.txt,v 1.1.2.1 2009/02/09 12:38:18 vlada Exp $ Author: Bela Ban JGroups 2.6.8 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6 and 2.6.7. Below is a summary (with links to the detailed description) of the major new features between 2.6.7 and 2.6.8. Bugs * [https://jira.jboss.org/jira/browse/JGRP-863] - GroupRequest: only remove don't add members on view changes * [https://jira.jboss.org/jira/browse/JGRP-865] - TCP: bind_port is ignored * [https://jira.jboss.org/jira/browse/JGRP-867] - COMPRESS: cannot replace message buffer in-place * [https://jira.jboss.org/jira/browse/JGRP-876] - ConcurrentModificationException during startup of JBoss AS 5.0.0.GA * [https://jira.jboss.org/jira/browse/JGRP-878] - FD_SOCK: client connection handlers are not removed from list * [https://jira.jboss.org/jira/browse/JGRP-881] - Persistent ports: update port on graceful leave * [https://jira.jboss.org/jira/browse/JGRP-883] - Use of Math.min In GossipRouter's Socket Linger Code Causes Unexpected Results * [https://jira.jboss.org/jira/browse/JGRP-888] - concurrent modification exception in FD_SIMPLE * [https://jira.jboss.org/jira/browse/JGRP-891] - ConcurrentModificationException during JBoss AS 5.0.0.GA startup * [https://jira.jboss.org/jira/browse/JGRP-896] - GMS: ConcurrentModificationException in AckCollector.handleView() Feature Requests * [https://jira.jboss.org/jira/browse/JGRP-868] - GossipRouter: use a thread pool to handle requests * [https://jira.jboss.org/jira/browse/JGRP-869] - Include the cluster name in the GMS print_local_addr logging * [https://jira.jboss.org/jira/browse/JGRP-884] - Provide Gossip Router Command Line Options For Backlog, Socket Read Timeout, and Socket Linger Timeout * [https://jira.jboss.org/jira/browse/JGRP-890] - Util.getMBeanServer should return the platform MBean server is no other is found Tasks * [https://jira.jboss.org/jira/browse/JGRP-874] - PING/TCPGOSSIP: unregister member on leaving the cluster * [https://jira.jboss.org/jira/browse/JGRP-879] - UDP: see whether new MulticastSocket(mcast_addr, port) works on Solaris * [https://jira.jboss.org/jira/browse/JGRP-885] - Remove Obselete GossipRouter Command Line Arguments * [https://jira.jboss.org/jira/browse/JGRP-892] - ConnectionMap.createServerSocket(): throw an exception if socket cannot be created * [https://jira.jboss.org/jira/browse/JGRP-894] - Add task to put distro in Maven repo when releasing * [https://jira.jboss.org/jira/browse/JGRP-898] - Make the RPC response OOB by default (back port from 2.7/2.8) Manual ------ The manual is online at http://www.jgroups.org/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Feb 2009 libjgroups2.6-java-2.6.15.GA.orig/doc/ENCRYPT.html0000644000175000017500000002357611366547366021132 0ustar twernertwerner

Encryption Protocol Document

(Author : Steve Woodcock)

Introduction

The encryption protocol provides encryption of messages in JGroups that are generated by applications. There are a few requirements in order to make use of the Encryption layer.
  • A suitable Security provider is required. (see security provider instructions later)
  • The java.security properties need to be configured (see installation guide).

  The Encrypt layer can be used in one of two ways (the options are mutually exclusive so all participants in a JGroup must have the same settings).
  • Option 1. Configured with a secretKey in a keystore so it can be used at any layer in JGroups without the need for a coordinator, or if you want protection against passive monitoring but do not want the key exchange overhead and complexity. In this mode all nodes must be distributed with the same keystore file. Due to issues with Sun's JCE you cannot use this option with JDK1.3

  • Option 2. Configured with only algorithms and key sizes. The Encrypt Layer in this mode should be used between the FRAG and PBCast layers in the stack. The coordinator chooses the secretkey which it distributes amongst all the peers. In this form no keystore exists as the keys are the key is generated and distributed by the controller using a public/private key exchange. View changes that identify a new controller will result in a new session key being generated and then distributed to all peers. This overhead can be substantial in a an application with a reasonable peer churn.

Installation

The following steps need to be followed to make sure that ENCRYPT protocol can be used
 

Installing a Security Provider

First you need to download an appropriate security provider. A good option is an organisation called bouncycastle, who provide an open-source clean room implementation of the JCE. You can find them at www.bouncycastle.org.
You must download the correct jar file for the JDK you are using - at the time of writing the 1.3 version is: jce-jdk13-124.jar and the 1.4 version is: bcprov-jdk14-124.jar.
You do not have to use the bouncycastle provider - others available include those from Phaos Technology, RSA and Wedgetail Communications. See the appropriate documentation for each provider.

If you are using 1.3 you must also install the SUN JCE provider (current version 1.2.2) to give access to the JCEKS keystore formats. This can be downloaded from the www.javasoft.com site. The following instructions are for JDK1.3 and 1.4.

Assuming $JAVA_HOME to be the JDK the installation root.

JDK 1.3

Download the SunJCE file.
Unzip the file and place the jar files contained in the jce/lib folder in the $JAVA_HOME/jre/lib/ext directory
Place the bouncycastle provider in the $JAVA_HOME/jre/lib/ext directory.

Next configure the security file to add the new providers.

  • Open the java.security file in $JAVA_HOME/jre/lib/security.
  • Search for security.provider.{list} in the opened file.
  • Add the SunJCE provider as provider 2, and add the bouncycastle provider as the last.
  • You should end up with the entries similar to that shown below.
    security.provider.1=sun.security.provider.Sun
    security.provider.2=com.sun.crypto.provider.SunJCE
    security.provider.3=com.sun.rsajca.Provider
    security.provider.4=org.bouncycastle.jce.provider.BouncyCastleProvider

JDK 1.4

The JDK1.4 already includes the SunJCE files, so you only need to configure the bouncycastle provider.
Place the bouncycastle provider in the $JAVA_HOME/jre/lib/ext directory.

Next configure the security file to add the new providers.

  • Open the java.security file in $JAVA_HOME/jre/lib/security.
  • Search for security.provider.{list} in the opened file.
  • Add the bouncycastle provider as the last.
  • You should end up with similar entries as shown below.
    security.provider.1=sun.security.provider.Sun
    security.provider.2=com.sun.net.ssl.internal.ssl.Provider
    security.provider.3=com.sun.rsajca.Provider
    security.provider.4=com.sun.crypto.provider.SunJCE
    security.provider.5=sun.security.jgss.SunProvider
    security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider

Configure the Encrypt Protocol

Option 1 - Using a shared keystore (JDK1.4 Only)


This is the simplest option and can be used by simply inserting the Encryption layer at any point in the JGroup stack - it will encrypt all Events of a type MSG that have a non-null message buffer. The format of the JGroup XML entry in this form is:

<ENCRYPT key_store_name="defaultStore.keystore" store_password="changeit" alias="myKey"/>

An example bare-bones.xml file showing the keystore version can be found in the conf directory in a file called EncryptKeyStore.xml - you will need to generate a keystore - see the KeyStoreGenerator file in the demo directory or use the makeKeystore target in the build file.
In order to use the Encrypt layer in this manner it is necessary to have the secretKey already generated in a keystore file. The directory containing the keystore file must be on the application's classpath. You cannot create a SecretKey keystore file using the keytool application shipped with the JDK. A java file called KeyStoreGenerator is included in the demo package that can be used from the command line (or IDE) to generate a suitable keystore.

IMPORTANT- A keystore generated under each version of the 1.4 JDK can be incompatible with other JDK versions. Make sure you generate the keystore with the same JDK version as you are going to use at runtime. This is important for 1.4.2_04 and 1.4.2_05.

Option 2 - Dynamic Key Generation (JDK1.3/1.4)


This option is suited to an application that does not ship with a shared key but instead it is generated and distributed by the controller. The secret key is first generated by the Controller (in JGroup terms). When a view change occurs a peer will request the secret key by sending a key request with its own public key. The controller encrypts the secret key with this key and sends it back to the peer who then decrypts it and installs the key as its own secret key.
All encryption and decryption of Messages is done using this key. When a peer receives a view change that shows a different keyserver it will repeat this process - the view change event also trigger the encrypt layer to queue up and down messages until the new key is installed. The previous keys are retained so that messages sent before the view change that are queued can be decrypted if the key is different.
An example EncryptNoKeyStore.xml is included in the conf directory as a guide.


Note: the current version does not support the concept of perfect forward encryption (PFE) which means that if a peer leaves the group, the keys are re-generated preventing the departed peer from decrypting future messages if it chooses to listen in on the group. This is not included as it really requires a suitable authentication scheme as well to make this feature useful as there is nothing to stop the peer rejoining and receiving the new key. A future release will address this issue.

Demo

To test the usage of the protocol use any demo and change the xml config file to include the encrypt protocol. Starting up an application using the keystore option will result in an output similar to:

04-Aug-2004 21:11:38 org.jgroups.protocols.ENCRYPT setProperties
INFO: key_store_name used is defaultStore1_4.keystore
04-Aug-2004 21:11:38 org.jgroups.protocols.ENCRYPT setProperties
INFO: store_password used is not null
04-Aug-2004 21:11:39 org.jgroups.protocols.ENCRYPT setProperties
INFO: key_password used is same as store_password
04-Aug-2004 21:11:39 org.jgroups.protocols.ENCRYPT setProperties
INFO: alias used is myKey
04-Aug-2004 21:11:39 org.jgroups.protocols.ENCRYPT initSymCiphers
INFO: Initializing symmetric ciphers
04-Aug-2004 21:11:39 org.jgroups.protocols.ENCRYPT initSymCiphers
INFO: Initialized symmetric ciphers with secret key version �;ٺ�8=fԱ;qe2�
If you want to confirm the encryption/decryption messages then set the logging level to debug for the protocol.

If you are using the encryption without a keystore you should see output similar to:

05-Aug-2004 13:43:52 org.jgroups.protocols.ENCRYPT initSymKey
INFO: Symmetric key generated
05-Aug-2004 13:43:53 org.jgroups.protocols.ENCRYPT initKeyPair
INFO: asym algo initialized
05-Aug-2004 13:43:53 org.jgroups.protocols.ENCRYPT initSymCiphers
INFO: Initializing symmetric ciphers
05-Aug-2004 13:43:53 org.jgroups.protocols.ENCRYPT initSymCiphers
INFO: Initialized symmetric ciphers with secret key version �~6��Ɨ/h%ڤĹ
................
Later in the output you should see logging identifying that the JGroups peer has become the keyServer - this will be the same peer that is the group coordinator.

05-Aug-2004 13:43:55 org.jgroups.protocols.ENCRYPT up
INFO: handling view change event
05-Aug-2004 13:43:55 org.jgroups.protocols.ENCRYPT becomeKeyServer
INFO: I have become key server

Note: the key version is an MD5 hash of the key in an encoded form from which you cannot get the original key data. libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.3.txt0000644000175000017500000000536411366547366022711 0ustar twernertwerner Release Notes JGroups 2.6.3 =========================== Version: $Id: ReleaseNotes-2.6.3.txt,v 1.1.2.6 2008/06/30 06:00:37 belaban Exp $ Author: Bela Ban JGroups 2.6.3 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1 and 2.6.2. Below is a summary (with links to the detailed description) of the major new features between 2.6.2 and 2.6.3. Shared transport fixes ---------------------- [http://jira.jboss.com/jira/browse/JGRP-737] Critical bug in the shared transport which caused channels sharing a transport to stop working as soon as one shared channel was closed. Fixed by introducing reference counting. Better thread naming for threads in a shared transport ------------------------------------------------------ [http://jira.jboss.com/jira/browse/JGRP-728] Timer thread pool moved to transport protocol --------------------------------------------- Related to above. The timer thread pool was moved from the ProtocolStack to the transport protocol. Now *all* thread pools are located in the transport. Thread factories, which control the creation of all threads in the JGroups system, are now also located in the transport. They can be replaced entirely, or - as an alternative - a listener can be registered to be called whenever a thread is created or released back into a thread pool. Section 5.5.5 (http://www.jgroups.org/javagroupsnew/docs/manual/html/user-advanced.html#d0e2809) in the manual discusses the steps to be taken to replace thread pools and / or thread factories. Eager ACKs for UNICAST ---------------------- [http://jira.jboss.com/jira/browse/JGRP-713] Optimization for heavy unicast traffic; reduces retransmissions Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Bug fixes --------- FD: messages other than from the pinged neighbor cause the missing heartbeat count to get reset ----------------------------------------------------------------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-746] This is a regression in 2.6. FLUSH: flush spans merges [http://jira.jboss.com/jira/browse/JGRP-700] Merging of digests incorrect ---------------------------- [http://jira.jboss.com/jira/browse/JGRP-699] Blockings with large state transfers ------------------------------------ [http://jira.jboss.com/jira/browse/JGRP-774] Concurrent startups of many channels with FLUSH ----------------------------------------------- [http://jira.jboss.com/jira/browse/JGRP-770] The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada July 2008 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.11.txt0000644000175000017500000000311011366547366022753 0ustar twernertwernerRelease Notes JGroups 2.6.11 ============================ Version: $Id: ReleaseNotes-2.6.11.txt,v 1.1.2.1 2009/07/17 13:58:42 rachmatowicz Exp $ Author: Bela Ban JGroups 2.6.11 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6, 2.6.7, 2.6.8, 2.6.9 and 2.6.10. Below is a summary (with links to the detailed description) of the major new features between 2.6.10 and 2.6.11. Release Notes - JGroups - Version 2.6.11 ** Bug * [JGRP-912] - Failed to parse the jgroups.xml file * [JGRP-956] - Binding to mcast address in UDP.java is not working on Linux with IPv6 * [JGRP-984] - JGroups can not parse IPV6 literals in TCPPING protocol configuration * [JGRP-985] - Flush and join problems during constant node failures and constant joins * [JGRP-986] - FLUSH: relax event sequencing for a joining member * [JGRP-997] - AUTH: incorrect matching of src against memberList * [JGRP-1000] - COMPRESS corrupts some messages * [JGRP-1006] - Shared transport: shunned multiple channels can not reconnect ** Task * [JGRP-982] - Extend linux method of creating multicast socket to HP-UX * [JGRP-1004] - GroupRequest: don't ignore InterruptedException Manual ------ The manual is online at http://www.jgroups.org/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada July 2009 libjgroups2.6-java-2.6.15.GA.orig/doc/CONFIGURATIONS0000644000175000017500000001020111366547366021152 0ustar twernertwerner## $Id: CONFIGURATIONS,v 1.1.1.1.26.1 2007/11/20 08:53:44 belaban Exp $ Frequently used protocol stack specifications ============================================= Virtual Synchrony & State Transfer Protocol Stack ------------------------------------------------- UDP: PING(num_initial_members=2;timeout=3000): FD: STABLE: NAKACK: UNICAST: FRAG: FLUSH: GMS: VIEW_ENFORCER: STATE_TRANSFER: QUEUE Properties: Uses Virtual Synchrony. All messages sent in a view V1 are delivered in that view. The FLUSH protocol makes sure that, when a new view V2 is to be installed, that all messages sent in V1 will be seen by all members of V1 before V2 is installed. The FLUSH protocol stops all sending until V2 has been installed successfully at all members. Messages sent after the block has been received and before V2 is installed will be sent in V2. Protocols: UDP: uses UDP/IP multicast as transport PING: discovers initial set of members, determines coordinator to which the join-request will be sent FD: failure detection based on periodic pinging of member 'to the right' in the membership ring STABLE: garbage collection of messages seen by all members NAKACK: guarantees lossless 1-m message delivery, uses negative acks to retransmit lost messages UNICAST: guarantees lossless 1-1 message delivery, uses positive acks to retransmit lost messages FRAG: fragments large messages into smaller ones and reassembles them at the receiving side FLUSH: flushes all pending multicasts out of the system before transitioning to a new view. Ensures that all members of view V1 agree on the set of messages they delivered in V1. GMS: group membership service. Takes care of joining/leaving members VIEW_ENFORCER: only accepts messages from senders in the same view. Stores messages for future view, discards messages sent in previous view. STATE_TRANSFER: allows any member to fetch the state from any other member (usually done immediately after joining) QUEUE: queues messages sent during a view transition. When the new view is installed, the stored messages will be sent. Pbcast-based Protocol Stack --------------------------- UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=0): PING(timeout=5000;num_initial_members=6): FD_SOCK: VERIFY_SUSPECT(timeout=1500): pbcast.STABLE(desired_avg_gossip=10000): pbcast.NAKACK(gc_lag=5;retransmit_timeout=3000;trace=true): UNICAST(timeout=5000;min_wait_time=2000): FRAG(down_thread=false;up_thread=false): pbcast.GMS(join_timeout=5000;shun=false;print_local_addr=true) Properties: Defines a stack with weaker reliability semantics than the Virtual Synchrony protocol stack. The biggest difference is that there is no guarantee that the set of messages sent between views is the same (no FLUSH protocol). Essentially defines a reliable 1-m protocol, where views are just regular messages and have no special semantics. Protocols: UDP: uses UDP/IP multicast as transport. Uses a time-to-live of 0 PING: discovers initial set of members, determines coordinator to which the join-request will be sent. Will wait for 5 seconds or 6 members to respond (whichever is first) FD_SOCK: failure detection based on TCP socket connection from each member to the member 'to the right' in the membership ring. When connection breaks, member is suspected. Compared to FD, there are no periodic ping messages sent VERIFY_SUSPECT: reduces false suspicions. Verifies that a member P that is suspected is really dead by sending a ping message to P. pbcast.STABLE: garbage collection of messages seen by all members using gossips. A gossip is multicast every 10 seconds on average by each member. pbcast.NAKACK: guarantees lossless 1-m message delivery, uses negative acks to retransmit lost messages UNICAST: guarantees lossless 1-1 message delivery, uses positive acks to retransmit lost messages FRAG: fragments large messages into smaller ones and reassembles them at the receiving side pbcast.GMS: group membership service. Takes care of joining/leaving members libjgroups2.6-java-2.6.15.GA.orig/doc/JmxSupport.txt0000644000175000017500000000645011366547366022004 0ustar twernertwerner // Author: Bela Ban // $Id: JmxSupport.txt,v 1.3 2006/10/09 13:36:18 belaban Exp $ JMX support for JGroups ======================= In 2.2.9, JMX support was added to JGroups. There were several design goals: - No dependencies on JMX if JMX support is not required. It should be possible to ship a JGroups version without any JMX support, and without jmxri.jar (for JDK 1.4.x). Therefore channels and protocols are not turned into MBeans, e.g. FD doesn't implement FDMBean. Instead, a parallel MBean and MBeanImpl hierarchy was created, where the MBeanImpl delegates to the real protocol. This hierarchy is located in package org.jgroups.jmx. For a protocol org.jgroups.protocols.P, there is a corresponding org.jgroups.jmx.protocols.PMBean MBean and an implementation org.jgroups.jmx.protocols.P - No negative effect on performance, minimal effect on memory. JMX support can be disabled in the channel, and all protocols (stats="false") - Support for running in a pre-JMX JDK (1.4, for example), e.g. under JBoss. There is a jgroups-service target in the ANT build which generates a jgroups-service.sar that can be deployed into JBoss. The channel defines in the META-INF/jboss-service.xml file of the SAR defines a JGroups channel MBean, which will automatically create MBeans for all protocols and register them with the MBeanServer - Support for running in JDK 5 standalone. JDK 5 has JMX support built in. The jconsole tool can be used to inspect the MBean hierarchy. To programmatically create MBean support for JDK 5, the following code can be used: JChannel channel; channel.connect(groupname); ArrayList servers=MBeanServerFactory.findMBeanServer(null); if(servers == null || servers.size() == 0) { throw new Exception("No MBeanServers found;" + "\nJmxTest needs to be run with an MBeanServer present, or inside JDK 5"); } MBeanServer server=(MBeanServer)servers.get(0); JmxConfigurator.registerChannel(channel, server, "JGroups:channel=" + channel.getChannelName() , true); JmxConfigurator has a number of helper methods which register and de-register the channel and/or te protocols attached to a given channel. Provided information -------------------- Some information provided includes (all info can be reset), besides configuration: - UDP: messages sent/received, bytes sent/received (per member) - Discovery: manual triggering of discovery - NAKACK: retransmissions per member, last N retransmitted messages, lowest and highest sequence numbers per member - GMS: membership, history of views - FD/FD_SOCK: last N suspected members - STATE_TRANSFER: number of state requests, total bytes transferred - STABLE: manual triggering of STABLE protocol Example ------- The Draw program was modified to support JMX. The following steps describe how to configure and run a JDK 5 VM and look at the resulting Draw application via jconsole. - Run the Draw program (the JDK needs to be 5 !): java -Dcom.sun.management.jmxremote org.jgroups.demos.Draw -props c:\udp.xml -jmx (the -jmx flag enables registering the Channel and protocols with the MBeanServer) - Start jconsole and connect to the local Draw process - Under 'MBeans', choose the JGroups channel and browse the channel and the protocols - [optional] Add the STATS protocol to the top of the stack; it display information about messages sent and receivedlibjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.3.txt0000644000175000017500000001130211366547366022532 0ustar twernertwerner Release Notes JGroups 2.3 ========================= Version: $Id: ReleaseNotes-2.3.txt,v 1.4 2006/04/26 07:56:01 belaban Exp $ Author: Bela Ban Multiplexer ----------- In JBoss we have multiple JGroups channels, one for each application (e.g. JBossCache, ClusterPartition etc). The goal of the Multiplexer is to combine all stacks with the *same* configuration into one, and have multiple apps on top of that same channel. To do this we have to introduce multiplexing and demultiplexing functionality, ie. each app will have to have a unique application ID (a string), and when sending a message, the message has to be tagged with that ID. When receiving a message, it will be dispatched to the right app based on the ID attached to the message. We require special handling for VIEW and SUSPECT messages: those need to be dispatched to *all* apps. State transfer also needs to be handled specially, here we probably have to use thread locals, or change the API (TBD). When deployed into JBoss, the Multiplexer will be exposed as an MBean, and all apps that depend on it will be deployed with dependency injection on the Multiplexer. Of course, the old configuration will still be supported. The config of the Multiplexer is done via a config file, which lists a number of stacks, each keyed by a name, e.g. "udp", "tcp", "tcp-nio" etc. See ./conf/stacks.xml for an example. An app is configured with the name of a stack, e.g. "udp", and a reference to the Multiplexer MBean. It will get a proxy channel through which all of its communication will take place. The proxy channel (MuxChannel) will mux/demux messages to the real JGroups channel. The advantage of the Multiplexer is that we can reduce N channels into M where M < N. This means fewer threads, therefore fewer context switches, less memory consumption and easier configuration and better support. Partial state transfer ---------------------- The Channel.getState() method can now define the ID of a substate to be fetched. This allows applications to get only a part of its state, not the entire state. See http://www.jgroups.org/javagroupsnew/docs/manual/html/user-channel.html#PartialStateTransfer for details. AUTH ---- AUTH is a protocol directly under GMS, which allows to authenticate members who want to join a group. Based on a pluggable authentication mechanism, new members are either admitted or rejected. In the latter case, Channel.connect() will fail with a security exception. For details see JGroups/doc/design/AUTH.txt. Sequencer based total order protocol ------------------------------------ SEQUENCER is an improved implementation of total order, and faster than TOTAL. When sending a message to the group, the sender sends the message via unicast to the coordinator, who then broadcasts the message (on behalf of the sender) to all members, with a unique sequence number. The coordinator uses FIFO, but since there is only 1 sender, this results in total order. SEQUENCER can be used for example to maintain identical replicas of a (JMS) queue: senders and receivers can send and receive messages to/from any queue replica simultaneously, without affecting the consistency of all replicas across the cluster. A quick 2 node performance test (perf.Test) with 2 million 1K messages showed ca 6500 messages/sec with sequencer.xml, compared to ca. 10500 messages/sec with fc-fast-minimalthreads.xml. For details see JGroups/doc/design/SEQUENCER.txt. ENCRYPT enhancements -------------------- The encrypt_entire_message flag (if set to true) will now encrypt the entire message (including the headers), as opposed to only encrypting the message buffer. Note that this operation requires serialization of the message, so setting this option to true is expensive. Why use it ? When one wants to prevent eavesdroppers from snooping out information located in the (non-encrypted) headers, such as sequence numbers. Incompatibilities to previous version ------------------------------------- Changed method signature of the RpcDispatcher.callRemoteMethod() methods to throw a Throwable. Previously they returned the exception as an object, now the exception will be thrown. Callers of these methods have to change their code, so this is an incompatible change. However, these calls are not used in JBossCache and JBoss Clustering. (http://jira.jboss.com/jira/browse/JGRP-154) Enhancements and bug fixes -------------------------- - FD_SOCK now uses the bind address of the transport unless a bind_addr is specifically specified, or the -Dbind.address system property is used. - FRAG had a bug that corrupted messages when messages were sent concurrently in multiple threads Documentation ------------- - The user's guide has been updated (http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html)libjgroups2.6-java-2.6.15.GA.orig/doc/NullDestAddresses.txt0000644000175000017500000000215711366547366023241 0ustar twernertwerner Nulling of destination addresses for optimized marshalling ========================================================== Version: $Id: NullDestAddresses.txt,v 1.2 2005/08/26 11:06:37 belaban Exp $ Author: Bela Ban Date: Aug 26 2005 When we marshall a message (org.jgroups.Message), we can transmit a null value for the destination, because the receiver can determine the destination: - for UDP: if received on the multicast receive socket, the destination is the multicast address (same as null) if received on the unicast receive socket, the destination is the local_addr (ourself) - for TCP: we use the MULTICAST byet sent with the message when unmarshalling the message: - if true, we leave the deatination null (= multicast destination) - if not set, we set the destination to the address passed to use from the ConnectionTable This requires that when marshalling a message, we send a multicast byte with each Message (or once for bundled msgs) based on the destination address ! Note that we *cannot* modify the destination address in the message itself, otherwise retransmissions might fail ! libjgroups2.6-java-2.6.15.GA.orig/doc/PerformanceNotes.txt0000644000175000017500000000411311366547366023115 0ustar twernertwerner Check list for performance tuning (mainly gige) =============================================== Version: $Id: PerformanceNotes.txt,v 1.1 2006/08/15 08:23:33 belaban Exp $ Author: Bela Ban, Eric For the transmit queue: $ /sbin/ifconfig [eth0|eth1] The txqueue size is currently 1000. To get the kernel queue size for incoming packets: $ sysctl -a | grep net.core.netdev_max_backlog For socket buffer info: $ sysctl -a | grep net.core net.core.divert_version = 0.46 net.core.hot_list_length = 128 net.core.optmem_max = 10240 net.core.message_burst = 50 net.core.message_cost = 5 net.core.mod_cong = 290 net.core.lo_cong = 100 net.core.no_cong = 20 net.core.no_cong_thresh = 20 net.core.netdev_max_backlog = 300 net.core.dev_weight = 64 ** these are the default and max sizes for socket buffer memory net.core.rmem_default = 65535 net.core.wmem_default = 65535 net.core.rmem_max = 131071 net.core.wmem_max = 131071 Here are some other links: Squeeze Your Gigabit NIC for Top Performance http://www.enterpriseitplanet.com/networking/features/article.php/3497796 How to achieve Gigabit speeds with Linux http://datatag.web.cern.ch/datatag/howto/tcp.html Enabling High Performance Data Transfers http://www.psc.edu/networking/projects/tcptune/ >> #2 What's the MTU (do you use jumbo frames, or the regular 1500b) ? In general, we have not tuned this environment. This needs to be addressed as part of the QA Lab upgrades we're targeting for next month. I'll be creating some Jira tasks and working with Ryan's team on this, so we should discuss the network needs along with other system relates topics (oracle tuning, etc.). Regular 1500 for now. We need to revisit this for some upcoming QA lab updates were doing. If we need a dedicated GIGE switch for testing, we need to get this in the budget (I think it's worth the 1.5k myself). If we can't get a dedicated switch, we'll see if we can enable the jumbo frames on the private side subnet (but this requires ALL connected machines be gige w/ jumbo frame support - not an issue w/ the current hw, but something to think about). libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.2.9.txt0000644000175000017500000001322611366547366022707 0ustar twernertwerner Release Notes JGroups 2.2.9 =========================== Version: $Id: ReleaseNotes-2.2.9.txt,v 1.8 2005/12/09 14:53:36 belaban Exp $ Author: Bela Ban Date: Dec 9 2005 JMX support ----------- The channel and most protocols can now be accessed via JMX. This can be used in any environment that provides an MBeanServer, e.g. JBoss or JDK 5. With JDK 5's jconsole, for example, retransmission counters can be viewed in realtime, or operations can be invoked that dump the retransmission windows for NAKACK etc. More information is available at http://wiki.jboss.org/wiki/Wiki.jsp?page=JMX. Push model available in JChannel -------------------------------- Instead of having to pull (receive()) a message out of a channel, a Receiver listener can be registered with a channel. When a message, view change, state request etc is available, the listener is called immediately. This avoids a context switch, given that all messages are usually placed in a queue inside the channel and then pulled out by the application thread. Here's an example of how this can be used: JChannel ch=new JChannel(); ch.setReceiver(new ReceiverAdapter() { public void receive(Message msg) { System.out.println("-- received " + msg); } public void viewAccepted(View new_view) { System.out.println("-- new view: " + new_view); } }); ch.connect("demo"); ch.send(new Message()); Note that ReceiverAdapter is a class which implements the Receiver interface, so that only the methods one is interested in have to be overridden. Fine-grained interface binding ------------------------------ Attributes receive_on_all_interfaces and receive_interfaces enable receiving multicast packets on all or a number of interfaces, e.g. receive_interfaces="hme0,hme1,192.168.5.3" Retransmission from random member --------------------------------- [NAKACK] This is helpful if we have a large group, and want to avoid having to ask the original sender of a message for retransmission. By asking a random member, we take some potential load off of the original sender. Added payload to MethodCall --------------------------- Needed to pass additional information with a method call, required in JBossCache. Common transport protocol TP ---------------------------- UDP and TCP now derive from this, therefore common functionality has to be implemented and tested only once. TCP now has many more properties supported by TP. Bounded buffer in message bundling ---------------------------------- Message bundling has by default always used unbounded buffers. For very fast senders, this could result in more and more messages being stored in the queue before the bundling thread could dequeue and send them, resulting in out-of-memory issues. The bundling buffer is now bounded, by default a size of 2000 elements is configured. This can be set via outgoing_queue_max_size="". Performance improvements ------------------------ 50% speed improvement for RpcDispatcher/MessageDispatcher/RequestCorrelator/MethodCall. Most headers now support size() and Streamable, making marshalling and unmarshalling faster. Discovery of all clusters in a network -------------------------------------- With JGroups/bin/probe.sh or probe.bat, it is now possible to discover *all* clusters running in a network. This is useful for - management tools that needs to discover the clusters running, and then drill down into each individual cluster - for diagnostics and trouble shooting Details at http://wiki.jboss.org/wiki/Wiki.jsp?page=Probe View reconciliation (VIEW_SYNC protocol) ---------------------------------------- When a coordinator sends out a new view V2 and then leaves (or crashes), it is possible that not all members receive that view. So we could end up with some members still having V1, and others having V2. The members having V2 will discard all messages from members with V1. Note that this is a very rare case, but when it happens, the cluster is screwed up. VIEW_SYNC solves this by having each member periodically broadcast its view. When a member receives a view that is greater than its own view, it installs it. Thus, all members will eventually end up with the same view should the above problem occur. Note that the view sending is done by default every 60 seconds, but it can also be triggered through JMX by calling the sendView() method directly. See JGroups/doc/ReliableViewInstallation for details. Address canonicalization ------------------------ To avoid many instances of Address, 2.2.9 now uses canonicalization, which points the same addresses to the same memory location. Therefore most addresses can be garbage collected as soon as they have been created, resulting in reduced memory overhead. Can be turned off with -Ddisable_canonicalization=true. TCP_NIO support --------------- A new implementation of NIO support has been added. See configuration sample tcp_nio.xml. You can configure the number of "reader_threads", "writer_threads" and "processor_threads". This should allow for large scale TCP based deployments, compared to TCP. Bug fixes --------- Critical: in rare cases, the digests could be computed incorrectly, leading to higher message buffering than necessary Critical: message bundling (in TP) changed the destination address, so when unicast messages had to be retransmitted, because dest=null, the receiver would drop them. This would cause UNICAST to stop delivering messages, which would accumulate forever ! This happened only in very rare cases when a high sustained throughput was encountered (e.g. 20 million messages sent at the highest possible speed). Workaround: set enable_bundling="false" in UDP. Minor: Apply recv_buf_size/send_buf_size to all TCP socket connections. Many smaller bug fixes. libjgroups2.6-java-2.6.15.GA.orig/doc/MarshalingFormat.txt0000644000175000017500000000330511366547366023103 0ustar twernertwerner // Author: Bela Ban // $Id: MarshalingFormat.txt,v 1.9 2007/05/02 02:47:51 belaban Exp $ Binary format for marshalled messages ===================================== An org.jgroups.Message is marshalled to a byte stream as follows. - version ID: short (2 bytes). Version.encode() encodes the major, minor and patch version into the short. - flags (byte): - single message or list of messages (LIST) If single message --> SingleMessage, else MessageList - multicast (MULICAST) or unicast message (for optimizations) SingleMessage: - leading: byte. Has bits set for null src and dest addresses, buf and headers - flags: byte - src address: Address - [length of buf]: int - [buf]: byte[] array - [Headers]: list of headers --> Headers MessageList: - length: int. Number of messages in the list - src address: Address - 1-m Messages: --> SingleMessage, but with no src and dest addresses Headers: - length: int. Number of headers - For each Header: - Key: UTF-8 string - Header Header: - magic_number (short) - if magic number == -1 (not present): - no-op - else - UTF-8 string (class name) - size in bytes (short) - contents (header-specific) UTF-8 strings: - All strings start with a short that is the length of the string (DataOutputStream.writeUTF(String)) Notes: - In most cases, we don't need to send the dest address, because the sender knows whether the message was received on the unicast or multicast socket, and can thus set the dest address in an incoming message to its own local address, or multicast address - This is currently as used by UDP. Once we move to Transport (e.g. including TCP), this needs to be revisited. Currently (2.2.8), TCP uses externalization, *not* Streamable.libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.5.1.txt0000644000175000017500000000173211366547366022701 0ustar twernertwerner Release Notes JGroups 2.5.1 =========================== Version: $Id: ReleaseNotes-2.5.1.txt,v 1.1 2007/09/20 08:13:53 belaban Exp $ Author: Bela Ban JGroups 2.5.1 is a patch release for 2.5 GA. It contains no new functionality, but only bug fixes. Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Bug fixes --------- AUTH: bug in 2.5 which caused AUTH to fail on second and subsequent JOIN attempts *if* the first attempt was rejected by AUTH. [http://jira.jboss.com/jira/browse/JGRP-577] VIEW_SYNC: there was a regression in 2.5, which causes VIEW_SYNC messages to get dropped. Note that this bug didn't occur in 2.4.x. [http://jira.jboss.com/jira/browse/JGRP-586] X.509 token not marshalled correctly. This affects ENCRYPT. [http://jira.jboss.com/jira/browse/JGRP-576] The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland, Sept 2007 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.9.txt0000644000175000017500000000336111366547366022712 0ustar twernertwerner Release Notes JGroups 2.6.9 =========================== Version: $Id: ReleaseNotes-2.6.9.txt,v 1.1.2.2 2009/03/31 12:55:15 vlada Exp $ Author: Bela Ban JGroups 2.6.9 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2, 2.6.3, 2.6.4, 2.6.5, 2.6.6, 2.6.7 and 2.6.8. Below is a summary (with links to the detailed description) of the major new features between 2.6.8 and 2.6.9. Bug * [https://jira.jboss.org/jira/browse/JGRP-914] - FLUSH: concurrent (partial) flushing blocks * [https://jira.jboss.org/jira/browse/JGRP-916] - TCP_NIO uses default IP address instead of bind_addr * [https://jira.jboss.org/jira/browse/JGRP-928] - Deadlock with concurrent stack in FC if RPC response blocks * [https://jira.jboss.org/jira/browse/JGRP-946] - JChannel: automatic reconnect ignores Feature Request * [https://jira.jboss.org/jira/browse/JGRP-911] - STREAMING_STATE_TRANSFER: investigate use of UDP datagrams instead of TCP socket * https://jira.jboss.org/jira/browse/[JGRP-941] - Merge: log merge results and make them available via JMX Task * [https://jira.jboss.org/jira/browse/JGRP-922] - FLUSH: let application handle concurrent flushing * [https://jira.jboss.org/jira/browse/JGRP-943] - FC: set ignore_synchronous_response to true by default * [https://jira.jboss.org/jira/browse/JGRP-944] - JChannel: automatic reconnection might fail Manual ------ The manual is online at http://www.jgroups.org/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada April 2009 libjgroups2.6-java-2.6.15.GA.orig/doc/tests/0000755000175000017500000000000011621261107020221 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/tests/ManualTests.txt0000644000175000017500000000764511366547366023262 0ustar twernertwerner$Id: ManualTests.txt,v 1.11.2.1 2008/10/30 15:48:13 belaban Exp $ Group Membership Service (GMS) Protocol ======================================= Hopefully these tests can be automated some time in the future. Currently it takes too much time to write automatic tests. The tests should be executed before releasing a new version of JGroups. - All the tests use the JGroups/Demos/Draw program - All tests should be run with both FD and FD_SOCK - All test should be run with FLUSH (flush-udp.xml) and without FLUSH (udp.xml) - When FLUSH is used tests should be run with both Channel.BLOCK turned off and on When *FD only* is used make sure that timeout and max_tries are relatively low. For example, timeout="2000" and max_tries="3" are good reference values. These settings are important for tests 9,10 and 12. 'Leave' means exit the demo program gracefully, ie. through "Leave&Exit". 'Kill' means just kill the process id Test 1 ------ - Start A, B - Leave B --> B should leave and window should disappear --> A should show 1 member Test 2 ------ - Start A, B - Kill B --> B should be suspected --> A should show 1 member (run with FD_SOCK first, then with FD only) Test 3 ------ - Test 3 - Repeatedly start B and kill B --> A should always show the correct mbrship Test 4 ------ - Start A, B - Leave A --> A should leave and window should disappear --> B should be new coordinator --> B should show 1 member Test 4.1 -------- - Start A, B, C - Leave A - Leave B - Leave C - Everyone should be able to leave gracefully, no error messages such as "FD_SOCK.up(): I was suspected by 192.168.5.2:1466; ignoring the SUSPECT message" Test 5 ------ - Start A, B - Kill A --> A should be suspected --> B should become new coordinator --> B should show 1 member Test 6 ------ - Start A, B, C, D - Kill B and C --> A and D should show 2 members Test 7 ------ - Start A, B, C, D - Kill A, B, C simultaneously --> D should become new coordinator --> D should show 1 member Test 8 ------ - Start A - Leave A --> A should leave in a short time (no hangs) Test 9 ------ - Start A and B concurrently - They should form a group of 2 Test 10 ------- - Start 20 instances - Mbrship should be 20 - Kill and leave members randomly - Last member should show mbrship=1 Test 10a -------- - Start 10 instances in shell #1 - Start 1 instance in shell #2 - Kill shell #1 - The view of the only surviving instance should be 1 Test 11 ------- - Start A, B - Shun A (CTRL-Z and then fg) - When B has rejoined the group, kill A - The view needs to be {B} now Pull-the-plug scenarios ======================= - Run Draw with udp.xml, tcp.xml and tcp-nio.xml, those configurations may need to be adjusted to use the IP addresses of the 2 nodes - Make sure loopback=true in UDP/TCP/TCP_NIO if running on Windows - 2 nodes, node 1 and node 2, connected to a switch Test 12 ------- - Pull the plug on node 2 - Node 1 and node 2 should each show 1 member - Put the plug back in - Nodes 1 and 2 should merge into a group of 2 Test 13 ------- - Pull the plug on node 1 and node 2 - Node 1 and node 2 should each show 1 member - Put the plugs back in - Nodes 1 and 2 should merge into a group of 2 Test 14 ------- - Pull the plug of nodes 1 and 2 *on the switch side* - Node 1 and node 2 should each show 1 member - Put the plugs back in - Nodes 1 and 2 should merge into a group of 2 Test 15 ------- - Kill the switch (pull the power) - Node 1 and node 2 should each show 1 member - Power the switch back up - Nodes 1 and 2 should merge into a group of 2 Test 16 ------- - Kill the switch (or pull the plugs on all nodes) - Power up the switch (or reinsert the plugs) - After the merge: - Leave the new coordinator - The leave should immediately install a new view (bug: http://jira.jboss.com/jira/browse/JGRP-350) Test 17 ------- - Run tests 16-20 with 3 nodes Test 18 ------- - Run Test 16 with DrawMultiplexer with "tcp" stack libjgroups2.6-java-2.6.15.GA.orig/doc/manual/0000755000175000017500000000000011621261107020334 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/0000755000175000017500000000000011621261107020736 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/0000755000175000017500000000000011621261107022203 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Tunneling.fig0000644000175000017500000000403311366547366024661 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 2 1 0 3 0 7 100 0 41 0.000 0 0 -1 0 0 2 3000 225 3000 3675 2 1 0 3 0 7 100 0 41 0.000 0 0 -1 0 0 2 6675 225 6675 3675 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2250 2325 3975 2175 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 7575 2325 5250 2175 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 1050 1125 2250 1125 2250 3000 1050 3000 1050 1125 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 1050 2550 2250 2550 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 1050 2100 2250 2100 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 1050 1650 2250 1650 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 7575 1125 8775 1125 8775 3000 7575 3000 7575 1125 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 7575 2550 8775 2550 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 7575 2100 8775 2100 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 7575 1650 8775 1650 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 3975 2025 5250 2025 5250 2700 3975 2700 3975 2025 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 1 2 1 1 1.00 60.00 120.00 1 1 1.00 60.00 120.00 2250 2697 3975 2547 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 1 2 1 1 1.00 60.00 120.00 1 1 1.00 60.00 120.00 7573 2731 5248 2581 4 0 0 50 0 16 11 0.0000 4 120 495 1350 675 Host A\001 4 0 0 50 0 16 11 0.0000 4 120 495 4200 675 Host B\001 4 0 0 50 0 16 11 0.0000 4 120 510 7800 675 Host C\001 4 0 0 50 0 16 11 0.0000 4 120 300 3150 2175 TCP\001 4 0 0 50 0 16 11 0.0000 4 120 360 1425 1500 GMS\001 4 0 0 50 0 16 11 0.0000 4 120 210 1500 1950 FD\001 4 0 0 50 0 16 11 0.0000 4 120 390 1425 2400 PING\001 4 0 0 50 0 16 11 0.0000 4 120 630 1275 2850 TUNNEL\001 4 0 0 50 0 16 11 0.0000 4 120 630 7800 2850 TUNNEL\001 4 0 0 50 0 16 11 0.0000 4 120 390 7950 2400 PING\001 4 0 0 50 0 16 11 0.0000 4 120 210 8025 1950 FD\001 4 0 0 50 0 16 11 0.0000 4 120 360 7950 1500 GMS\001 4 0 0 50 0 16 11 0.0000 4 150 945 4125 2400 GossipRouter\001 4 0 0 50 0 16 11 0.0000 4 120 300 3150 2775 TCP\001 4 0 0 50 0 16 11 0.0000 4 120 300 6075 2175 TCP\001 4 0 0 50 0 16 11 0.0000 4 120 300 6075 2775 TCP\001 libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Architecture.eps0000644000175000017500000001116011366547366025361 0ustar twernertwerner%!PS-Adobe-2.0 EPSF-2.0 %%Title: Architecture.eps %%Creator: fig2dev Version 3.2 Patchlevel 3c %%CreationDate: Fri Feb 15 22:09:45 2002 %%For: bela@sjc370334 (Bela Ban) %%BoundingBox: 0 0 209 285 %%Magnification: 1.0000 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save newpath 0 285 moveto 0 0 lineto 209 0 lineto 209 285 lineto closepath clip newpath -103.0 333.0 translate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def $F2psBegin %%Page: 1 1 10 setmiterlimit 0.06000 0.06000 sc % % Fig objects follow % % Polyline 7.500 slw n 2925 2880 m 4335 2880 l 4335 3225 l 2925 3225 l cp gs col0 s gr % Polyline n 2925 3285 m 4335 3285 l 4335 3630 l 2925 3630 l cp gs col0 s gr % Polyline n 2925 3705 m 4335 3705 l 4335 4050 l 2925 4050 l cp gs col0 s gr % Polyline n 2925 4110 m 4335 4110 l 4335 4455 l 2925 4455 l cp gs col0 s gr % Polyline n 2925 4530 m 4335 4530 l 4335 4875 l 2925 4875 l cp gs col0 s gr % Polyline n 2955 2775 m 2850 2775 2850 4845 105 arcto 4 {pop} repeat 2850 4950 4320 4950 105 arcto 4 {pop} repeat 4425 4950 4425 2880 105 arcto 4 {pop} repeat 4425 2775 2955 2775 105 arcto 4 {pop} repeat cp gs col0 s gr /Helvetica ff 150.00 scf sf 3360 3120 m gs 1 -1 sc (CAUSAL) col0 sh gr /Helvetica ff 165.00 scf sf 3375 3525 m gs 1 -1 sc (GMS) col0 sh gr /Helvetica ff 165.00 scf sf 3375 3975 m gs 1 -1 sc (MERGE) col0 sh gr /Helvetica ff 165.00 scf sf 3375 4350 m gs 1 -1 sc (FRAG) col0 sh gr /Helvetica ff 165.00 scf sf 3375 4800 m gs 1 -1 sc (UDP) col0 sh gr % Polyline n 3600 2775 m 3600 2475 l gs col0 s gr % Polyline n 2700 1350 m 4200 1350 l 4200 1950 l 4575 1950 l 4575 825 l 2700 825 l 2700 1350 l cp gs col0 s gr % Polyline n 3600 4950 m 3600 5325 l gs col0 s gr % Polyline n 2175 5325 m 5175 5325 l gs col0 s gr % Polyline n 2700 1425 m 4125 1425 l 4125 1950 l 2700 1950 l cp gs col0 s gr % Polyline n 2700 2025 m 4575 2025 l 4575 2475 l 2700 2475 l cp gs col0 s gr /Helvetica ff 165.00 scf sf 2850 1125 m gs 1 -1 sc (Application) col0 sh gr /Helvetica ff 165.00 scf sf 3225 2325 m gs 1 -1 sc (Channel) col0 sh gr /Helvetica ff 165.00 scf sf 2625 5550 m gs 1 -1 sc (Network) col0 sh gr /Helvetica ff 165.00 scf sf 3150 1875 m gs 1 -1 sc (Blocks) col0 sh gr /Helvetica ff 165.00 scf sf 3150 1650 m gs 1 -1 sc (Building) col0 sh gr /Helvetica ff 165.00 scf sf 1725 4125 m gs 1 -1 sc (Stack) col0 sh gr /Helvetica ff 165.00 scf sf 1725 3900 m gs 1 -1 sc (Protocol) col0 sh gr $F2psEnd rs libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/PullPushAdapter.png0000644000175000017500000002662311366547366026023 0ustar twernertwerner‰PNG  IHDRíÆ~·8Å pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R « IDATxœíÝQ»c:à:Ïüÿ¿ì\t¦ÛV""!‹÷½šé.R->+Á0Žã  ?Uæ2 C•ùâ<.7o‡² À”XçÛ—cÙÅ <]"\.7Ç|o®¶R€;I¸DVŽùlŸ6K€MÓLc· Mmä[#@;O8G*Ç(Ã!Í@kË9ƶP…Ý)4•Ê169€*”·¡‘ÿ¾_rc€ºÄhdžcœ4´ãDêZ¨Ç¼„€Ú>ûUQ*ú•cl]í8E„ê~rŒ%€s8i„ZæýJB @;ö±P×òøšR’*ä€S)É@EsŒ3 œ_õg @ ú•€¨ä€kèЇã䀳éćZþ{9'bú©Ç8?bѯD%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr ÕŸ«@§†aøü{Ç [òíÓ¶iÃ_ìDÏm M=€mÓLýP`àRFÝådr 7Ñstè¹m¡éW åsV¡¡Cr s:GˆBŽ¢2>€_¾ûÆq|¿8 ÃZ…fVÂYœIÝ 7g•~OæÌ{Ó¾§ú~Ûû•ï^¹ÍõÓ¶ï¹íý\·!ÇÜ„~ëƒ2·yëùžs<øVüÙ·”D:>á‘V½V>ib{OLU«aùKÙû¹nF¿5mþ«O˜cï¢s–XåÄfs&Å+-gæ7 s+OˆÞÕlçÖóC<áðm­û#§kiöþïyæL^ÀŒz õP¨ß;Îô~<—`9úrËΔDJ¾åçN£_‰U/i®xMõtž„§}Þ¦ Ú5˜`ÍO=F›®º–6ùÅî•XcÃ?g¶à¸ÿ^Nz¨*' å5ÞÖö~ŠWìÞ'(Õúgg»v{˜ nÉø–U¹Oî ·‡yf /^±ßÇþtªhô æÏá®wºÁG€NÃ/Uî”:Iõû <ù^®Å+v¶ÒrîŒ×âœ5#1yÓŸP?¢·z CJ»[®¥ae ½Ÿ‚AÍ»nðŸxÑo°ŠË„ð«ýÉ5tåý[:xyQÁc†n ç™GÅ'Â*ßà—7èßü ·vAùúŠíZuÖó£´ûºý ýJ§zì/hažcl`'PŒ*~rŒ  5çŠP×B¿’Í  …*·e¦~å˜nKpoB T4¯ÇØÀZp~-¬^¯d“¨E4²cô.T$Ä@;ËõQ  {Qhjõ9‘Ÿ;£»ï$@̇bG¤îç;Ž£Â @!αý\‚i”‘f6MÄ1ÐÔj¿ÒÔô黳(cníÏîN0ìÚÒÔc6I0pš}9f>±X ¸Àuå˜[r}42 v8@eÛã|jQÄê’c€¨äà *1@ r •œJa¨HŽ¢’c€æÔ`€Fä *98›ò P‹D%Çm©¾íÈ1ÀÍôCŽÍô@Žrº™®%ÇÀ!Í\HŽ š¸„Õ4p29*ÓÍp9ê3hàr ´bÐ @kr ´eÐÌÛc?8ДgÐÍô6]ÇÉ1pƒfª“càTÍT$ÇÀ š¨BŽËèf8HŽ+4p„×3h Œ½0h`/9ú¢› ŸÝ1h “2h`“]3h AŽt3,’c ƒf¾É1‰A3Sr ÄcÐ À›Qéføsu¨Ã1ŒW„ŸAÿ-<Ù4ŒÔc€¨ÔcnŹÓ‚GW¿‡wújÒµ”¦  õ¸ƒf€G‘cànÜix9îÉf€'càÎÜi¸79îO7pWr <‚A3À-É1ð Í7#ÇÀã4܆¥› ¸9žË  :9žÎ  .9x½ šb’c€º™€Xäàƒf€@ä`A3@r °Ê  sr °A7Ð-9ØfÐ Ð'9ÈeÐ Ð9ØÇ  r PB7Ð9(dÐ p998Ä àBr PA3À%äºæˆ‹n&àdr 8"bÐ p&9†®}C0h8ÇŸ«¦£.¦ÿ˜&éÓû;b€vÔcC‡EP'ÐŽC0ÒLD¢ ЈCH†_ð’cÍ=KNŽ!<…€Ç’c¸…€g’c¸Yš¹¶1œ@ŽánD€çc¸!Qà!äîI”x9†ÛenOŽáÎÜFàÞä *9ˆJŽ¢’c¸3#|îíÏÕ €&¦ Æh_€»’cèTYù.À17&ÇЋDPY÷p{r ×[‹)ï ²7Ĉ/Ï!Çp¥YFYŒ r käº ¬P@Žá2ŸbŒ=Èìí¾Á.î ÖöW5r¯ºû·ê`Þi÷ëþ1\ì[<Ê0 ýß™)D#ËÜõs“c¸˜m" ±å†hä¦{|ŠoÃ?ç£_‰ËŒãøþà*ýHlýSC4’¨Çp¥ÏžÈ~BXÂÒ›,`‡¹HŽábÓ-ÓÆ ý Q= ÑÈwÛˆÃõf'OÒ ™Œ¡ ³[÷¾ÿáüÙ¬ôPZ8ØÈų¬œSöè·Å©v +¬µÜÄ„³5¶¹Äï7$vø9í—cèÈbš™ýàk¥âÍó®Å w­ìýÒ­Ý;Õ+¯ÁkŸ´zû§3Ô¯DwÆf¯¿]Ò6 ÄÖW½‘›3¬žŽ(›mÎTeôÝÛž]k[=†~¥/:¨~ƒK`Sˆí®ÝÝog˜s.¸+ñZ×f×R¢’5Ê>czqŸÖ~_u•^9kýÌPŽ!†ô½‡~w¸gV\r”=Ëöû Ÿ,ÒÔâr_[«ní3ĵœÅo‰CH‰„þýW º[YˆFñ]’©êÊ(“™–¦)ãc¸ƒÙxh Åák½iÔÈ‚KY3Ò5æœy&ÞÓèë[›mÙâò§Rá>\¼ U„ØjÎlä´/ãä^ìËOÉ®j@þråîæ;Í„Ø)=ûÚÉ纻¸ËcS9†{:gð4‹×ãÔ­þ^²ïÚ¼_W·{T9†Ûò†Nm:ËŸ\ÛCûÔ®{í/^¦ôý¶Óšô=ág鳫öÎ33Ïm>þzfó9š‹íÏYÛr }©ÞÃ*ÄÀ9Ò vr aq#7ÛæßéÛ[µwmç|ºÍl‘^{ùÉlÏçßr ×Ûüé—íþ$8ÙÚótºÚ‹™ µÚVQúcî}ý•—g’¸Ïïf¹%çkr=ê\ЛÀ†nöT¸@¦ ?Ñv¬îáòk¶Õc肽9\¯Äe.OñD'Çp1!€br ëá‚’c¸ŒJ É1\OI€2®WâJÓGR¿Th¢¹|¿­ÃŦÛÀiObàä®7Ž£4@ýJôbvjz`“C_Ÿ©$Ó°HŽ¡Sk‰Ì|O ÇлYRY‹5 ÀÉ13<û÷ iÆ%âýp½ÿ|^9íZ'WTô@Žáf¦išY¸Àùäne–fÚ-¥õ"È!ÇpC'fD€È1ÜÓ ãpE€ËÉ1Ü–&€Û“c¸3U€{“cà(Qà*r T Ê\BŽáÎÎL¢ ÀùäîizÅõiÏeN&Çp+Ã?ŸWN~’(³ÈC©€F<'’ØÖâÂ…‡Ìqß­†Á‘ )9†®M£@f…£‡è ÊœCŽ¡wéøÒmJeN Ç@Ð Ê´fœ/]›>¿:"Ã~š’c -Q 9še‘cà ¢ @ r œD”¨NŽóˆ2uÉ1p*Q "9Î&ÊÔ"ÇÀD€*丆(pœ—e’càJ¢ Àžy+Ž…¡Ýþë»ýΧD¥s¡ ÍÛ§\q¿oóýÑî÷¹€Ë©Ç@/Œ•ØKŽy ðyØÈy'Êì"ÇlsDáL¢ @>9&—®}N#Êd’cþrÀ +¢ @9F¡…N‰2›ä˜ !\H”H“c²¨ÙpQ AŽùá8AŸD€5rÌë¥ÜB÷D€ErLŠcýe¾É1ÛTkè„(0#Çüâð@çD€)9æ/E¢e<ìhGŽYå Á…‹2È1œDÒ'Qà%Ç@\¢ €óCé…pDàáä˜eŽ D!ÊO&Ǥ¨Ð‚(<–w ÊÏ$Çü¢C\¢ ð@r ܇(<³Jm†ˆDàQä¸Qx9fN†e€‡càžDà ä˜eª2Ü€(ÜÞPpÀ¶O¨Â)¤Dõ§xÊÌÓˆwñÆ9GSV2›>eÔó'~Ÿ‹¶¡ õxce€[’cà).‰2bДrUUF§ЈÏ¢ƒ ¸9G”nCŽ'e€{cà¡Dàäx.QˆNŽGe€Ðäx:QˆKŽZEO$Z“c€×KUˆIŽþe€päà‡(Ä"Ç¿ˆ2@ r 0'ÊQÈ1ÀQAŽ–Œ2p9Xu¼*ãæ1@S®n@–µ}h¬]äçSÄj67Žãû§; ƒŸ.ЛÞë1Ã0$NÓª(¨ÊØ0stc2w…ö˜ÐZY“ú ÐZ¿ýJ³Ýå÷qúohMСNë1ÓŒ2ŽãâNsöºª ´–Y•±1§é4Ç|lžö9/„3M£L:¯Ø6ôžc€Þ$ê †Þ'ëw|L¾ÍÓ¾ïëÚ$ß—FoÓ9²8êý“þ —Y{@k½ç˜ƒ# ×N ߯§ç¼8mzÂôâàf¦ifö"À9:íWª2€wsÂôiöN(¬pCão– 3 ž¬ßzÌç"Ï××åK9“§Ï§sNÌpmª]‹³‡€FúÍ1¯•ªõÞL³vÍöf¼øž°xª—4 tcÞÒG|¿çûmk³­uS/.Ñéø˜5kÝðÕ$Y6a~p\€z̚٥ыe•¸±`õ˜EeO°¢ë´³6ðà£ÓS‹7v‡~¥~ßX(ÐiŽÙ5ä%ñ<£t°¨uPU¸D€~¥Ä#rìròuLß­U‰€FúÍ1³›çîÚc ÒSÛÛZà¸Nû•ÞòCÆÚÃjÍ¿¸ @;ýÖcÞ6Gº¤ÓÃÚ³eŽôâiˆÂoˆ¢÷óq$y¬ëìÕÚ{l ®098M¬Ì­zOÖõø€9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨NzN¤¹Õ©ÇQ5¯ÇŒãØzÀ3©ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •DÕü9‘@8Ã0\Ý€,ê1sÃ0؉@ê1ÀÜ8ŽW7a'ðdê1@Tr •D%ÇQç À²Å1ÔéaàŸIboÊ:iJŽ`.q˜£2]ѯÀ/™—²»â¨Çðc–N¾‹.â ]Qà¯iFÇq±çhú¢LÃåä^¯¯“x§‘1ôCŽà—œ˜òy’ ×2>€9qç;ålNµëbïï‹§6Ç÷OµÙNeªKÈ14¹šz1‘¼_LÇ‹]S/®xªµvæ4’êô+P_º¿éû¯9ýS9wµÙõׂ©6Û©£ídê1´2+Nää‰#Wzg.®xªt÷Óç¯Ã0¨ÊœF=€&¾å›G÷Í+½ë.îÈTeRÀÅÆ®nHJNuÇe\ç“c¨ïäPR¶¸3§¢ãcèTŸU>[õXr %ª_ª-P@Žà5Žã;F\r­‡SRLŽàJ-nÁ×T”v>„q¾ürð–tp&9€×ëw™!ÿF·'':ÉL9×TÿœÕ(äþ™E™²ó[|XAŸ™àä5C‚ñ1üø ø}K›«c2»´®¡2]9‰¦@s&õ~)~@£Ååwx ìÑ ´£Ì]~¨àrïƒñâ/¡ÅqzmqŸeͪD×Úl-g*©Î½¿¼»~a÷þtÖÏ¡b¯pÛ¬] T¡ÌÅ:¸ÆÍ^ÀqÆÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr ÕŸ«tg†«›E=ˆJ=˜Çñê&ì zO¦D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQý¹º@GÆql7óaZÌ¿i›Î©ÇQÉ1Ày†a¸º À­È1À$ 9ˆJŽ¢’c€Sé`*’c€æd 9ˆJŽ¢’c€³éfj‘c€¶¤ 98ƒ§ -È1@Tr pªwaFgP…4$¯MÉ1@sÇÈ1@Tr p6Cd€Zä IhMŽÚ28hGŽ¢’c€ "T!ÇMÈ(À ä !ƒc€¦ä *9¸†!2Àq®npO9=Jz€ƒ†‚ýˆó'€*$98H¿UI=æÞÞÕ&«ª³qÕ©ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •D%ÇQÉ1@Tr •ó†a†ãïN&Çl“` OrL®q¯nð‹ó—¢ „#Ç(´@TrÌuè–“EÍ:$ÇüPz€Xä˜×K¹b’cRTh grÌ6Õè“ó‹ "Çü¥èáÈ1«Ôf srÌuè–D%ÇüPz€Xä˜eÇ@ÿä˜è™D%Çü¢È1@TrÌ*µèœD%ÇÌ)Ã@r •³LUú'Ç,b „Á1J=ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢’c€¨ä *9ˆJŽ¢úsuÈ2 CÎÛÆqlÝ’Ûø¬Òâ•¶ö¥tþ-ÿàýcnå}ˆr|j-+} §Ñ¯tC™Å›ZËz;m‰—Ëü°ko{àhG=&˜ÄY¾Cã f+ùû똾aU€¦ÔcîczÈ”iZ˜®Õq3Êìu_@SrÌ­8û?ÇæzöEœCŽ¢2>æ¾/µM\|ûÝ’óžôE:9óÜœª`’Ì©Z˜-w×[ìÚü «+1%% 9æq#6—ؼwÍsqª‚I6§*Ónïæ·“Ÿ{ >¸„£_éV6G•„˜ü7ÔšgÎÝYª,h¯Öxsæ¹V×92ÏÙ;… ×…ÆsŒ™]M³ö§œ7$Øknö=eÎ3óOç¶w(ëôÙ\nâëKüõÈꪲ6®¥_)˜ãe€ôêû¯ïWŽ,·lž‹SL²¹ ½ç™N!óÿ~±úêúb€¸ä˜ªR`H¼¹l¼mÎ<7§Êqœ³ ãE‘²XPа‚Õ•˜‰Dd|Ì­¬Ýœmú†²¿äÊæY°¸Ï_ôÇôõÓn‚WýQ—Q¨Ç㤹s³+ÛW€ÚEï2’_ŽM|†§ìÊçHò‡ÑtHŽ\M‡’ìze-凜E.€È1p½KÛ®ÝRO”1ΗGî’Wwžé?½U™Û=¬®ÙJŽ¡ÉåÁeóÜû¡â•ù~:UBÝðTå‰Ki»>@'ô+ñË÷CyŽÕÊæ™ž*ñ˜¡ÖOX[ÖÚB÷>á¨ìâí‚ÕpúÂc88~bïc Öìzø@­yæLud’¦· Ì\c{ç9ŠfYIDAT;{?{úƒ»-‹~%þÚ¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*Tù ÊÇU>¨ò1@•ª| Påc€*T]ƒ‰Çf°mIEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/ChannelStates.fig0000644000175000017500000000235311366547366025455 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 3918.750 2850.000 2850 1125 4050 825 5100 1200 0 0 1.00 60.00 120.00 5 1 0 1 0 7 100 0 -1 0.000 0 0 1 0 3938.985 -144.431 5100 1950 3975 2250 2925 2025 0 0 1.00 60.00 120.00 5 1 0 1 0 7 100 0 -1 0.000 0 1 1 0 4234.520 2491.142 2400 2250 2700 3525 3525 4200 0 0 1.00 60.00 120.00 5 1 2 1 0 7 100 0 -1 3.000 0 0 1 0 4172.873 2668.484 5700 2250 5550 3450 4575 4200 0 0 1.00 60.00 120.00 1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 2400 1575 636 636 2400 1575 2850 2025 1 3 0 1 0 7 100 0 -1 0.000 1 0.0000 4050 4575 636 636 4050 4575 4500 5025 1 3 0 1 0 7 50 0 -1 0.000 1 0.0000 5625 1575 636 636 5625 1575 6075 2025 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 450 1650 1725 1650 4 0 0 50 0 16 11 0.0000 4 150 675 3600 750 connect()\001 4 0 0 50 0 16 11 0.0000 4 150 870 3525 2550 disconnect()\001 4 0 0 50 0 16 11 0.0000 4 150 465 1950 3450 close()\001 4 0 0 50 0 16 11 0.0000 4 120 990 525 1575 create channel\001 4 0 0 50 0 16 11 0.0000 4 120 885 1950 1725 unconnected\001 4 0 0 50 0 16 11 0.0000 4 120 720 5250 1725 connected\001 4 0 0 50 0 16 11 0.0000 4 120 435 3825 4725 closed\001 4 0 0 50 0 16 11 0.0000 4 150 465 4950 3450 close()\001 libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Message.fig0000644000175000017500000000201611366547366024301 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 2325 2175 2325 1500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 2775 2175 2775 1500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 2550 2175 2550 1500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 2100 2175 2100 1500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3675 2175 3675 1500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 4650 2175 4650 1500 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 1 7125 2175 2 2 0 2 0 7 100 0 -1 0.000 0 0 -1 0 0 5 1875 1500 7125 1500 7125 2175 1875 2175 1875 1500 4 0 0 50 0 16 11 0.0000 4 120 570 1950 2700 Headers\001 4 0 0 50 0 16 11 0.0000 4 150 1065 1725 3150 (java.util.Stack)\001 4 0 0 50 0 16 11 0.0000 4 150 690 3075 3150 (Address)\001 4 0 0 50 0 16 11 0.0000 4 120 330 3075 2700 Dest\001 4 0 0 50 0 16 11 0.0000 4 120 240 3975 2700 Src\001 4 0 0 50 0 16 11 0.0000 4 150 690 3900 3150 (Address)\001 4 0 0 50 0 16 11 0.0000 4 150 510 5550 3150 (byte[])\001 4 0 0 50 0 16 11 0.0000 4 120 435 5550 2700 Buffer\001 libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Architecture.fig0000644000175000017500000000340611366547366025343 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 2850 2775 4425 4950 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2925 2880 4335 2880 4335 3225 2925 3225 2925 2880 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2925 3285 4335 3285 4335 3630 2925 3630 2925 3285 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2925 3705 4335 3705 4335 4050 2925 4050 2925 3705 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2925 4110 4335 4110 4335 4455 2925 4455 2925 4110 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2925 4530 4335 4530 4335 4875 2925 4875 2925 4530 2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5 4425 4950 4425 2775 2850 2775 2850 4950 4425 4950 4 0 0 50 0 16 10 0.0000 4 120 645 3360 3120 CAUSAL\001 4 0 0 50 0 16 11 0.0000 4 120 360 3375 3525 GMS\001 4 0 0 50 0 16 11 0.0000 4 120 570 3375 3975 MERGE\001 4 0 0 50 0 16 11 0.0000 4 120 420 3375 4350 FRAG\001 4 0 0 50 0 16 11 0.0000 4 120 345 3375 4800 UDP\001 -6 6 1725 3750 2325 4125 4 0 0 50 0 16 11 0.0000 4 120 390 1725 4125 Stack\001 4 0 0 50 0 16 11 0.0000 4 120 600 1725 3900 Protocol\001 -6 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 7 2700 1350 4200 1350 4200 1950 4575 1950 4575 825 2700 825 2700 1350 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 3600 4950 3600 5325 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 1425 4125 1425 4125 1950 2700 1950 2700 1425 2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 2700 2025 4575 2025 4575 2475 2700 2475 2700 2025 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 2175 5325 5175 5325 2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 3600 2775 3600 2475 4 0 0 50 0 16 11 0.0000 4 150 765 2850 1125 Application\001 4 0 0 50 0 16 11 0.0000 4 120 570 3225 2325 Channel\001 4 0 0 50 0 16 11 0.0000 4 120 600 2625 5550 Network\001 4 0 0 50 0 16 11 0.0000 4 120 450 3150 1875 Blocks\001 4 0 0 50 0 16 11 0.0000 4 150 540 3150 1650 Building\001 libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Architecture.png0000644000175000017500000000677011366547366025371 0ustar twernertwerner‰PNG  IHDRç<´1/ pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R « ®IDATxœíÝí–› …aÒÕû¿åô‡S÷ŠÂæ¼Ï¯Œ‰Ó4³ƒGü|¿_Hù;û ðù|f¿£f5y;¤ÖÍûø,›ØXü™õ—‘Zè!µÐCj¡‡ÔB©…R =¤zH-ôZè1”ÚÏçsá"ä±ËåñC©½£œƒ+C#ž³É虦Ïçóý~Ãö/||$,ÝRÚÝ?íâæãqé×Vþ!ô0×ÖF‰9„qŒ¶D;¦Ï[üc÷;ˆá^îwKL^/3‘ÚQGí(.×vöì…›¬TîÓ£ðËÀ¹×›L´µ.8ô»ñ=u ÷/&Ù£ìßÖú³¢CØ.VήÒ,6ÏÆÂÓ¯°*{Ü(†ølð9F¹¼¼×µßcÖÄËJ…€ØMm¶˜òNp–ÝÔB©…R =¤zH-ôZè!µÐCj¡‡ÔB©…R =¤z6_Ë€ë~ Ú!µü^³Ç×› zH-ôZèÙ¡®Ý£V{Î~uÿ©u;þaFÙò+M…=¤zH-ôZè!µÐCj>úË¢õí¸c“ž¯G…kÑ•ºØš]oôÍ Dj¯H—OŒ]jœïG!µmgîa‚³û†am¾)RÛ5¢4‡Óq6=¤z¨Ú²gK•ôÔÍE¸É¤¶![ÅVVlNo9–}AöñíDjçã^#g‘ÚùëYœA©…R =¤zHmCv¤bYO ¡­g¤"ÞDjo© ;,ݤ—ÞÙûHm[é(l÷YŒâ A¬lÄY¤¶­2R1¼*†Ø•¯Óú—ÙËHíãÂñ1Q” î5ô!ÜR™Ì•¼~æ‚ßòÊÜmm[i^בŴœM_íRyzÚ†úHÅʈÄÒ+ ë}TÐCj¡‡ÔB©…žMÎÆ¤ÒoƒÏŠ‹ŠÐC…=¤zH-ôìp6¶Áé…¨Y'E;¤Öq™t†‰ôZè!µÐCj¡‡ÔB©…R =¤zH-ôZè!µWÜYeñÚ?÷Ü/W´É8„7±V×t¤öœÒ:_.Y¿£ç.ÏýK2"D…0LºàÜÙ-áÂuT¤ö)ikzvw‚[B…pK©® ôgÃGX›Hí9G {ŠÎk…)çy=Híië"FýbA§¡í±ÃšfiЉ;gcÐCj¡‡ÔB©…R =¤zH-ôZè!µÐ³É].„šÂµÐ9¸ }ôZè!µÐCj¡‡ÔB©…R =¤zH-ôZè!µÐCj¡‡ÔB©…R =¤zH-ôM-3v¤ÙšBX›$ò°ÉlÇ&˃Ùodu”‰¶–©…g-þ‰­k!mÿÔ.Þl¬iñµö÷O-öCj¡ÇJ‚·òoºèf«ËWæRë–ïÖ™EèûL…=¤zH-ôZè!µÐCj¡‡ÔB©éˆ¶—~Œ§ûV^o©渒tH·7ÓîÅtñ±,ï#µcD?o^~ãê]Å+ºoê¿”6¨~—eGÌE[ûA_ª6ò¥ ¡sw;hkLjFH…Ã¥Ý+³µzv·†Ô“ž3•Žïi1pl‰6–vO kHíHQŒ*?¦knñù¾ÿ>ÕQ×B©…R =¤zH-ôZè±ØóEw½:s©¥¿sTÐCj¡Ç\…@Q[¡R>™K­ÓùÛ¼LèûL…=¤zH-ôZè!µÐCj¡ÇbÏ×£Âþ£°‹-ÍŽÌî…,R;RÍÊéê2¥‘¢B&MÛµðÙ&Rû?¡<]^)»(J¨q¶Nõ t¾Þ8ÚÚGdW, Wå`ùĤv˜ìÚ3³ÞÌÞ¨FŠ‚›æ8{ÃÄt¯§ß§:R;X©¡ò Ý uTÐCj¡‡ÔB©…R =¤z,ö|Ñù¯Î\jéÝôZè!µÐc®®åT¬B¥è7—Z§ó·y™Ð÷™ zH-ôZèùë’‚æ…²)ÿ¸ãçlìì*Äû‰£Ù¹PGpÓ Oé}à™…±jum8ÑÙÏ“ö­lö©p‹wôš§ÿKØÞOj?´9ÌNÐ+=Õ9¿¸,S×â²Ê‰éA&;ï<ܱ¹=}Öˆ®kc•¾ó);åAiáDWˆWö<¸¾Ê¢Á˜FÚ©ÖN ÿ$Ù§¢K»£dÔÚŒûë:Ú€k•î©-Ûð«Ë¤},£úR蓱8zf–lÂêUS6 6“"µ“užiY8%èÇ8„å°6cmíxÙ…Ý™ñõµß7²šý‡p¯ŽNõ»ô,… z~*„›)Ë~)±¥Ÿñµé1” bY™³±æXD—Žè·“u<íçÚXtÏ ¨­.Õ†/Ž.»KD–Ž$u¿Æ|eïw•ø’^§Q‰‚Ä÷ u\õ~W•A·Ò€×´{¾ü4„¬úŠ€'üªkÑA?jz£öX®‰å{U¡ò×,Îe¨ÿJ@%þÛoò}Bßg®A©…R =¤zH-ôZèa.ÃHÙëÞõ©éUt¦à6‘ÚÁú—êÈbæE*„™š%²Y´µƒ];¾Ó¦žBjë_ª#Ë‘]%­þ;-#µo¨,ÕQºú/7pùMÔµ3}ÿs¹ùNŽÈÐÖΑ.îé»À²ÜDjGjv{ùû—©¤¨MQ!@©…R =¤zH-ôZè±ØóE¨:s©¥ûsTÐCj¡‡ÔB¹º–S± •¢ß\jÎßæeBßg*è!µÐCj¡‡ÔB©…R =¤zH-ôX¼ÊðœÒº]¥eëË-¢„Ô¾¤'©¬öÕ‰ ám,Ìq©…*„ÉX;ñR;I½€ á)¥â•S®ûhkG*ÝGÛI^]©¬gYÅævÔQ!@©…R‹¼• qR‹¢eËn‹gc+·"èa¢ï.Ò³ÿĨ gé¯Ô@é­Á‘%ñAYIí!¼ èÜw²­OÆVj=NÈRBI0šÚé?ÝYgcÐCj¡‡ÔB©…R =¤zH-ôZè!µÐCj¡‡ÔB©…R =¤zH-ôZè!µÐCj¡‡ÔB©…R =¤zH-ô<8+Ÿ•2ðÖ’€*è!µÐCj¡‡ÔB©…ÕÔFÝjÏõ²Ñ· ÕÔºî<»ýß¹é¸ÕrÔß­Ô~üÞk zœ½_sv¹wÖI^‡p[›ò‰ôYµî÷!>ÛÐfïü&ÛïB³*á þð‰ ͵#IEND®B`‚‰PNG  IHDRç<´1/ pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R «\IDATxœíÒA À0À¿çC/²¤U°ÇöÌ,H9¿à™kéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=®¥Çµô¸–×ÒãZz\Kkéq-=„AuŠÞ>IEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Message.png0000644000175000017500000000336011366547366024323 0ustar twernertwerner‰PNG  IHDRir·õ pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R «ŒIDATxœíÝÑr²:P8Ó÷eÎ…™4Ä-*Áµ®¬"~„dl§§iô³ýò8Žï©èËì†aÿÄdÇî6>‰k®\àØoÇÿ>]Ð%Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d§iÚzyßV Б;Ù°È= @Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$d@Bv Ù$døyô ãøg)¹êÇl'§R-£yÚ:+eÙ½Ô|;Ûí¶Ùmƒòñ7{8;¾ÁÜ-]Ž÷S™xH ­òto´[ù’æÌŽy˜-¼2°w¾kš¦öÕ7»ÕP¿XêüãÆm[ðÇ[²/åIŸ[oíñ°Þ%®-ÉŽµ(›ûöüÚ šŸÙ~×Π´¥¶åUO¾ÓÜ«OoçØoɺ;»,Ïìüx»÷~ƒ$;ªéqûê<–7XÛgÛ­çÓsªóQ^|†Ó”·}+þ…={¿ço÷>xÙø #¿ï(cbqÂ|÷]¥j’r†³ø½ÚÊ+¯ŠƒÉÅ=úØ•ö;Úé×ðÛƒ÷\ÛwÍÎpëXÍEËRÏP^à›ûúá6zï78fÞ±8¹¨¦!í6ÛS’ŽÏµ±µgžŸô>©*¯ýÖ£Ó¤{é䆻êË3¹þ®HÈ !;€„ì²HÈ !;€„ì²HÈ !;€„ì²HÈ !;€„ì²HÈ !;€„ì²HüËŽù¿Å¿ú߯—+ªµ/Íâ=·ŸÜÕ\ØÝÍžÿ܇vÒE‘]8¤ó·½÷JM´¨žw¼hÉ…»ëƒÌËPÎ »O´~×kgtQ|Enû«ÅÌ.ßD?ÃÒªÑÕj7‹ËJo,&4n.#¾'žÚwUŸµFO®,]½«-`Ï•|qIÍvÝ µc¹[|Ev¡mÉÛƒ¶Û—Ûï\+ëM´fu]¸²Ç¬5A"k»ºÛ‚óùXëšåg•[.Ž¢íz2mö•éV>Ó\%iµ}›Ë×.òän‡ß¶ÃüãF÷û*Ëß•®…ÂÚ`>ÄdÖÐE‘=ZëNUs½½®SXþ¾£úÖíî ÂÚMrµÏ¶•Ë鯯uoñ³Ú’ŽU\ÝîVϯíd{ûçGfEvdñ0«nÿÍ÷)¥?¿VFxž97žº(² Û²ó0GÐešhÑŸy‡4…ÒCƒÿۆϕsxÿ¼@íòvrï‡IEND®B`‚‰PNG  IHDRir·õ pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R «vIDATxœíÔÁ À À°Òýw>f „dOWÖÌ|‡þÛÀ“¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(¼(6³»á‚tñIEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/ConcurrentStack.fig0000644000175000017500000001054411366547366026032 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1800 5625 6075 6750 6 1800 5625 3675 6750 6 3150 5775 3600 6225 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3450 5775 3600 5925 3450 6075 3600 6225 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3150 5775 3300 5925 3150 6075 3300 6225 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3300 5775 3450 5925 3300 6075 3450 6225 0.000 -1.000 -1.000 0.000 -6 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 1800 5625 3675 5625 3675 6750 1800 6750 1800 5625 4 0 0 50 -1 18 14 0.0000 4 210 1770 1875 6525 OOB thread pool\001 -6 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3900 5625 6075 5625 6075 6750 3900 6750 3900 5625 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 5550 5775 5700 5925 5550 6075 5700 6225 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 5250 5775 5400 5925 5250 6075 5400 6225 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 5400 5775 5550 5925 5400 6075 5550 6225 0.000 -1.000 -1.000 0.000 4 0 0 50 -1 18 14 0.0000 4 210 2085 3975 6525 Regular thread pool\001 -6 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 600 5325 7275 5325 7275 8325 600 8325 600 5325 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 600 4125 7275 4125 7275 4950 600 4950 600 4125 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 600 2925 7275 2925 7275 3750 600 3750 600 2925 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 600 1650 7275 1650 7275 2475 600 2475 600 1650 2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 375 8925 10200 8925 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 1800 8925 1800 8400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3750 8925 3750 8400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 6075 8925 6075 8400 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 1875 7275 2100 6825 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 1875 7350 4200 6825 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5925 7275 5925 6825 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5925 7275 3300 6900 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3675 7350 5625 6825 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 3675 7350 2925 6900 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 7275 1200 7275 375 600 375 600 1200 7275 1200 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5700 5550 5700 2100 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5550 5550 5550 2100 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5325 5550 5325 2100 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5175 5550 5175 2100 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 4950 5550 4950 2100 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2850 5550 2850 900 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2700 5550 2700 900 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 2550 5550 2550 900 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 5325 1950 5325 900 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 1 1 1.00 60.00 120.00 6600 900 6600 7875 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 3600 7425 3750 7575 3600 7725 3750 7875 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 5737 7462 5887 7612 5737 7762 5887 7912 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 5925 7425 6075 7575 5925 7725 6075 7875 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 6075 7425 6225 7575 6075 7725 6225 7875 0.000 -1.000 -1.000 0.000 3 2 0 1 0 7 50 -1 -1 0.000 0 0 0 4 1500 7425 1650 7575 1500 7725 1650 7875 0.000 -1.000 -1.000 0.000 4 0 0 50 -1 18 14 0.0000 4 165 1695 3075 8175 Unicast receiver\001 4 0 0 50 -1 18 14 0.0000 4 165 1530 975 8175 Mcast receiver\001 4 0 0 50 -1 18 14 0.0000 4 165 1770 5250 8175 ConnectionTable\001 4 0 0 50 -1 18 14 0.0000 4 210 1020 7575 6750 Transport\001 4 0 0 50 -1 18 14 0.0000 4 210 885 7575 7005 protocol\001 4 0 0 50 -1 18 14 0.0000 4 210 2265 7575 7260 (UDP, TCP, TCP_NIO)\001 4 0 0 50 -1 18 14 0.0000 4 165 855 7875 8775 Network\001 4 0 0 50 -1 18 14 0.0000 4 210 420 2925 3975 up()\001 4 0 0 50 -1 18 14 0.0000 4 210 705 6750 3975 down()\001 4 0 0 50 -1 18 14 0.0000 4 165 870 7650 900 Channel\001 4 0 0 50 -1 18 14 0.0000 4 210 900 2325 825 receive()\001 4 0 0 50 -1 18 14 0.0000 4 210 660 6300 825 send()\001 libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/DrawScreenshot.png0000644000175000017500000007761011366547366025703 0ustar twernertwerner‰PNG  IHDR ¢ â8iCCPICC Profilexœ•’ËkQ‡¿›`£ãƒ»)Œ _ʘ"í2mµvˆC1Én23M¢™É8™Ä碠®\‚"]t+Š«"]ˆâB_øOHA„RÇÅ8‰ ¥xàÂwçu÷Bô›æ8`Ùž[žŸ“ªµºûDŒ}Œ1Î~Mï9³ªª°£ýøˆø0©9NGš:ugNŒ}»ulã¾oï\@­Öê Ò@²pH6®ÉkžãhI½¥ ni·RÎXÍ€_‰FÀ¯Ä@oz >²m´mˆìf ³§C$ ,=Ý‚È*ðÕ²ºDï'uÇõ ºLVku)X¹;3¿ zoÓrðLÀø_±ã‹pxž‹Ql3…DêMoy*€ˆ»°ç§ïoN@ì%l»¾¿õÐ÷·Ÿ@ô ¬_Õûîà^B¼ƒÝüàÎï½vâ@dx¼•PÖáÁ œ8‡n T²D¦¦Âh@<_P)#ËÓEu—'ÿ³:ýpÎ Þö*@ ØXv‹åíFéBÈf¯°4ÌiBv!óCÄÿf¶-©‚™DÔÖV•É8O¢ê1ÛÏ @Tí[3c2»¥­Œª²ç!êKö½âÞb_8~BºGÙ¶`oÚÊ^c¿š­|Ô“÷1&VUÉ & "ö¼ðH>":Ä5Û¿U…Šz‚½%þ‚m˜&ufŸIÿ&""‰?G7ÕÖq;´â¾*[R6…mŠê5*ìõ”A=±·› ð|ß~kf2ƶ$!ÓÔvŒ¸;Fƒ€½ÅóØ±¿ø¾0ñgŠ«?<îáƒR…3ˆ¨…ƒ²ÆI*&šíaÏ„gELŠQUfR‰ªØÙPÏó2ÞEňíÝñªªˆþ´Ç™ÈAX+@=Ëc<σ*‡S¾íDj‘muÛãT ß‹fýèá*ª*3Äd;“…1&‘Hxžg‰  f¶.˜}b6KßjŸÿvÆhô„jø\†˜øõ³¢é©Øöo„]”8’v•âq!âÐc)j§çòÂZaû‰DH“MŒ#ÑÌdÈD"â1Æ€ÂɈ¡¸© $ž$ì8+Q_ ‡Ý°C‡rFL(Á4ÝJ![bb1ÑôÉGÑhdhç~«dÀȈ0”<¶0…#âÆÏÔ$b)8³i‰^ "iU†Â1:þˆ–±¨žG"Ôˆ¥%Œ„YRÌk+¾”£É[D0ÆØÁ[¡†ªØ¾J¢JrƘÀ΀ª¢D3},ªÇffÛí,ÎÌÆUõ”‹¬K€`ÄNFŒU¦Å0 ÆÎ"*}Ïòˆ˜TT£¢Ä¤Fl‡#&½u8gc/f‰H%PUáœB@Ë Äâ6Zü& [âÛ™íÚQØÈVyµSCËUb—"™=ÛøvHËUªvÁ<-,Fó\\xE=*΋Õ5Z‰ùõj5¿ÆWÆËƒñÏT5(cé»…ü׿ö̲¬B©ˆ×¦¢RÂæµ ’((ö Š%†@œ°ŸK Äée@ü°5%ìlj‡ ˆÕ¶š›¶™æF“j$"õˆ¡*VV„eÆÒ4izÙ=s¶hÑ2dC{ÇsF¸¼@Êæé®¬V˜¥p‘˜aD#Å),ųçy0"árDÔ{ãY0*DÅcbbûpÒ#Š8Ê­OØ× —ø‡ýÀ"1mâ„ð“g(™½á,&"†oMJD`@(C~Ìü s¦Æ`¾VG¬2Äž'Æ„‹é"qý­VV.jØ´øiÇh!ÎÖÁ.£[YŸƒ=VIwýhc(lÄxˆTàè-ˆ¢¡$,$| H¤"‡]\ãÞŽ;i²Ù%ÍL–Ú³~~a"¿(¯¨LD”ÀVË E×iJ]_S#F’õ[·nÜgfì·ïäÝ«Ç(­¨¨€ƒCnbË–-k76¼ù·K–½³lõ{¥•\ZÉì‘*‘œÉ®¥ >ýÕ›TŲNÄ„Ò;j¬K5ÖN¸Óg>=qJÍÎãÇ”6l€ßÌÁ¡»Ø¼yóš uËV¯}þµ·—­þ0¿¤"¯¸ŒÀ °ç[ùˆÙC´Ê‚Ʈ٨˜Æ­+òõäÏìwò¡S òvk ßÉÁ¡G "Ï󚚃‡Ÿ{ãg^¯3‰¢òäùÌžFâ—çyJþÇoDŒ]Ó1ÌžH¬ßRU³9à ÇPçg®»98ä(¬’‘L<óê½O½R›¤Â²áìyWCýËê]ìyj1FLjÚVÂɯž4ãÈi“$B¼.äà‹°º9GxòŸ+<úRŠò‹ÊÙ÷­Øöof?Zk ×aÅArÛðb|mö¡ÇÏœ""ÍÍÍ11ô½º˜óçß¿¬¸ð¶‡ž«o¨Ë/)±k}ây>{žA´†¯iªÛü¥“9rÚ¤Œ î´4‡Ü@¼J|ħ'l©k¸í‘—ù…ì‘Ç>‡Fó$߈D¶'‚1AsCíçO<ôèƒ÷r3„Ã`BlJÇ̇í¿çÆ-[ÿðð‹~~!û>ìù¾Ý³‡Ç¢"˜ YMrj͸gNéD‡xï½÷–.]ºzõêuëÖ;vìn»í¶Ï>ûì²Ë.]©ØÀÞî0”ožѱïõòÊ5«7&A”ÈóŒµ`²ºŸ:ó×á6±ŠŠÍ ûŽ/ûÉù§æ':ZnZºtéc=öüóÏ/_¾Àĉ>øà£Ž:jŸ}öéJÍ^ýõ'ü•çž÷­åv™T³ÿ!‡qä‘S¦Léâí?þx«ÒìòívªhjN]ùû¿®Xߘ_XBÌ̈‰Ùºk14 š›¶Î=rß/±GóijÏ>»lÙ²²²²êêêòòrµµµëÖ­«­­:zí¾yRÆ5uÅÕ£ŠmßNÖ¬­7®ª§õèñÔ½Ï,]øÌò‚’ ?¯ìy^‚˜ŒªO°~TÖ>[Ô$§í=¾¨ ¯£º;î¸ãîE‹F–W9}úWfÏVàÃuë^~åÕ¿?óÌfÏž;wnçúã‚/\8¶|ØqÓ§W6›€ k×-{õÕë/>jΜ/qFç·/X°àOúSeeåôéÓgŸ6›k?X÷Úk¯=õÔS§vÚÛ»}»hª¯M~qy±ßúx}2@Ûãm4~Aë«ìcóóó Zž šê듟_^\ÐI•ºZt÷ÑÑk÷ jß]ºaؤ=Ê}´}» vé[›÷Ùg×>­€µ5,ÌÏÛâ.o­}M´ˆA¢bw'DR5ªˆA²q¸LK–,™4iÒĉ«ªª 466î´ÓNË—/_²d MŸ>½£š ìí!’–¾²låŠO¾øúìK¯;l\~x<µá©?þߢ×@õ—œÿ¹]K@튧nüÕ¢µáES¾|É—Úµ´íS6¼ûú«¯¯xóÑ1ûšoVŸÚþòûÛŸ\iÿšöÅËΜ1ÖÖcécwÝtï‹öø_¼ès3vo5¶v­èÔ+žËŠújÀÚµøòe46¿Íe!Ö½ðçŸÞþÁ9?üæÔQvj’w_øË5·? ¨ž}Áy‡ÕT@jÝŸ¯ùýŠè®µkk.ûÅ©c×ðÊýÿwË£+`Â1—wr»µHmxå’+n9åÒk¦­y±·3o¿êªšs®8ujN±¥ïÕ•©ÆúD~‘G~³ÇäÈ'ëÓdµ¨¤šjv­ ‚@#÷·L,¸ãŽßüêWG2ý裞°ß~ÆWó–§ì9áñ¿ýí¦_üBTç~éKÕæÎ þ:ÿׇpÐôãÚiê~¥•ê6oÚwâ„g~ì¯ÿ{TOïx¦Y°`ÁÍóož6cÚQǵïÞûUVŽ ÈÆÍ›÷ÜgÂ<>ÿ†_«êv'*Ô¿ûØCOüãÙçn¹çÑy¬8{ŸâðxòÝ[/ùÚå÷¼“Îyà®LîXÿ[Ϙsù[áEÇ\÷ÀÏN›:¼íSkß}åÑG}vñÿÞƒyËþtvy|"Xÿ§ŸœÑ-ÏÙ¿>Õã¿:c¢­Çc¿þ¯³~z=~ÎU÷üàŒi­Fé®|èŠ/ŸûÍ“o½…ë¿÷´‰Åm. ±üOWyÑ›7?uû {Ø>¼ò§ŸœtÑ-€IóþþìéÕ\~ʼnçÿ#ºë­·¦?¾êЉù×Qöªî»Mšqîe,+Þzw;o—z÷;Ç7ýæg¯8a׎^¡W`-ë÷¨$› ŠUŒa³úO Äˆš”¨ŒQš8 †]ð?~üøñ㇠–ŸÆ ?~|SSÓ믿>~üøwÞ¹mööMë—ß¼àn„Ö/äû¶¦^^8ïî—v:ý‚NðWÿîú;®ý¯’«o:¡hÚ´†=ý¢™ ßøúÛ\{ïóÏÝFZÞ²ü÷­Ø DûåçEšž¿é²¯âÐ3.˜5±bãÛËÖ‡'7½þ—›ï{鸯^tÐø¼×¹úž»þÔ´ ÷(jñÌ®4­û7•ø© #S©Ô”I¨*É(¿%R½tå‚ÅDØfÔ^S»â¡k,žzê·N™Rô¯;¯½û†Vþø†ý«Pl^·Žœ5udQ*•š4eTI¾ïsgŽ¿ø·þíßqÑÑ#×ýøú…Wþ¦êº gµ®Â¦çÝJ‡ž{äø²ÿÞÞÛŽÿú9‡^që¼IWΟRÙ‡”íÞ; / & S9¾Z§UÆVAЖK—.}ð®…‡Tït`Õ¨jc Ö®k^¿^¼ÀŒ Ì´ªQõ;{è®…SöÙgï½÷n[¥K—>sûï2ú€ÉUÕU(ÃzÞö  e$ÕU8pòHýdÔ’ÛïÜkŸ}Ú]¿Zºté]wÝ5fï1#÷IcQ›ø°©ñÍ~ÊÛ #ö¯»yôwÞ¹O·Ç¨ÿ‰³.º<üCSÑ;&þáŒËï™4ïöÇIüó‚Óÿë¤}Ëÿ¹æ;£€M«_ÃÜywÍ=¤lëë¿<ýâ‹N*žúÎ/÷hÓëÞ{â.úés““祮þOç~êâG1÷n?gÆè^\üÖ°ðäÚÇ®9ë§÷üçÏï:uÿ¢Çn8ùÊK¯=îÄ…ÓÊ[<³kE§6½ù†}þøƒÇ'“ÉYÓ1¶ÄkW|ûÞ#/ºÀG ªyÖÿ㆓.ºå˜ïÿöңʼìÔËç8úïo¿k>´yÍ[oáóç?¾<™LNŸ5¾$ô¯è°¸¶Ö^?ã\̽ù¬½ËÞYÐÞÛåí=ÿ†¹‡Ÿ;ãÞ9ªºµ«Œ,M„NéQðˆ<ò¬G`¤šUŒõ“hûF«W¯.**ª®®N$Éd2–«T5‚D"1vìØºººU«Vµ;Rìí1 ªŸ?ÿpóñóç_q[úhêÃÿŽšÏÏ=|r5P}Ιï_qÛ+?:ú€Ñ‰ê™gý ¼¨úø9/¼ºpíÇ[R£+[¯íü™ÿ¼éðÄGÏßtÅ;éÍÛ^ÅNÇž?wæd£«ª'Çç¼"»M­]€‘ÇyÏ’Û6lnÚ£¨…¢ÝÅ¢Ô|úèfVwòÖ€M]q«ýOco.~8ö´£§T'œsþ+—Üðô+ìôxÀgŽ>efu׊“ K¾ŠÏüâÌšÔü÷™ë~|ÛË7Mß¿åX¿éå'þ œyØ^‰Žßnô~‡ˆ¿Ïäùs§ôÝDa»wEE…‘ 0§š=ö âÁGº}@E @eee­Tl»Ñ±ä…*6lÜgLuÑë¯m[ÿ~sE#ô0¶Ô}¼y²ò– —¼ðÂäÉ“[Ý àÕ^QÿÉÔýÆUzK½õ+“Ûò¬K§Š¡úÔ0?9y'l]·åÕ%KÚf^xá…­þ–²š‚ ¯­Ü²b]C³O„ÀÛ’ š½­Û¸eIt{¦d& '|uõê³Ì»‹ö<âbl½\ùç;pð÷¯:ë35@Í?㈋þÜ¿Ï;eü _ºþÑð95ß¾âîG¯xkõGM»Wç·zþÞ_¾íý³òß^tÎÿ—n»`íS?ŠIßúÃÿÌýŒªî¾KÍgbÏI¿ÀÔãÙ­HÏùÏŸ_yÏÅï®Ûv@YZWÕš¹¿x4ü+]ôncóZ¾—BqðÉç]𥚎mÚX{ýáçÛƒyÖ{ŸžþíÏo]qÞ‘ÕÀ·oüÃÃS¿òû‡ß8îûC±øÊyß›;1?zN+Ï÷í¹ãçÛâŠjÎÊlØØw4kþ¿ÿ~~æ¡yí¼Ý›¶aw?öÌÏáŽÿ¸áÉÕÿs”ßñwÜÑãí^SVVá„§ÖÖ”mŸcL*0©TÈðñÎ;ïŒ3¦¸¸˜™ãklkAày^IIÉèÑ£ßyç™3g¶­ÆÀÞÞ ÍÍIU5&°B`Œj^‘gÿ,­®VÕÕk>™:¢ÅêGÝÇËU±µ±9Ú¶9A‚æd½Š1áC±~Å2UuàžAÐlCGÄWoÛð®êØ­lv-]³üMÕ=GWúÑ}í ã¢•¤isoê•?ç–O¾|ݹÙ>½ê‘ß=¢cϹtÎò«®K†/!{s~ÅžªO.}oëãŠ#ªIÓÕâRÞ~\uʸÑö*««Uõß«ÖO)ËlÀÔªe«±û¨ÖoÚâí¨j¿Ãõ…'ßZ?{¯ª¾·³ˆ ”€0LˆÈW1ª¢bTDL€–AWb¨ê«O==©°xÔæ X‘Ú*[KÈ'AR·l+Ú\7R¨¦°èÅOËYgµºÀª§žÞk|ÑèªÍeþ¿>Š=/O4{ÉzI`Ì芚݋–,^Üêv‹gžºh¯B¿j£Éß¶¥‰”±ŸPU• !¨7…?º¤pbáÓÏ=uVt{Gb5³667aÔ6²Aé°|ûç¨É{xmź“wÛ%ó9[Ö<`SC³H¢õó9‘榺ð©€w^|Ày§"ÒÈT~k×,&nZ¾A§óù¿×ìRتå3Ÿ­šhù^"šÚ®ž'ùqÍ7Ö\³ì7³‹U¼ô«‹nÄÄ_?øã¿Ÿ8»)\m·ååçÙ‚K«n}jÙæs÷+Ÿ¾iF}­"óD ‹¸aÛ[ÍO¾úÌ­ÀÙMhý¦-6±Û gã/·>·fÞ¬]üÖíÐQûtåxG׈@ì ˆ=Ϙ@Å"P£’êè!ÍÍÍEEE »'9§R)«³WVV~ðÁYxûv ÐÔX3u›|RÛÔâšÔº7V˜6¾ªÃÕÿV0Éfo>³ð÷O>  zÊ1_:ýÄ]ËÀØOšqçOo¿æÒÛ«µ8å‚ëx+»ó¢ƒz¬\ôÓ_®˜6aòþŸ>`Ÿø’¢²SÅv¤mx÷©ë\9í«WL—zU0hŠßˆ¦1€‹~úóÓjö=ààOí56Ñiq’JØm×Ðc?Q:fðA«DªöàĽ+·óv¼ó¤cðä;õ)t¹¥» Õœ'"60… hãÛð>€@ ¤Mío¼¹óˆQ#<*©()¬VP^îçç Í©¦DAž§þ–Æm†—-{³Ý'$Þysâq#GŽÚTVä÷Іq^>©Išú…‰Íy~]í¶Ê—ÿº¼ÝÛÿýɲݦ9²qX• +-žð=¥$•×賿)ƒºŠ·ßXÖµa"ü_xqJض-\-Im\ àý¶¶xTrùãÿpÊÔ‹:¡â§š·5X|Û¿ýÛ?˜xÔ7ÿçÇî7ÊPsê…_ºì¸K>÷©K&ËqéÏNÎë¸â­Í›ñü¼c¿øÜ)vÂg?{ä.¡N¢eU_:*¿Â® Õ¾úûÙ×=Ê/ž:vrã€=ÃO5×!½Hä…n5žª¦Ræ{ês§rô)³OúL¤VtX\ Û½ \xÄØVçZ¿·÷ÌoâÖW65éÎî³ôTĆñô™ %öJA``cÎ‹Ø šmõ‰ÒÒÒÒÒÒ’’’‚‚ß÷3Gê š››“ÉdSSSIII»ëã{{+žGDžç‡û£¦L¥;î¾úÁò ÷-ÚtÇ ³¾»ñ£¤î?_rõbŒ;gÞÜ꼟ïyLœ¾Íó˜ˆ^Z\zÉO~V^ûæ‚ko»v)_yÓ)•@jkc½mâuaÓ¦­êlw¦Ø^Ñþþ_½hä–ú«_[x×ÍÝuà÷qÖÎüÉÇ}9\èhzÿÏ×ÞM}uîÁcüÔûÅDžg7Ê&~$ýû¡?ü¥â”G¾ùðõ‹‰¨Ø¶ÊØÏ_pA]rëŠo[0ñ‚™_ÿÅÜý :..jÏüðÕKJ*‰¶d4 4my{M=¼4ó`»oW<¼Šèon öعc‹—^YÛ×0zŸ0“Bý0œ¯ˆˆjWŸ0\´Ò׊Q#Ë*J‹*+ KÊ…ùD¤š“ŵ ‰ü­›>Ü\aÚ·¦­.#ªP>¢°¬b˜_:e’Èg¤ …[ò8¯axfÚ¯@‰TUb̈’²²¢òâ%Ååy~@)“ÚÖT”_(H46ŒÐ÷Ê;¸½%¢jQU «?s~0蘭U,8²bÝegÍGtÚ^l|õªi§þ¯ÿÛÆs‡%¨j¦>aÇÃ{[~דÏüøÙïŸþÝÏ=¦Ï¬úÕ@òãì=Ëà½w>h“Û}Bᄽy”Wú•eTZ¢a^"D0IlK0QoËÏ«-šÐþí{Ÿ\ÿQEiUåðò²ÂÊ’âÒ‚¼|€šM2¿Þ÷}bl-Èÿxân{wiž±ÿF—ÎùÕ?ÊwÃÿ^xƯ:ýÛç´ü¦ª«ŠíÙúÌ9ñ¢5ç-úí·÷éwò|‘ÌÅ,4K=€ëž¸àSã|ŒûÜ~ô·øÊú:S¼é÷_;㱚o?}ïÿøÕÿýÞì›~pzù˜\|hkJw¥è1“3 8üècÞuúW~ùÇÇ¿ñã“vϼ`ÙŸ®¼À}ß}ßw­aßc'Oyíª¯=µfÜÑß}ð¦ê_\û©¿Ãy?øÁgûÉ}uÒ,RˆÒ)3f8üè“üé)çýþöß;z¿âíצa3À¢Ñ¹ÎßNš›øÜÑblgY*bãù>£b¡jÍ:ÚêÇojj`Çh;X#£ˆ¨©©iøðáí®³ìí­À U%PÆÅÅSŽ˜3åˆ9Ö<{Ûýª{ìRêVÜø£[ÖLùâ5çÎ(‚A§ßÈ䩚(¦ªõÛƒaùà«Ö›×¬Q=±¦L”;ñ¼ÿ·ê;׬zCPÓRPØ¡¢Â’bUÝRÛØª†O>å›ßlÈ÷ø~ÚgoYô┿:kB…½lÔ^3ÏÙk&lxå[ªS¦î’×òþŠªaª0mv@2‹ó+Æí©ºjí¦`¯2µµªºÇ®-‰{êꃿÝÖ«î9º¢³ ›Þ‚Š„ÙKTý(Ö“¯*±ÎØZ–·ßú;M?xÛ}w—•TT––¢¬”Š ADMCˆê›R©êcn÷ å{O¯OÞ®ùcµ¤’ÊJ©¸„ ‹à‘65ÁIRë×–íý¹voŸ6aú› (ôw)-ª(++-).-,($¥¤4sª”¬—¦úw§í>½+ƒKÂKHxyé‹yÄÑgÏ;úìy–ÝuÉ/÷-"Øô¹'^´bÖ¼—æŸ^Öñ$jái1 ±Ð_\Q  ®¶Q¤€W<¨Siüxù à‚é£Y0zÊ…¿ùÓ+SN{eùZ™1¢Åãv¤hU•6llhuåγ¾{Ë®u… ¾_ûæÂ ~zÿ¬ ~~Îô±"ðž‡ùÆÃ¿ ï?ZóÌ:nj \=~'djI×NæÏUÏ<—¯­ Ouüv›?\8alëmŒ>Š R &ԇݱ7â1™0ÕP‡»âcÇŽ}ûí·íÚN^^ÚøÒŠóA455566î±ÇYx{©ÚU«?iøð«–½1¦qØÈñ»–2j×¼¹ÖTT•%¶®|æº;_¬>æ[“ËH½tϯV@ݲ—^Hš Ч8µ2µ/Üvåí/ž~éu3ÇåHnZó ë7bíúå+VUWYµ÷ÁÓðä·ßSùÕcªšWÞõûg1åË#ó ðà}Ïîöù½ªòÞ[òèJàˆÑe@æc¹£¢Së_øÎnßgö¥ç6u+þm** °õŸ÷Þ `ßIVüJ­zöþ'}ᨽò+Çíí HÉàÅÉûî7¶<@êÖ¼önóØÑøé½{®º˜qÒcHÃúåï6VUK}´tѯG‡‹bW´ë±'VÿêÞk«¾ìS]³Õ'N×Ê,¶¨j|5}yý‰5£¸Ã†Rï½ñ$pbYßoN ;ó°)…ð¡¡ ¥‘Õ9÷?âˆ<µxRCÓØÂR­¨à’bäÀcÊKqÂh}rÍûå•qDû ÁA‡/½ç‘ƒ¼‚â¼òaTZŒ¼|xDy¾ú$)ÝÜøÑ뛪&}î°vo?tÚáK}¸¾)Uè—V” +)(ÊC>ƒš‘ôʹ¹I›×ÊÇU3jÿö4’ÿë•5[ÿý €>½x÷ÚQã÷Û·ÒÃÇoþ}EjÔ.U–üñŒËï¯ùúofŽ`‘Ư÷\YÙÎûí5v§C?n»ü¿®ó³swi\rÅÅ 1ëÊ] EP4À¯~~çÔïÎܹà{o^œ9~¸ˆd<Öï¨èäê¿ìwÂ÷ÿþŸçy/À¬|á¥`äØlxðºËÌ:dg’ÿZxýýÛ¦ÿì™…£'MÔnSñpÿÌY³ö‘/"fÓ›O¼Ö¸ÇîcümoüìÔo§]ðÙ=DÄl]ýÜku»ÔŒiZ½øÊËŸÎ,GþŽŠë°a3P²ó¾5¸ù¡·ÏŸ¶›—ìèí€äë‹oÎÞ/Ó„H@Â*†=/ôÀ3jbmœvyžÇQ¾LŒ;vãÆ|ðÝLŽƒ™‹HÉdrݺuÆ 3fL»o2°·ÇH}²òún@D/ÝwëK¨ùÞ/.–‡úwŸ˜¿0t,›zò·¾r”<|ÛKºm©=Y³ûAŒôá{Däå…KðëWã“ïÝ1ÿÊûС—^xÂp¿õBÙ¤Y§ŽûÛ {í¨³¨èèíR½rë?è䋦ïÂ>S¯@5ôÐŽöAS¿x­ RF5) šÅ4ßsÍÙhÏ 0//oñCo»õ×Çqب‰5yÃ*ü’"›ÆÆæM[6¿ýöƒ{¢à«_?ü„ã›››Û–——÷ÔcÉê_~ô>c&Õä +ó J d‚†T]íÆ·ß~üÁ—e§ ;ꄎnÿëã=½þÆÃNÜ·f÷šaEe^!@õ¦©6¹yõ‡o?¶è_‡ ÿÖ±G¶{Œäê¿L=ñ²ŒÓîzé÷SŠñ悯~áÊÐtÖù7þô‡—@ýCß;à’Ðöú•¹â”Ëþtù½/Í™P `åÂóO™—a(>íò—~?§¨_½ø['~+|î´óï½ñŠ ùþ³?<öëñƒOûþïþëŒó‘ùXtTtrõƒSOüîá—ß}㜽€ú{pexÁ„óñ³o=qÍׇÕhõú?½÷¥ÏM(P¿rá§Ì‹îþÊ]¿½dJ¥ ¹ráÔôñÓ~ó«Kf„{Ê×aÃfÂ|ü쩇}½æš¯>aTÇo÷ÐÔ/9ÿwãÀ±è4 Û×ÉÙ³¯Zè%òýD>û ?¯€Øcf(‚ Ù47&ëk›ê6?ö?lwžàûþË/¿ÜØØXVV6bĈ’’"Ú¶mÛæÍ›kkkóòò¦N§e̶۷ij¨O¦RH”–ub{Ô×ni€WPZ^Ôrà”ºÚ:ù¥EÝ·•¦†ºd @~iy7“jjhJ¦\Z^šywSC]2%àDii‹jwZœ4ÔÖ5¥ååµà†—ïøïßüýü«çOn»¥ö¡ó¾÷À̯ß0wÿ^ýâø ~^\12¿tX¢°ÄË+H䀘¦ž~H ’2ÍÍ$Õ¤]{vG(++{ôþû?yúñ=K w™XS5j´—ð6¬[ÿþÊ•ÿ®«+Ÿ1ëØ“OÞºukg·?|ßæ5÷NØ ãkö9f )mødýšwþ½ü5-}ò1ǶóÛzä¾W>ºo§½“vÛ}ôˆQ>ûmødÅš•kÞLîUqÒIÇuvûö`¶ÖÖ'“ȯ¬ê]Ï4lø¸~qeUYË(³iÃ&äW–u‹Ê4lÝTŸP\YÕÇ$¶Ö×' ¼ÊªÒÃÖMõI/¿²²Eµ{XÜšÇ~tüE‹nzòÕéUíÞ¼á¦}ޏqöuÿüáQý¢dãkW-ôòò½D¾ç瑟ðý<öPÕºº:"ª¨¨PÕ±cÇŽ92•êЬ6nwÈȪ§Ü4é˜FµÖcý#÷ÿûSg´ÞÐï3|öÂ_”((©È/)Oäz‰”wtÍׯ¾ËKä{‰üD^'ò½D`T‚T²1ÕTßX·é±›ÐÅœq4ÿí^™…·;88áüŸUTå—Tä–& ŠyÄ^hàa¡"*²]1dÛ¶mõõõ±½¶•\ºÞ;öv‡LØ‘6u¹ŠŠ«fX—bGºVûáÀÞîàB•@é ó€oÓNØÌèÈCª=ôP«ØÛ,l°dk.ní €™°á2o½õÖvý'+ìD@Ñ\` ­Âô–-Ï<óÌÖÏÁ¡?!Æ'aŽS#ä„ eÈz‰(pá…ÆV§ƒ÷|ù¿­Ò`×Z‰YU}+( A ôâ‹/èª:8ôîœ}1`5 e‰YE™ J0³S"†ˆ”ö‚²öl#¶)Ô&ÉŽbÙ98 …ÓÁ&ÿ•L¯ {d ëèàЈ²„QÂlÄ0»Ü”Á'?9 !„®§ÌŠ0OQìÕ ;[¸m ‡¡‰D%DQˆ(ô"ŠÝV>á0ÔТÏ[Û¹v’å$m¡Fsë pr“ÃPkhõßóa#Û:KS‡¡…*˜8ŽN€ãsÜlá0´%ß&‘*ŒÄêppô‚ ö"bÂ#HQgúç0´šÄjlöjÓ0ØbGýìr°B°Â‘*+Ácá\:†ˆ˜ÙºÙ5Y»mgU ·í0!*D°é~Ã(¨Š„†€NËvrPµ±´ÔÚeÂINC °ÇD>‰ˆ€™iaj€«éàÐP«#êö”Þ®ˆS´8F8 %@¡ ð­Åì[“Xën¤Nvrb bXÃX»ÆdÍÄaÿœ]‡ÃЃM`ü©ªãÙPˆÜ<á0´ jÓ=Ö aÉ­Ä: M0qè^gaÚ-<`Ô#†8ÂahATQöO5Zzõ¼ÐüÉMC ¤vi)-'13‹*Q¤b±c…ÃPBVU­Û„…ˆÚ­<çjç0¤ "*ÊšüY »ÌÌØ½spR`f=ŠbÚØƒná)0oöœVG._´p@j2$Ù…ÇÊ„ˆø”‰‚x3Ïa@áXÑïP@csXÏcc\ŒØ¬@[.dw¼è+¨j߉HUmž"ÀÄ`viâŽý8ꟈz‰0 ªýSUÙme; =Øm:»¶¤"ˆÃ)#Zwrv€C ªé*i];^ƒuû…ŽÄ¦®_àÐ=‡ùìâج€Âàû.OÑÀÁ±b@ÐV]ˆ5 828 =dš†‡1Æ ±ñ#ä†Ó'†ì~]¨Uej‘σÂõ—ˆ·ˆ+.éäŽN„rB°쬠jãv ’¤†ˆHŒX_ ±Dðãý ¤·(† ˆ ¢@Ìž(Œ+/Q Ê¡û¸{Õˆ¶¿°û']ïVY ªJ…ŠˆO¤ªìy~Ô†I¨NÏî8Vä Tá{L*Ò"âù¾Š ‹IƧXôír¡Õ©N¨Ñ œ`_ ž¬“Ϭ€¯ "ë:rñz ÇŠ\ƒÚÄD ب6°öU%7Iô ,5b^ô…ØÔꙎM!š*â\Ù>”l\qT>Ñ÷p¬Èd„§Ì½9«gÃí×õ ˆM]ÜE)ªë]º]~9Ÿ¤Ž`-;ì2“ˆ¨ˆïù¾†QŸ”œ>Ñc8Vä(4¶F3- ¨e¶G‡~ÀÝ«FL¾ Ë.5Т°6NDdóÙ…E¡ûi¨_;e¢¿q÷ª“¯zb k1ÄAÖq(½îP(¹»¬DŦ.ž²PQQ‰Íšˆˆª’v²söNYÇŠ>„Fiºªñ&¶1ѪjCNx(´šìn6S¬CÄ\qèw8V ,¨¥&­i68:dÜòiŸÂš¿Ú…Xf&fŠÜ°ÁN»ÎV8Vô)ˆI5Œ[c' UPdé¡€Õ¶º‡î™öY8ái  ¢Å: ' ª0"ît;CÀîñ"çÐBËV0³ç±ˆŠ k ƲNlê„IºDìoFÐmÙe+úD¤*ŠMžì’“•§\„‚žã »Òù©‡èœAŽ_m¡"Ä¡a,3cTÕ,%ìEŽ=ƒ¥ÄÈö"._´°“ÍjÇŠ¶°:´B}ϳi¼T„™Œ‰ìbìÔh5Ut ®÷(LþÅw²ç"68•¢WàX‘+ "@UTDX V)`·îœKöÀaòUOÄŽÝ ƒ“™º ÕhÓ:t™ðm~; ³Ü¹Ib áXÑÿ°Ó@8Pæq S;:»0°j·Ãv@¤€@ªDÄáÆu¸Þä8Ñ'Ø%Z‡NF)`òØ žˆDÄz¢ºUØ>EWHaí;zWlÚîÙ! &k f@í2àô‰>„cE" ®˜­Ov˜›ÈíØõ-ºN ‡~‚(T|ö`„b^ÆÚkäWäXчp¬È6Ø 9¥Ñ´Á^ä`ä$§>†“`²dŒÀÌlSPÄg¢u'7Oô!+²±h$"Ö:‘9¬BeG6ÀídôÂØ—ªÌ'éb¢t´q‡,cE?ƒ­#Èc€H:™¼Ó°³Žýƒ0!6§ƒÅ«BŒ¸|,ÙÇŠþ )*v7;Now&.`_aÞì9V½îzñ»Wè:/œ‡]·AÌ¢’™ñ—¡ÊL‘øDn+»àX‘ jÝ版™°›!†$bm:t5˜Øf."/ÖaH" ØzÛeä¸J v´Ú¨Û¡¬D]ž:Ú ¼|ÑB'6u»?a•ëðˆ"Êgçôˆ¾D[VtݨéîU#ºb%Õ +ºXÐPE80s¼î¤Òö¼Cßb‡HÑ9œÃiO "QèÓv…ÛY‚ÝFE Ó:Æ+ZmáX±]hÆ´` v¿aÙ¥³z.?9‡ÓžÃêv;Êg±ÀZÈŠËÊÒ_p¬È0S:S‘ŠÄY(@äÖzwË.’ O}1B Ø‚™ÙîlÛ¤ÙÎÏ®/àX‘+#qf¢´ÒíÔ‰~Gƒ¡íA CaÃÕÀnTP/6ÌS‰MÎ.vàX1 &Ø ÷±ÐóÔþvyO†ˆY™ªLBH™@™ØºhGy'%†ˆˆ‰ãøN"»ÚYKò®àPD×ã+;á©w¡PÖÌó<"ò<ÖÂNäü'Ž ›ßÔ&zŒwìT!ª.`÷ÌœÃiO¡­#9©*«¢¥E‡cEo±"û¡ª¢éxq»0¥—³wê_ÄÖ¥l(ˆëz)Öæ=víb­Â¡ŸÉ §+ô?Â|ñÖ½.Ú»‘–æã½ƒ>›,q¾°û'Îî@°Ë°¢’ÇcL”›H-z޹‚8~¸-˜Ù¦&f·îÔŸhÕo]zÔB¸iµFáj¬ªF¾Ú²8ôÚ²ÂéÚý ‘p2ˆ6êTÔ`S¸ŠC]7ìä¬ó%ê*D!Uˆh` QŽã;EaÏœô4Àp¬èwPhÇûœr˜¬Å¥fÉ-L¾ê —²‡°1a™8•J!T,¢ý &vzDÎÁR¢CE A033#mãd Ÿ˜]F–^E·Õoˆ·¨}ß`™|"HµÀÅÜï%8Vä løáŒðÖëÔnq÷E¡­¾l[†vôEM² ½µ©æä§ž@¡JP<‚’ª®9‰H˜˜¢÷ v¬hŽYfR…ˆøDÌœ»FEvh{¢{S{Û»::’£¼UPÇ‹n‚ˆ@챪‚Èó}Rò­]G÷ü±+Ú…cENläÏóÂE'«Sq…¿+OqÊ_OÐE™©'¶ãÎp‡`ŒøD*Àˆ°†*6ÙøN]g…#F·ÑÅŒZ=™&+º …ÕµU™9Ó…ÂÊSYågçH·£pÛÚ; @ eŒ0312²žf+vŽ;R;#ˆQ €ˆŠhÍÄ]Gí7tÃvÜAí$ìúaDqØ|vÑz“v}Ëα¢ßàXÑ×°ö¯aß'¬SQÈfo@k7Ð˰ÝPµÝ¨ÕE„~uHG(PUn6ÙÅíè!+r "B¶~×qʉÈÒIc<‡>C¿­‘ºÕò® Žéþ¶Q÷í¹˜ .¨M_ñ"«ÀÌDdŒ(- a5d‰cD6¢ÛÁ /:‡¥€ˆ‰\‹lfàÖÛtŽÙÇŠ>‚ O`ã`fRPd¡/QŽZö\¨›¾@¬RXII­ÿ„Q "€e‘ªë­àüïúD &&`Œñü0HHW“Oä\¬ß~sWÊÚø1w¯ᆗva' ÏómFb&ßó‰`¥'Y»¶9áXÑ+rjóÃ#Zƒ‚u©H§+êóô§çi»ÌÍiw%DnCÎ|£W@€uÁ6&Ô'$ lÓ汎=„cE/"4îˆ\(ÂB8†M×ÑEU¯£^×öxîöÏý#6Å¥,»tÖ‘"þXƒ ©{6ž Ùÿ˜}D9€# ¨.=ű¢-+rV¿ÎÌS'íB†‰GW°Ý¶í7E¼Ýšt^z¬töŠÐÖE:dƒCh® ޽ Í쎑ÏLÆ(³'&Œ"Ûõ':VÄp¬ÈQD1õUE=ö¬K¶õ£Ç{i᩟—j{+¦rªÃì¨ð” ·6k‡«afÏóˆYT£¼§8aQ¯æXÑp¬è„k±LF Äó| HÃv±CúèŠM=Y•Í!î÷D‘±SG™ÈQéÆwôp¬:Q#Â€ŠŠŠ@5Þ·èä–ÝGçhû.YØ…Ü^·¡"­¶²}bßnÚe,ÂöÂ<áXÑÏp¬èÔ:`“Ç+TŒQ«€mÑ=£Ö`—]:ëòE wt8âª61Û0±ÖR<”™ì¹ %;ë|‹² Ýó\ëV¸Ý!ÌzJD"ƲADdU¼Øl†åEv'ë‹·CÔÈ·è70311FŒ55Æ„3F´Ü´ч8+ ÂÍ cB§P¹ˆ¨àÔ‰ÄÝ«FlWµí-±©ïŒ ³“Ýý‰” »EADÌìǬ`"qñvw¯1ùªí$Ê V +—Ôú6~²Š²nÜyžG67‹M‘í§n!ž*ÚRc ¬þºá ÜvŸr»Ì›ÆžÀØo5øqkeªv9ÇŠYFXõ!4 eæT…¿çȹ½ˆÁ‘iñîU#ºø –2¹Ë U×Zɦ´còU4ŒÁ öAí++rÄLÄD f&"bµj¶[wêE x?év„´etîò†´»D$"äy>{l³I‰§Nô&¼—8Vla|3Q…ŠˆÇ n97ôÒºÓá8ùª'ú.<ÏŽbGÙn‹}ófÏÉ9RÍaGlukÏ÷•ÈóQXé©·„'Ç ÇŠœEùë" „tPUczgÑ)ýв–tm+ÖÃ]±ž,ŠöÛ5oöœÌ]ýìßT»q-Ƴ/"ÖBÜr¤w•lÇŠ¶G+² vJ°qÅÓ9숙Ù÷½ ‰œóŸè¨Í³–)ˆ®ˆP½»†œý#DÄžÇà8bŠøª &’ÐØ)çÌ+ºÇж°^Œ°ŸÎ6a&kâ1h,ųVÅïž_[oYXôÃfjOâë H cȘ„ŸfŸÈîZ€‰º˜"'àXÑ.+Ú‚bMª"~”§È:…Ö±ƒ݈˜éÐëÈz—ïx…ÉFýƒO$ )®¹¨Pt ÇŠl@6³"œ "bãçùQ^ì0¬ô’il6£í×RÉZ™r` B€¨031)”ã¼§ñô0h´ìNàX!öÊ#޳Ï^˜8>6§Á¥Lt1G°Ÿô³H»¯œëî݃ab¨Mý ~ !" eö(#·OLê1 eæŽwÈÄPØø²¬ˆ–ÔT˜6°11e0ä>í¼ßöd¸Ð_0¢Z7^<ûçW»)!* ò<6ưÇq~lµ‰ã{+@æ@Á±¢¯‹Þ!d?+¢œaÀ};I(`Sz9¼g×Å…Môø3e5„™íö…EÛ¶²Ÿ ØdŽvc‚=6®¸ªH¤uÌñÜ‚cE_cP²ÂîU[g:b²öN{’˺õ€¨¿C*ÿîŽn æLô³Ðíšmœ'/²w¢¼§”³Þ§ÅŠÜ2ìé ºÁŠ>ªIŸÀæˆLí>¶]èšõzÝt x1D¦¨¾FlÕdg…ú@¸—&y¼è]÷à”¡&_õÄЙ¨úqp«OÛ8€åÇÎEÉ©>v~Á^ë¹Ø>èÓsuë0”i&î± e“¡_碪íXÑÃ' eV´2ßóìofâŒP¹‚îu¹® O=Ô³<‰YŽéÁ}bBèyÚ‚Ô7î"6ä)+zÇ Õ0O‘C¢žÏ ‚hz *ç(1ÐØ®T‘Ó„ƒXf ¡ÊÄ2"¬J õ­ÓiÆ5Ž;ÇŠœ…ˆȦV«_ˆÍSvOºYW„§^T=³CY±NC¡ÐØí€ˆØwqpê•üØý†²b»×ô.+²ÍȱŠFLšÆ2GƲÄìå®]l7ÐoÑbbônì·]×+ˆ˜ìj¾=&Ÿ`2&—扢ÿ³èônÀAÇŠÞ‚¨]w2Œ@jÄŠžaè°"ô™P`ݪçùbLhÕ‘#ÓÄŽv›,wÿìkô¿¡c."LÇâyBĞǖ *­ò>f/ºÁŠ>ªINÀíuwkÿ®;‰Ý­3vò°¾D9Áˆ>B6ôœ,t<ÜÈ´l‚Ý®XDĈDi'†0)+†(lŸgfö8Ö§™YD5ŠîuÚÜêi]ÏËÛudß³J»I¨ÂÆ·Ò“C²ßƒÂ±¢{p¬hñZ«u©cfYw"Ïcfo +Ø;èIçφnc×û"b¹åEç ­CgÑr“ª‘¨*Ç~v7¹ã‹rŽÁ±¢¬«D‹@áñô¾…CVÙþY^ô.5²õY›p‚ˆ­ädõißz 3d¨¯ÆÆÈ6Vؽ˜ââîU#ó#ØÏNU=bQ c=ŒrÅ.¶ä&½Cè-jt2å )™ÉBD¬½Sf^ßb‰NŽ]|N[ AV¨ÚTv«\È÷½T ”ƒIŠv=7|ê‡>³]¹mÙ¥³._´Ð齉—[me"â8×P@ϧË-ìkbt…ƒu.„.u1 b•š=ÖOß×È*Á¢{{¯_Í›=§·6ö²ª­:{ž¨ˆ 1á¾.âÜÀŽ «¾´cE/AEÔÒ¡Ïi¸3!F3îØÝÇ 92;[½Îvy1h|Wã‰AŒ‰ã„Al4´‡ç ~E{ «ÐS8Vô2wæ2e%'9år˸1;zQˆ’‚Äæ³‹œ)rY˜,·@DVOÆßóÈfì²IZœeGn"sªè-vt=_rî‚™ f_Q…ªˆøQà}Fè?ᤧœ„cE·›À²õŸ°))ÔÎäA¿î4Ћ²Ô ùa'Žë‚Š0bG8=ˆÓ²s½¨wï<‰"‹#Ô*ì ËŽ¬wÇ0äÎTˆvyѹõ`Gg1/2U…د(Ùª™éYZÁ±¢×j–]PÛåÓZ{ˆ½REœ>‘åØQbbU [:Y^øÌ¬BLdºf]A7X1x‡ø^‰G 0ˆ'£ÖûMfê#s‰xªó¿®x) Ê…Ùx["üÛÎÑ9{„>áXÑ Ž€âhNÌvz`Ïày^`†D\Ì#²G±Î†š ¾íж9ášaÅ-èïzõ/rš^™Áº…%Ø8€aR ɉp±]B.~µ¾¶½îÜ—¨×C«å Âè5žog 1 ퟢ‹>ÇŠöàXÑ.T•9t&JûšÆ:7söKNƒI˜ÍQäâ€Ó ¬YGìyªPŸ™%Èv&dÂ-¸8SA› ˜¡qÌ}"BÈð¼AZ¼]t…JY+CdŠV®j_¾ha'¢Þ€W¯ëˆãsˆ[s?µ~v‘½Ó _wr¬è- VxžF)PñȱÎwΣhÀ°£êµ½~ :Þ²Kg-à ‰P›üYXŸSmbî;ô?+D$*¤ ³bf!1ŠxëÂ@ÖK1²§žÙS“.‚ˆDŒªÚ|ÀÖ£ˆUC/#ÄqÐ"dù7ž7{Žýo +’F6l¶ïB7"b(ˆÔCPbBdó>l»„ínQ´ºÀÚ# âõۮ𢷲um7¶fŸ“%à(–~D™ˆØ*D”C!”+ZÁ±¢ç'4ʯQ’ÇÁ„n!ç$ô¸7ZOÕø¿ÞªU–# W RÈúÙAX£ü؃,nG·ç†œ£€y³çô¤Úm'××(—ÀÄ"žM¯Qسœ ŸÜQ£þ²bA.ÒÁ¢sRØS­¦“Ü}ÙÞ‚J˜Â:Ÿ†ñbŪ"Ìl³fg?+Ú…cÅŽ"S…ŽBwHd"Kªš[q;:{sHfêg­´Û/˜µ6/=A¸ÑÒ¦)ÜǶ–P’köNŽÝ€cEK¨•4ö› ²ŽÙé­‹!‰úܽN‡ŽŒ>vT`"ù66‡áÄ™ø–…C²ûØŽ­0DXaa'ƒ0g—Qz}Vt0lQtñS¶eÁ T7Û%ÝŽÚvqÄȹ$"cL"#v²ªúžç‰J ¦”,Ž™p¬èpdÖE¶Qef!›ñ1·õ‰ÜšìûN·îSò¡̆®AFxa ÷'l¼ØÜZtj¹õ +²¶ß‹ˆŸ°$!5F0(HÑmôó¬ß °Û-¢+Æ ]TµsPfFJ¶nCa¸3¢0^¬ gìí±¢] nVØ¥V«G{ì…{LÅíÈQ?»œ›Ý»A‡Îƒô)é^]„ºµßêtŽNŽ}ŠAÍ kßAUÌY‡ç¢ÿD·¹Ðê+gùÄo«×® ŸCÁ-L„úDëpã9Ç ‡žÃÆ$ðªÊ kûÄÌ ÎMu"ǰC]º-UûM„êz)Y>žtQQUcŒ'ÂŒ1¾ªR†‰xnª9ÇŠlBÜ,Š„ õ|ϘT”Ò.—„¨î‰M9dÐÑQÅzQ„j7àæΈÈFï3Û©úD$Pv·9Güì,+z^Äg…UÒAÅ dÃÌ6]1¢ÛŽ¥¹‚AïÛ” P‚Q1*žÇð˜|ßSò#g¢ÜËíXÑ났@Úg(4Wµ¡;ì[½® ·&‹¬›¶‹,Y˜ÍÝD:*Aè&¡Æ(y>EnØ9dïäXaáXÑsD¾uÎCDžObr`zRÈén–[ˆ§ ‰âýù@häŸÀú9Äp¬è¨‚9LˆÆtA†?vöoO źw·ÜP~´µÆ~qÒÓÿßÞ·ÆØu]ç­µö>çžû˜9©õ´(ZŽ,VË‘ÔDIê"@R;pR´…cÇœ¸H‹AÛ8§p’IZ£ ò#Ú¤ÚÔ’ÃFÛu$býh,›’-JM™zP¢4r†3ù³×êµ÷¾çÞ¹3çqïÜóý ïœ{î9ûµž{íµˆDåŽÒ>vIkŸ%U\D€™‘Ù=I·´cÞ‚}ƒ÷ÝñÆ5æŸß!ìÄêk{䀽rÌlÉZ¥BÌEF(_ìfPRE‰ÍÃM‚fL7ÆièåÄU©Mc¸ìWªÐ×ÀŽMD¨<<­ÐÈçw*š×W ´Û¯³¼yl쳇ñaæ¸/‡ˆ(6ÖwÔ«#N%U\Jªf!u:1 ’'fA­ôN%®1Óþ8,þ@ôljœs$ÂŽ…¬E¯=yûô;JªÐsvP8DäMjDb†}ævZ/"nl@‰> f×GBF DƉÓÁ‘N1>%U”؈À,¶OI 1âÞõ´ï°›ë¿/–}`˜ÉƦÿ6¶v‹¸Œ ‘„  5m Ô³{ÝÈAI%ÖGW7òjb¨ Â#pÔnh1ð´“^æ ÄÍyÜÇ`D`aÀ¬1Æå.½ÛëFŽ0JªMˆ " ÌαAÞW6õájóë 4ýÇ{eî P…€5èÉàt-µÅC¹?q (©b”ìœ1IÑÅT îØ£V*6C ™]ÏÔÞõ¿b[êÆïW úúD ’’LÖÌõÓU_›EJûzk(©bô!êq¢°G×¥õ°Ø[Äø£$K4Q³7ÌÔØ ©Þdˆ`ßmb—Ø<ÆvƒˆBÖ3p̱zÆËªnµ·--l¹^ýnbLÀæ Ž™‰³”®?0™|u¹I ›Ø¥=±%”T1êÐÒu Ì…BH¾¥ßiŒ1¦ÊŠ5’UÒ£Ó©Eyq¡ÙÊ]·îi^ʉñÆøÚPÈìäK Jô?•;vWÄHhK%6 LscƒŒµçš¹I­Ñsv€A‹*]OWFIû º%!Ìd©X°Ë‚”öÄFØ2- s`c¨<¥i2;Y©d•W—œ3¦šÄT¥ê´1;3»5Â!ªØë&ì6DÄ‹ _À-à>¿—b}Œ4=(ÊwW€VƧˆ°{ò´ŒwêÁ¶Ð¨¨M %œñ‘+õ„Î/®šZšÕê9˜„Å&†Ô²&Ä2òZl‹Ò4rT1VÅû]u›Ù¹%Ul€’*Ö@ÔïJâ89ìuÃ6…÷ÝñFiC—ØFh1GD@1Æ0³Ö0ö”±%U”Ø^t×ý XÜçO¯VyÚLÚã1j“b½ê}“ mA@aq€ÂÌHd‘"@rû%IAI ¤Š"T{*^afc ¹½jÓUb“ÊÓ&‰bƒ´Ç%ÆÌNÈ$Ê]žPê)ÄkSˆ1¿ÍÐb“y“+{`DӨdž—Ø<4¸IÏcû¨D̬uYFß»]Ø$éèfõ&U¢qpÛ’1"9  ¢5–ˆºùb¥P(»lZME’€MoTìû mÝÇÖ¸XvŽL‚H¬[u£²_·e“âªô¡oQB(Ñ"éÚÒº+a¼³iD( ¤ŠÛ ÄnÞé ÀGËî]³v×h(벿v?ípb“.Ùý&""ÿ&"KD @¾&ê~CI ¤ ðù@—>Åä >ØQÑŸv3Äcß@‰µ`Ý€(ֶݧ‘*}ZRE‰mGÌÛ!,ܰeJ›cBô»ÌÞËl‰˜#‘ìq+K”ØMxŸ+‘Ï+‚d€r-¼§Í»*”QãÛ‚+nݿݻw,ìœKÉ(²}u€‡?Þ)¢¤ŠmÁ˜SE,8!¢žŠ¹“GÅÂVì(Q”†õZìÇðqkŒ1&-""ŠÚÔ¨aGnIk±ÿ²ŠxS1·S_®§½j[‰½Â˜'Có6ƒ1ª9ù¸Ø‘8‰]bç0ÎÚ±ø °s„>ÌcT¶¯×b'LŠñÔ™Š¢âŠ6÷¾‘+ˆ¤’€™]ž›€efäQ (©b»0¦T¡1NênRë]îG5à5Åx®ÿE(-ë,1;=S4¢EI%¶ UXDíuCEiQÚØ%ÆÎ9t‚bŸ4J×S‰1„¯oªŸ™}º'%„µw­Û:¶¦ýŒhÊaÀ¾ÙºÓ±6ê4eæx btåÄÖLŠ’$¶Œýæ¡Áâ–5;&xç¬cábvO‰npg‰  .ÏMª…Éú:À1Ú^·r‹(©¢Ä–Ð=t­î&-ŠÝw$utQ®ömÁøäCÓ}lD/4äÉ*%à(Kˆ"JªØŒU€n^k! A$‘YÇK”èÊ7DÌLÆP¼aËb¯[X¢Än¢«©‰í5'¿9±Oô§׎MªØ*˜™EXMŸìʤâ%z1&åŒ4¯8‹×“Ôª@é-Š%®žbúdîÆ;Å«{ÛÊ%vjD+y "£åCÐSia—¸jŒºU᜚¢z’?X Ç—TQb ]ª5'€9äKQ¡á¿(Q¢€«2G‘.˜‚ë•‚…M"‚ úT)!JôcßS…­ƒ°þ™…™CÙ=nY‰ý¥‹Ñ! $D"ÔÄg‚ˆ–™½;¶ô8•ØDz‰M ÉÃûÖT›Êú%ú0 ûZ ‡êÈBÒ('ÛWô±D‰>ìoª@DÒ}l"R¿SL+ e\l‰q"êþ„ÒsŽ µXÔK[¢Ä˜@DØ¡nÙcPƒY|Ô%Æ €ˆ1{ØâöµžAÝÛ&–(±› Bô¹ÍD¼WVDˆ°ÜÇ.1~aVµ)w9:CÆP±K¹EQbÜ R¨áhÁPH‹åvv‰q³ˆjzq_è„}åìRN”7øU¯;„Ž}éS.“(—3Äkc …,Ä…<ã{Ô°%ö "…“CšjŸH- uÖR¢Ä¸Á&×g Š€ 3 ÔžJŒT:HüP(GÀ0¢¥ãK”Ø|ÞDf' VZ"b 8:T‘;iå¼ÜÊ[K“ª$Ԩ؊%kFfTÇ*¢‚Ä̺}í\øT¢ÄØ@XüQS_ÞÉúúŽ¡žÝðÛQ<,7óå–»Ür«7|b A51µŠiTL#³¥ÀV “A×?!hµìÑQ›Aæ )‡j²²]^îqëï.@„õØ©5^: ’ÍóüÞ… r'Ë­|~¹=¿ÜY\Í/­vVZy+çÜ ÝB±+–YÒ¨˜éZr°‘l¤Š½QQRŶC8Èïc`ˆéÒ‘fþæ7¿yêÔ©—^zéµ×^[XXØëõczzúðáÃG½ûî»ï¹çžk¬b0nýÝEˆžÇÔâ,¡žFzåùðR† ‰×.µÏ]\}m±õÆÂ¥ËËK­•K«Ë‹­ÕË{ݺ~TªµjcªRŸ¬5&f§'—šiΓ°5QQRÅ¡ÛFé&zr,ZG^@†ÿœ3?óÌ3_ùÊW^xá…7¿ùÍ?øƒ?xã7ÎÌÌè9©!sîâÅ‹çÎ;sæÌc=öµ¯}í8~üøŽ»[wÊ}KšfßB„Z-U…Ä+/¿ô꯿þúÒÂ…Ã&Ý:{döØÔdc¨&€™/-ŸãÂ+¯Í¿vö\sú@çða¦•¢¢¤Š‡† E̬ÑN>pˆ¥óã?þÄO=zôÃþðÔÔ”93[åŒÉÉÉ©©©ãÇ?øàƒ_þò—?÷¹ÏÍÍÍ=ôÐC›W)Æ­¿{‚b¼Ÿ^A>…’CÙç[¯.¬~çìËsçÏÍNVzèmטlTlj0±CÇ:“É×O/»ùõ —žzöÌËg¿Ój®Ü$"G¦6%*JªØ-øÅÏÌ(B!½ uËEäK_úÒ7¾ñûî»ï¾ûî3Æ8çœsºH†jص=ºh'&&~è‡~èäɓ߸Æ7Œ1ïz×»6/'ƪ¿{ŸYßoÓyØðÝprZ9Ï/wÎ]\=ó¹ù×^yÓ ÞqÏYb˜ò<ªöëvhJ| JS×O™yëÿûæé3¯¾"€|ˉ¡Ä½’APRÅ.@­hf¶qÓ1îK° <Ž`æçž{îÔ©SwÜqÇñãÇEÄ9§^õ/_éxþ»v¤róm. W›MȲ~¶eDW†nb?~|eeåÔ©S³³³ÇŽ»¢é¹µþæM´ÙÞxJ®±¿{Q (Ö¯³ˆÈ"1r|óæN.wÎ/¬ž}õÂÂÅ Gfê÷»¹j ª¨Z¼÷ØÍ+ÍöÜÅ gÓjf)Khãð§’*v Ì`TÆÖúØÃ%#D$Ïó'Ÿ|²R©èðªÐ=²‘'y¡òñ6>q"þÍŸüûÅGnã•S·<œüõ ÞZßæ[å£-‰Ž;váÂ…“'OÞ~ûíI’l0Ô[ëoóT㦇+¿ûw?p÷n ûkìïÞBŠn*j¦!ReO8­œ.wæ–ZStwÝr}=5è«|÷Ìĺ¸üûŸ¾ûí·¿}âoŸ¼áÀÔÏ3/}«~à Ó'—6úéÖPÜbf®§æ®[®OÑÍ_\˜[j-\î´òu×î@ª¸b/«vÝ Óÿí[°ýý¹úú«4|ìØ±J¥ròäÉ<χv÷Kã8T=BBc©;ȆÌp¶\Dž~úé×_ýðáÃišÆ%ѳý¸.ìÇßÔøÄ ùÝO.}çìÅ¿ÿ|ó'~ê{'^ع¯¾¥«Ò4=|øðÜÜÜÓO?½ñ8o­¿9´òèÐpýÝCˆh´ŸzÒk>¯¸cob aã—[ùÂJûÜÜBûòò‰lº–‚‘£ C0]KLdÕ•ss +íåõ×oI»2d<=HÏú ö¨iÁ9÷üóÏ×ëõF£éy“[A/®þ €ßøüÂiO7ø¶·¯üæçÛæ»oôÜöÆSõ¼çàììÁ÷üâäçýõ§>;ùž‡ÎÎ|øýS— ùÝÚGÞ3óñOÏÎÎ<µ|å–ÇFª£²ÑhÔëõ3gÎ8çv¨¿k±©®åÉï½ÿÀÇ?›êO¾û¹É÷|dâüàßn÷ˆ Á~ÖØhE°pÜǶ¼â¹“åf¾ÔÌ—/¯f¦j©EQª°(Sµ4³°|yu©™/7óõŽ}”T±kpŽÄ×ÃD꺧jn¾øâ‹SSSˆ¸¸¸èœS€²~ˆ ¯€û‡÷uo˜¹y~þâǺs×:[?þHöèõOþyëú?NÞý=“ç[Ï4ù™dò§š_ø‹Õw/Ù÷~°¾‚èšæÑô‰O˜ŸûXûpu½wh›snqq§¦¦^zé¥ ŒÎké/ø¼]›íZ³ˆŸølÑ~ñ7“5®úí¶÷woÁ̺‰íØi³A÷'C}Çakz+ç¥f¾´Úi7W§*T1²¼´X¯eI’hS} 22EªÐ+3÷/ÏÏ®ÌEª¨$ƒŸì|òÏùÑ÷VÞýÇ“OÎ_š=Õxäg’ýXó ß+õ›Õ÷~°þÂß-CÓòãY0_üÍäÄýÍêÙúk~{Ó¦û©âÅ_ÌóÜZ»îxí!PElˆï@ ѨÖ“vÌ<77÷Ö·¾U}’¯½öš1Æ£ëÄ#ëשּׁ¬L4ÍâüüÚ¯»0?ÿõ¿œhö—_¼«.7üýgµ¿úÒâiÿ‡_©ÞÿçCr×Ûo‚¯´/ÌÏÃB þÞŸ÷Íaí3‘W"b§ÓqD4==ýÄO«ðh©IDATð†ñ[ëïÊB ®,.ÌÏwâÅoo¶ksßûO*ðêß<3ÿÌ~ôiø•?õ—G×þö'î`Â_c÷ˆêÃc´ºç'`(ó;-·òË-·ÜÊ[Ë‹“dFš*2“õÊËób¿ê•QO%Uì¬E!@pœ£XòD¢)÷‡±ÙêÜ»téÒÄÄ„J_"²ÖêÖh¬°®TvÎc2mÕÅúýaã-?=wwB`“ìÙÇ €ù±·½)þhl‚ÉkßšüáOùK?‚I’€EwëM”$…*D写Ó4ít:Цc.]º5ŒmìobŒµI¯™Íwmúû–ª÷Ìä›:í¾Ïüýúm2¸Í[îïÞ‰Œˆ ±êQ&"FÂ’ñ¹“Vîr–N»Y¯fÕŠX;ÂTQSwØi7s–VîúJªØMT*I;"­/šâI0çá=‹-"Y–u:jµª[AØ­)CŠVÄÑ7ç•¿úJí~ ­Wžý?~íÌŸ|Èhžµxô­ÿ»óø³oÊ1_Î>ÿùôûߌgþOŸvú7ïSë¹ÿzÃþ)ZkÁ"°Ö®÷BU#Ôè´ÖcÚí6"®®®V*•=[믵YFÅÆm¾kÖ¶~úçåýÿqvþó®_¾xG†çýv½ž_K÷"‚ämk"2Æ‘!DÃΉ ‚ %[ƒ©!KhlÚÎÙŠ1#LØÎ[dK˜šuãbKªØ5d•”»DÓþéP;Ç@`È áQ;·‡ºtéÒÁƒõŠjq…ÀúšÓá°üo òÛᅫþàÒO=ŸûÛ‰÷ü¢´úÀ.²µw¼Õ¤Ÿùjõg¿_þú?ÏüâÊþù’5À×’‹ÏÌüç_%x³½˜ÛC†Pì("ÚÁ ×étòð_ÞÛ¶ë Ë-ƒ:~-ýÝk…ÌNÀì„ÈB¡TË¢Q±õÌÖª4¦–.·Ýl#MG˜*.·`éòR61]K¨V1Ê€§”T±«@¬TÒf[" èæ ³Áµ§Í[Ƙ#GŽÌÍÍ?~\ý{Æø¨­$IÀǤ¬gòÊ¿=½Dÿºñ[ÿrò·àÁŸmþÞÇV¦¬]1 ÌÞú£—þä¦ßÿ½á×?»ø¶)»ü}í»!{ä{®€÷ý4ÃOÿÅ5þâFå Ã1Z­–®gU)Œ1sssGމí߯þV2€Ïývõsáʃ¿ÎþáÍvío¾9uOëßÝýÖ-­¸ÕX€Ã²ýÝC ¢°CDfG„DärÖC¨~ë3îf*–4/wRÉ/_ÎÁ"AbG’*:9ç‹—ÛI¥ÖÈìDf+ë¤)©b×Ðn·ª)É5Ž#$>‹I †²Õ@D·ÜrËã?þŽw¼£Ñhx ×Z«k#ú'ãô^úW fi’*ÏN @Ó÷6šúùÇ>¶tþÌÅ&¦]Ã"@2soëKç;¯^ÄꌛÎà÷!“̺ø“ ÚdY¦9ªõßååå^xᡇÚø$æÖú›ÜÝZXh­y˜Ùd×›ÀG¿´øQ€ØÁµ¿Ý‰þn/ž{º_-"ÒhŽùäÓO?ýðë1§®dõþmæ9Sa @ƒã¢~ê'gnªû+õ©Á¿õ”j¨µÖ9—¦©sî«_ýêäää]wݵÁùäíêïvumÀo×àZú»8vìØ¦ïÕ0qñ¡"ŽÅJ1v¸l ‰,™iT®?8¹tiñì¹×ºsª’‚Ñ¢ 2ÉB»}öÜë6«90q`"›ÈóÜ’*®›§ Ç’çy­^oç-=#HÈ8Ä`Œ©ÕjoyË[¾öµ¯=zôÞ{ïÍó<îZiìÙ•÷ÐvÚ¼èÅÓæ=õÔS§OŸ¾ÿþû+ä{·þî!|ÝÓà$qPYÀaܨ¨Xš©§×Mf¯ONÌͽúÜ‹çÝ{§5PI»Én‡|•´ÚyîàôK/Í-,ϺþðTuº–¬çt‚’*vÔiçP“,«¬¶Ø±#C"€©e¯8ˆ˜eÙ=÷Ü3??âĉ4Mï»ï¾½nÔFÐͪ"NžSd ÎÔÓëgjK«3yëò³gÏ[kßþ–;-&C[I4®_¥ÕÁOŸ~滯NLNÝrdæú™ÚL=Ý å%Uì”*$giu:µz-Î ‹sM.0œ±±jtNLL<ðÀ'NœøÂ¾pþüùx`jêjÌÞ=Ââââ—¿üå§žzꦛnzàbü÷?·þî>Μ9óè£þÒ/ý 1èIrfpì¬s¬uZ4±Â°åíPd‰¹n*kç“ÂîÌ‹òäé—_»¸|ßñÛo˜RÅ|¥…Jˆ…ËWÞX<ùìóçßX¨7&n?zäÖë&¯›Ê²d#7NI;Hˆ(¼ƒd²Ì.¯´µŠ øS©{ÝØÁ ¢,Ë<øàƒžô¡A·z±·£sçDÐ"h‚@¿Q±—Í]Öàd5½é ˆ ¼p>{eaùüÿ=yhº~Óu3×:89Qª¡gæKK+¯ÎÍ¿üúŹ…6•©™C·™¹íÈÌMë“ÕŒ EI;‡"Uˆ²ƒV;¯Öê9ƒËÅ'æÂØŽ.±Z­ÎÎξóï¼í¶Ûž}öÙ³gÏ~ýë__^^^YYé»s Ky§¯Ço Z­NLL8pàÖ[o=~üøáÇF–e›—ÄãÖßÝA$¿.B!',³trKDŽsÐáËïa NdÉÑÙ†%¨U’W/Ôæ–çVš¯<óR~òÛVs¯Ø›V’¬ždµlbæÐtãú›Ö¯?PŸÈ’MZ?%Uìú©@X¤ÓÉk(µZº¸Ø6 v8”OªúU«Ukm–e‡ZZZZYYi6›yžk·âÜ¡¯Ð?›»p]OægY¦Ù-ëõz–eÖÚ«Ò!Æ­¿»€çž{îÏþìÏ”‚'} ¾cÒsvP0®‡ª‹H,MVÓôº©z–œÈÎOÕVÚ‹«yÛ‰ãá’pˆhRƒSU;]OL×f'«'²jz•±KªØv¬¥ "ê8—ç®R­%I§Ýtˆ (ÑÎf b’$ÖÚjµ:33£k£'ޱ@×kçnG¯GèJ0ÆèªP6´5n3nýÝ9<û쳟þô§#9øý üsÎ9×¶dÑ2³îc{šnª°ÆVlýàDvhª¶p¹sq¹µÚqކi•¨œ¨&f¦Q™®%•,1[+â]RÅva Uhª,Áv»SŸ0Õj–wšÂ’ç1iì°@ –ìu‹ ¸®qmŒ[wEr8}úô§>õ)õĪy­÷¸œQ“!K.¡'CØŸµH,%–²Äœàåéj³í:nè¢Sk* 5*¶bik"¢¤ŠkÇzT!H( –°í¤ÕêÔjY³Ùê´À1ÈÐ-­õ0^mĸõw1Ô‹ˆ†HDœAa²ˆˆ„ÄDrTZ4²!:F°£(©bËØ˜*T³°!Ól¶’4ͪY«¹HDÃn_—(qõ8uêÔg>ó™µäpúôé;ï¼S˜ @ž;ACH"B¢¡² Z! $‹û §N($NŸ> ÐݦF@̙ۼ1QK+†p£æ%F §OŸ^OH|êSŸB†<ç–(1*¸óÎ;5¢iƒÏIš›81–ˆ â}ÿôwAص›.oqÞæ¼õíÏbo:Q¢Ävà¹çžÛ|Àïÿà¯&•%©M³$I1©QŘZfW–WÞx}¾½r©½ºÐ¹¼Ô^]v­Ëy§ÉyǹÕgë$ºÕÂŲ}ÿÓ(ÿÉÿÝ oѾÀ®X_7¸ê:ö…* ¿‰ @l­h*PánB\ñ\xwB ts¬Çžtÿ-´ªg@D¼¡&þ;–Ã5o (Þ>è]¡‘¡1¡ËÅ®JxNáYáëÞ€= ]Ò¦BxE÷åþþØ3[Å»z__p½¥ž™„ìJ§£ïñƒQ@XÒw)4VŠqƒ¾ñk3…' ܹ”BI’,>¬M’Š©TÒj=kLÕ¦L8”6¦s¨[³ifM–," ;@$D"2Ç~äçóvSò6»6»Ž¸»\رë;a' ìvÇö£qaÄÁêŽmaÍ…ï{Wá9:™2hÙ¯]:ñÉÒûmϤõ·)ŒUwABaÇ¿*ûž€ˆ¿Eĸ\7x‚~5¨ÁÝÔÖqáÆ.ª¿0vÊ¿©{OÏÍà—WwŠQ¤Õ"‡Á8TžT¡äÞú“ýˆ¤gšõ.ñB‚žÏI—,)Klvd³ÚA$Ô¸¼î¶}¡O=Sïô±{d ’A"2–lŠÆkɦ6©˜$5IŤ™IR 2H¤TdÐ:‘œ¡V¯¤•ĵR›Ö„8“P»Â®#̈È"¾ÍF²+°»6X-x Ú¸^šÇ5¼TÂƒŠ¤žCÇßú;w.ÜV©ðAå¿ 'žŒ)0³øŠ@ÔÞÜ]5ÅkL wÙ6öX$Ïb»° \%.ôM…U¡³EvÞ•ržØ@€€“Îi‡Ä§ugȯE-·ežÞÓ¥ßn_ û„® õWD€ `²j“4¹Üì¤)#QWG(ò “áG¶ÛND`?,=ƒÃ*ж‡!Þ_\rýâ™4˜=­¤Õz¥6‘U똤˜ŰXÿ"õ;9Vܰ±(E@„• Qˆ„UN€ b~]I¡Ë£+“5­yY×aŠäß³î°wµaaÎ \‰ ‘®ë¢ZS¡qý"`Еț1¬NOKÝ×&­HâkD:† Ä^fßwO÷ba¶±Ðæ¾_QQ‘Ä0Ê=¢©ðãøÐ ÄO1š-}m‰£Ñ#ñŠÂ{ãCy½Rïr0Vú+êùêzðS5Œ‘ Y‹dÈX4–ŒE2ú'CÆ"‘:dÉ$h,‘É;—Ö+SSS®kÇŒIò´m;-aa@TiÑ•‚žáˆ&ÚDêʆž>F™¡wFk@@@@D Ë=Þùa™"w ËOçJâ+ÂhS¼«°ÖBÁ+Fÿ´® ïõUkŠŒ,|`Äž8I‰šM¢Ö_ï €uŽ^ÑX Úf#ÂEú@)˜C]:–8nþÎp=ªZÝú™dÒ á5ìEZ¡k$ÂÈ $€õª™®¥KͶMr‹†P%‚§Ã¨ëúaamÛšï_‘ÜŠtYP‘Xô!ñ³1F+žõ¯F"c¬IÓ´RMkõ¤Ö°•*1,,•Ôrî¬_[HHˆh€ “I@€ÃÜ BaAV“"òŽ^ÃÔ¯xÏz§<ÎA“Æu>t§a0*~.þ‚·EfÔÇ߉Wú™ÑÀSl›aXW¼m€åØ;;-ü ^> * cW@4.å)•‹@™€„]žÖ¯™-ÿtD*4 z‹N´šM;ÓÞÈšG˜=’&­,轺1ÆQ@Hõ!2D†È€IÈZòâÁ’±d-¢!£&µÒžæÜg²†;9LÌL­6›€&I*yÞL]‡;=’J†<)ê{)èv ¶Ð¯µCPÄþkÏÀõD¬ãŠ(€$Ìïé‹@)‘¨¶ç•ëpCà2š^›¥(˜>½:êš·€(ë$êr%fTù®wÅE±çñ-Aàù§y.ÜOˆ¨ï’ k½G#p m`f”îB÷3s® Ì~TÕ b6:øPD$½'Œ'° ×0[Qx 0£ÍỔʥ–£´Ó@DŒÖ2! å-  L W\‘BYü ‘$uHÄ·Cc!êAñÎF ò‰È$ Ù¤R­¥YÝTªí’µ6IEÍŸ³V!€d¬°±( "hD™!Æ\=Nâ³ò{(6 ƒÑê¡¡³%·Ðþ¾=Ñ*‰Ë%èÂT°Tú\·Qno(ÞÖUÇÖ¸ ~Þïs|ôÚvïì’Cöªé%iÁ›Üßl‰ú°>Db‰¿ oSÑÆË Ï-£Ó v¡ÇÔ÷œºË›»TÞfïÕ:ý¸é¼ES üØÿÃ"]]}ßõ¥žq„ñé‡Â(uÇRºk ";îŒô}œÙ¾ÙA(ˆ ˆˆ %$"cÈ&&IL’ƒHh ÃŽÅ&d¬µé°TÓtrú@î.JžY®‚t8wâi?îÞgB~‘ó•̉D¤Ë1Cá—>-©Ç‡HAI$òZaa¸P„)¤bóœQ•7D‰O ò5—”+vY[pŠ!²)wADWÐX™bW*èÍQTx­V+%‹7j½!ž{ê“}S÷ ë‹‚DG <õQ” ÅÙñ tì å¼^¿1Hz$AUÇŽÔߊ~{9˜_^m’`‹0ÃΉ—.^$û©AI¤7 €;›á««­ÜQÞ¨’AðâÊ;ŸDtmˆsìg((myÞ1Æè I#˜^<èWQòõêhŒaf]š™UNùÁV1V@ˆ %I’V’JÆbž£IŒµŒ(,,b‰ ‚8a"L@XŒe.MÈ.$ÁœX˜Á"yGà~R Îï¢jÐѧ];ºßÊÐÎÕR¯+—ï×ÚzTòp3Äçôp˜žWô2š° »#ÿŠW”ãwï/ªKeZüï ÂIºšÅ@+*zÐ 0!©‘ºEa”=Ë1¸/zw<¤»5æÅ Š?}ý-èÒóýÆúY)ppñ˜žþûǽWì÷ê~–©à¶Æ"¼êE-+¾´0‰ÞçM $2M‚DÆZ²  hŒM0Øz³îdxuÝ!¦ÆÔÄj3o5; ÃN”Š*+DÀ~¥Nn+à šR—‡/³ªÕžAcØ"£Ô†„š»…H"u‡:XŽ1¶g.z™²7A zG1X‡ˆ,"Î!‘Î…!Ò‹\{]ã¦@ìˆÈÎé µÒ¥@z‘¹<§P1>Ÿ}B³ð–èÐW!;F*ì²XBVý:zá é™$œH7fü¿(@„ιXû3š1¼…QÔug°RÁ[ÔÀ<´jFE0AVÍ¡§$¥ ‹óÜI¡ÞùRVŽÞd"â ¢<½ÇE|¡ÖÚÜ9vΘî”J×ú’1Æ ± ’16i¶-kõ[}”õÌŠŒc þ\68™I\.d„‹a Îâ)¢¾È#bg"µ-·[†Tºê¡ÿŠî¶ðœÂíáÙÝ•P÷J?kØpµ^VÖey½¶2µº‹vqtªï±]±¯O‹ªeh-@Áމ®U"¯(…¯DÄXF !Ž]øqºž1òº°ºÀŠ4G{•×í¼¥áWöÀ ºBlK¿c¡øØ¾{"£YûÛøŠž^cýÞŠ£ô€Ö’M¢ É¢1HFÙ†7AŒ¹ÜìLOOLLM­tÚ `ÒÔŠWö‚‰qGÄ3#QNÇÌÞbP‚³¨æ¤gŠˆ‘¹0éè Ä`W×·0€ ’ÔegL—±b°Òâ¼Ä'X"$rY«lîªù,@†b¸ÄP° <ߌ ccÔRÕt+hH¾ˆ~UHÅû1>V@„ˆäÇG—‹ !pàãqvßÂÕ`!/Ä1wŽ™M0 T@:ç*ƀъoÂ)™œÅ&pË¡‰$I_]X±•´2‘pî ª5âtý½Â êD*PZ4¿zl…ÎU )ì=·Ol¯æ!ÁŽªm`f@HÓ‘ŒµÎ19$ÎUˆˆZ¢VUt Bc@É3èF ù8*$@t¼AÌaµ'Ö º_ù”RÑ=ÆÃ)ÜâÕ[T1,]‹¾¢0h}^& >‹¸Ê{]]a‚!À#v$>D_ ]=7xÿP€…½•=È÷ŠQo*pê(FŠmÀŒƒ$ñ+¾ðp ôÃ%dMxe‹A/ŠË.P”c`íôî)*}^VA¿œ–‚¢dªtÛ ¾ÂX¾åûë·s{$’"—‡Ä£¢ûB©"ˆ[BoL !$26E" "› š($Ð"ÕžÈëªJ?Ââ¬ét`êÀTÛåBˆ/' ãZtÅ1Œ{Ô&é!òx½¨DKp¬« =T1ü d‰‹0ŽŒˆê†Ð—mœˆ 02¦( ŠbU•9ŽítTž°s™~{ª¨“Hðiˆ–k—ÀèÁó^¡uT@2tD/ÂODĨ áQ¯n4wˆ»Çê® ?ô$ÆP@È"NØ×®Ž-'Ñ]5Dô.ϽŸPü‘2J ÙO€Õ·¨:@$Æ¢Ü|¨Ú¨Ô^¾¸$Y½"ÌišA`åúsÕŒ1Î1"«Â Ý «" ¾¸èºôü¼_‰L„Ä œçè][½óÈì@mS`V[Wk@Љ#ɉ "m>«ÛÌ‚h€DËÙ #;4¨)$q®ƒˆÂì¢ç§O…ïùS=§á³§%ö×(ò=|#ö.’Š ªRƒT]a¡B}æÈà 2„gôî»Ó öÈCû´WÆr°~I@ OàÂoã P4gŠFx9g·oˆUR€0[གªÏJêyox’ˆQ1ö;È'ŽÁiŽ™0„&¨Â´SujEÆ jTu­\añîÝþ}ré:Áu" ^`í—ð É;I êÚŒ(ÚM¸ˆDQ¥9!!%kIãBìS¢±O‚DFÝÖ~—‘ÐØ¤çi½Ñ˜huV-á³ÊòˆHD ¯‡y3¦ ¶Ui“$~=Íʦ£µ€µV•>D!ã÷'âj”^ųˋ{_­< Eȉˆ¾·B¹2JDf϶Š*B„~•D“(\aÖíL¿}wŒ‡¦ðó8Ñì/@Dãµ""2Ú"u4YkcË‹úŠï—è(úÈ=h H‘ÛBQPCP6ˆÆöî%èî‹ØÞMrí8+†AŒC9z :3=ñúâ2Û4bèZ*!1:cT+¥$T·$¢<ÏјH¶Æä"ü€‡5¦wæyn‚QEˆbSpÎ%I\”º[äM+"0Æä¹³Ddˆ:yÎ(NGÂÛÿkŒgÓ±úIEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/ChannelStates.png0000644000175000017500000001150711366547366025475 0ustar twernertwerner‰PNG  IHDR…2šŽ[Ò pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R «YIDATxœíÝÑ–¢¼P®XœÓi·®*ž¼E¸þW17&¸øÇó»jˆ¿æýh~ÖïD$ɣÞ‘ ÅzÆ/ß>ûegu%-ÂÏ÷O±Uqd©ƒ‘7¦g1¯«=ÃíD{óЉ0Ó†U—«&IW»7óãvc—%Ý›;WÛ÷í•´Hdü¦ï±ÒVßÑÚ|6¾yWÉ^Ù׿Åøz9˜z'îYýQÞo åç×pð; RØS„¹÷ÙÏK“f6¤;g”rÙÞF6ò>ÍÔÁ¨±ÍéDc{mcsŒ×¶4VdÔUÊ£¾˜H¢º"”Goµz^I‰ôV„òh]«u@"¡<ꑉêV‹P­èð¼D4}¡<ꔉê–E(J}ž—¥Û"”GýÒ"Q]Q„òˆBýG·}2qô\„ò¨k†lT7/ByD!þé¹O&ˆÎ‹PQÈ£Þ™B¢º©åÑ}G…ã R¸%ÿÀ Vo¥øhñyo?ÿúºé§;ßÙ²œ¢[ý¯w{î–c_ŒÙ÷<"(•þhúñ"P¦_‹ÿšn™~fÁ´¼ó»‡(–sî–‚[¯áòî>Å÷l<Ì2v>ÄÇ%zP ²õñÚ¶ùA¾:”{wçsr"VÄdô!–ÚùÈhùš…âÎ;bùç‡ìyPæ>îG¸ÛX„+y´1½ÚªíÒüëëÞõ)ÛSÝÛKÞfŒy9+þÕyƒÐùæÑù^x½^®‡:'º°=†íü´ÌµÎM˜ŒEØK½~Õ^‘‹µ·EDvw½y½?£wW“ŸXT1g¿ºÌW±½e¹œâRøJÃN¼±aûnëí¦bûÓsÉ^8N™ïÎâ2Ñå…£ÓãnWÏÇåÌ_§ßø¯A05mõ²ä=Å6œ­·ûŠíO'Û¢k¯ÁY^}>ÿáÄ+Š`x³ß<Õ.¢º¾,¶áH½][lÝ×Ò9´³Å_Ú_Bw[/óÙµLûlùÃ?¿†E¤.—³qg:1y Ç#c½ÝTl½ôG×ÚÞÍËÙ¾ñ–=—¤¿[λ%¿øí;Ó°eUœ(¶á`½ÝTl.<ù§ÏËpúÜê°úÜÓV¯ý£¡ ºÎ‹PQÈ£®õ9: ”yÊ£ÿè¼[&‚ž‹PQÈ£~¬Q]Q„ò¨Ôs·LÝ¡<ê”æˆê–E(Vt{v"Ž>‹PõHsDu«E(Öõyv"”‹PuGsDuïŠP½ÕáÙ‰hz+By´¥½jÐ¥ÓUÊ£Zªa”ÎôØ¡Ïcë…0Ê¥øøê6|,B5ºKöƒ9ûúwå]e߉{Ößxm—–fšFg_óüZ=)wâ>,é *éj÷ãÐg]'Ý›;W;å¶U”®–+|â“޹ɹI¢Šðí=smXYªa£Ößµý)¶« _NW7P„ë÷O±UÑį†=køñ;{¹ÃU/œµQ„åŸߤ°"¿{ =þ½gäÍiÆåOrä½v..åÑWž£Ž®Ò¼¦M-Ýáîgµ"ü÷‡Ñ¶$8ÕpáÙ2ò‰7‹'ý™"Œ²©U?zoZíÒ µž´6ŠP]¦JA<ó f¾? ’ÝÙ‹P]ìÉ‚¨Ò¥9ð‚ˆùlä-Byt‹[Ë4È1ÐmÓ”eÃ3¡<º×U»-ø1|õ¾T\>šn¡ö†ËT-nY¾¡õ-þw[]òÔ?O÷)~íܓσ<â!ó#|ÅíSL¬Êëõšî¿ ¦âÆb9Ë%ËhŒ×¨oL™av*.~XÞ~TFß,ª7OŽ^õGTV4M÷}Ë.ñé¨lûÛxŠ×¼N|:íh¹|9µ“ù#ôîu«å|öò–i@79<–ønîÜØ-{"‡DÓ^°; =!Æk@Î!8!à?âGÀ½)€(äðÁc“›òˆB˜?º#€ÌÝ‘GÀæúâ3›aGÀGæ€îÈ#`‹ï_z$€-O~2—<ŠÂKl €-æ€É#`‹ù£N™B¢sòØbþè‘<¶˜?ê—)$z&€·>;Ê# y¼õääÑ €8äQ8¦´ âù:”G@òX÷ðäÑ b2d£OòXQåŒ(‚Ò"Ñ!y¬x~òhG@ò(.C6j©UxòˆâUe”È~¯—}D/ôG@òø§î”¥±@†ltBD!ðÂ?Ϩ^fòˆBUŸ¦”G9²Ñy”†Hâ>AJK}ëË9þyj Ž>ëJ…°³÷Ñ"qÈþ‚©>s4úS{ò™vp± ç;~ü¯âžË?œ_è8–N²àyêjGGû~ªƒyÌoŸÿ:¬ÐéúÈU^ìT±®ŠeÖe¼vØü̳}û²€¦;vQ¨UW???AÂhÐÝgy.š_Ô"õ쾺Š@t^QÓ¯ÅKóóÛt.’&¼Ós]鎙·Äó}?Þ¾=ÑxáIL‹Ô˜*u°„­Po¦š8Që‰ ¾©«ŠŒ×€(2e'K¹Î~ê5þ9óGЀI42^ËÍ¥L´D¥'’8$rµÈ#èKØÁÚ Ú E¢ ò¨"‰m)ÊCµC$‘«WZãŠ$òÒQÈ£ÖµQHTzû6µ‘‘þ¨Mº$2’GЦù'e¡«o™Q¹èZfÔF.ò¨q"©7‡iý| ÜHAÔ])È£^ˆ¤æ5°µñ}1p#2ýQ_tID&º#’ÓÒÞÔ½wÊÀ€ôGÒ%<ê—HJ­É}§iïqèz§KÊ¥íåÜÈ0è’ˆAÄ0üvImŸ{Sëd×ü©½D1öGy¥å›tCz–µø¸OüHZm–ë¼ónĽò¨b<’£ÕÆ”/߬Ø% yF̽p+yÄ[¥ysíÊ$ ¦~Ô/8"«{Š~&£µ!ÑÖçIòˆÏžo”ž?&{N8ä»<IÕ;²Zat\§€½î>Vãt(qÖ¤7òˆcn:ìX%ÁWWÄwùQ0ŒFaW¬UžnNº$•â7wD’˜{ÇóÂW¾ ”,‡e–õl€'š ÌSiçÑ›ë ÿ¾‹ß F©&n:ä>fM®0šìYíù}dÐQ)Ë‚È>¾#i ŸÖ|óŽ'Ž[¼K¥ìÇê»õ/Ú¢ÔÛX‘'Ž+o]X8V‹M¸ï¿J_„µ¬‘ú ¾fÂhÔØæáó³y‚£—=äœá{¢î ¸]«Í‘Hºœ<â^­†wGpžéZòˆiŽ8DÁW´H’GÜEsÄQò¾¥EºŠ<¢GÜÂ`ä\ÀíòˆBq=ƒ5ΑG@ò®a é{òˆBQÈ#.f2›Óä…<¢G@ò.ã%ÿ/É# yD!€(ä)]2Msù\k¯¾$€(þÔ^ØeêeŠdÞãŒÿUÜsù‡^ K‘À˜ S¾É2Ý>ÿuX ¦ñõxÁ–ñ»éœ)DŠþhyû2˜n]1.¤?¢)«½ÊBD2E¸L¿Ž?,g‹~~ ‹Ž‰hôG$0jÍ3e¼}{»È/£¶È\.Áõú¼ §Ï­¾–ñ×ÓƒpŽ<¢GpƒµKÈ#naÈÆ òˆBÁ· Ö®"¸‹!GÉ#øŠæèBòˆi‘8DÁyš£kÉ#î¥Eb?y'iŽ.'¸‰äOh/’4GwG<¤¥HF7‘GpŒ0º<â9 ´HÂèVòˆG5IÜGñ´¼‘¤9º›<¢‚Œ‘$Œà)¦š,Gøê·Kr‡A«âGRü5l‰çšÊ"wÂèažnBxä\¥æyƉ"Îñ¹ek[” €!@T_ÎÉ#© ’(yDPODœ¡bçìB›.›¼£Po]8'È#r¸*;æ×…+þhäÉ,ßh²]ÃÅý|dòˆô¶ß §Âù?µSµ×¹IIEND®B`‚‰PNG  IHDR…2šŽ[Ò pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R «IDATxœíÔ1 À0À¿çñà‰‚^Ý3³Îï€Ç€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# Â€ ?*ü¨ð# â–Ÿa™¿+IEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/Tunneling.png0000644000175000017500000000650311366547366024704 0ustar twernertwerner‰PNG  IHDRê½s¡$ pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R « œ(_lÒ&Zë¡£ÓåAœ Ãðx<@÷þ;{h‚óøÀ¦Ë:/[ÎãíŠkyÙb‡a8¾Iÿì÷Õãyx<ãWžsXó<ù”MºsëZ´iE{žüyXSß&æå»^yÎüøÏŒŠþÂ|Úà{°a˱Áéö¨„GÚ&&É6yqœùw\ÿò{æ±É½mÕræ_ÕÈFW6lϧر¿hb‰Y<’Œ}Þr }+Úóaû7;Ž'?Ëú8óÛü?‡P"zµ®å@›V´ç³:Hö:>¨ù™Ìù|AŒso>„":´¢å<_ÉÓ ¿YÝžÇð°ö¬›µ zB¸1Íû*\Ÿ @"(ò€DPä‰< Èy@‘$ò€"HäEÈŠ< ‘y@"(ò€DPä‰< Èy@‘$ò€"Häåg†³§áhÇãìIšÖgaüIgõ±ÃÕ ¬ÐaaÔ_@"(ò€DPä‰< Èy@‘$ò€"HäEÈŠ< ‘”Ÿ=¾tüÌ™ç3%†a˜<_büÊËÜFû…qû<˜ÌÞdÞ^ÎçÂGnà…qãþ¢ù¯˜aÜÉU ã¡ãÇ£Òo²têuO6:ÔNaÜeü |ÕóUo¨8>îªå¸ãñÁãŸ_ßù\@õ~ À]µ\7΃ùD+î@ç®R·ï/šÌùË?çC+Î7nì…±»þú6Of5pÂ]±y·Y(öSóëúd÷« Èy@‘$ò€"Häå'­^9 p¢ ãO®vÝàu¸Ž:,Œú‹HäEÈŠ< ‘y@"(ò€DPäIÝ¿hW“ûÕ-A^¾Љ6 ãîy7sõ|Ñ æ€5XÏï/z<"`ì”ÂxÄñÁx®t ¤ÉÂxZ@Ï,Œç÷Ђóó`†sàD§Æ#ú‹^2† 0qnaÜ=–Ï©èP›…ñüþ"Z Häå´ñdnÆ §wþÂÉ6ÓsA‡Ü€þ"ÇWñr÷s~+ÄFîšK³4$ȃko±“ÍrüÊóÿÇïq8O ~¢ëóÊ&›¨Í•u4¤¹ ãO:[÷®c`… £þ¢{jðÖê\‘†Ôyp+/»}á[RŸäÁµÕCõÛhí-~îZËêà†D›äÁ埳zäöl$fâê 䬆D;äÁÅ|r›Üm7æOÊÜ)ÏþnÍÂBX^8§ßãí“]û°Ö¿{¿z1ñnY-/¨¯¢Ô2g?òàÎ~-4늋’ô‰ùE¿¿¾ó߇YY|N\Õ·»ö›P\VØv¡}ûm>øœïgÇj-7ö7‡`--‡ë°yÿ¤³½¼×ñ:ß>Ê‚p§Þ«ëj­UsÕ_Ô©uO‚Ó_´@"ruòà†>¼çêï/-$y$— ®çć<+^ËÞÝ6ua¹}Û/·nÂàò -{ïÚsŒÏŸ^òÕÚ¼ÄSã¹.yp¨åí¹·[ìö`ÛºëSãµ=äÁÿý±ÿe×þŠ7ˆ§}>øŠ<øt38±×þݯ¿{œ¤³€xg¡!ÍB·ÜœOu?ýæÁËÖÜìy ãÒ?ß,çÿ:y ¼H lØ>oT“Çb5©ÇÙw°Üø•†t-ÇÆ#òà«$Ô4X=?€d§TiWaÏŸ3/žÛf 0Ñ6r©¡xÆò×¼v’+üSö2›#ÀOã8:˜Hu…É @áMu%É­Ã OÑç€H$7Àµ ˜S—ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr¿Â0 gnU4½ÖýŽèÌú«ak§}’›†aÇqþïƒ —²µŸ§€ÿø¶ß»ùN-ȼùH?˜x|ñþ‰ç¯ÕrÑŽCÛüü«´9é³ú‚ØÚ‰BŸ»Eóds(/s|oÝ%òøçkÓ/‡K•móë¯Râ˵`k§YúÜ-Z÷•çH¢£¼~rÙ¬ÓwxÊÞ¶~ü³ŠíŸÏ‡–Iî¦åœ,³ŽðC™½hÂô0hÙÞnkÁ6ÿùïXzD‹…ß; v³gçþFË_'ÑY‡f}NÿV¢ë5`¼‡>wéóh{¸™»º‰ÃØÐ²uGù…Ûmâ´»ôÓ>;ÇÂöNèKœè·÷VÜ@r7íû…Y·S›}޽'§ß¿øµp§½}ÍCÛíôü>Î>›Ò½’Lo~äįàö~Þ’ùV\Êhyë_†ï¿›&O¿gúµÎâ¡AG·ùÅöÆÛ{ÚÚçÇ6'忤୸”>w[r25ñ`þ“óŸ–ÿ+¸Nº'ýóÁt÷tz¼û­½Öi.Îi}–>7¿ùrò}oíÃ_O/§èsóÃÔñcºkk_ŠN/ðÏKAÕøúé˜äæ·ÍƒëÐ¥[ûbã*nç´>Ëh9г^G†3O^9ô„3 ¶ïTr¿³ÿ[®Â™©tæåånäã7²ôAŸ"‘Ü!¥ÏL¥3Ê ý‘܉äLÏ›LÊ =‘܉äŽGW›LÊ ]’܉äŽMÿ›LÊ ÝpõÓx¦Û&n‘T6•Î(7tIŸ"‘܉ä€H$7D"¹®å$Aê’ÜIIrãè·¡9t¸©®°Ï-¼àå£å an®pv«Þ›d6±?Uz_þÌT:óòr¿üãÓ%ç–@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H†q¿f®X€Ž4¶°éOÙËl‚†Ô^×™©tæåånäãëðP‘ÑrˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘ÜйqŸ^ &É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnhÑ0 µžüÔ[‘܉䆿èp ’"‘ÜÐ(=o`“ä€H$7´EWH“܉ä†vékž^à?Æqü|>Ã0|ÿ8óä§Þ ¸”>7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’")¹gŸ»óå©Ôân»Q¥ï”|f*-8T£Šåîoˉ¸Ìf´"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D2Œãxø5ÃpÅ¢t¬ ±…MÊ^f|Ü0¤öºÎL¥‡jT±Üým9,³-€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆdÇñðk†áŠEèXAc ›J’› Cªvg¦Ò‚C5ªXîþ¶œˆË iFË É ‘HnˆDr@$’›S¦øÅ}†á†ù; ßÊHnÊ-ÎÚÕ AŽùÇ·†ž^šðm>­É÷ßé爫™?XlÛ›“>«oÄú…‹/Ëú[óñÅ¡}n²ülnÄ9AÍ7ÚÅœþwó}öF¡6_ë[CÉM–ñ¯O²ƒ±Ì7æÅö¼9ièΉÛù·fñøÉÅæå$7ðvS”&Rv/nÅ0÷“܉äصù»Ç½sýoîáÜrþ•8t·n¶ ßÓ¿7^Og†çlüÎ2ãNúÜ|>GÂ8ýn¡N8SfÿÜžkk¶˜‘o Gésóéæc³Íúù£!±ñ眘ö3ÑOvåANÒçæMðÅá ÉM¹Ík®iëk±Á!FËù!g]ë‡øÊp†>7D"¹ É ‘HnˆDr@$CÁ)Ž~‰p”óÉ©¥ðWa6ÁÇMWZ®>•ªQÅr÷·å4²Ì:nR{]g¦Ò‚C5ªXîþ¶œF–Y‡‡ŠŒ–@$’"‘܉ä€H$7D"¹ É ‘HnˆDr@$’"‘܉ä€H$7D"¹ ’Âûs·p×<€Ÿi¯Y ú Ï ‘HnˆDr@$ž^€†axz^*ÿ¸ Eä¸/”‘ÜY41÷;Æj‹-(f´"‘܉ä€H$7D"¹ É ‘HnˆDr@$’›cÒÐ83•ZÔú&¹9l†Dû~f*µ¨tLrsÌ8ŽßëŒn6îg¦R‹Aß\·œóÆ}}Áð3S©E WúÜ”KwÎÎL¥5‚þHnNù®&šþâ©Ô¢FÐÉMéÆýÌTjQ#è†ä¦ÁÐ>5‚>HnˆDrS.]ûÔ: ¹ É ‘Hnªq¹ìö©t@rSSúz[g¦R‹At’›:†aH4ëg¦R‹A\·œróáÓu³~f*µ¨ôGrS"}/Š3S©E W’›Ã º¶O cŽssŒHhŸAß$7D"¹ É ‘HnˆDr@$’›cÜ&²}j}“Ü&Ú§FÐ1ÉM ÁÐ>5‚^In †ö©tÉÕO)7oÜ×Þ:3•ZÔú£ÏÍ)ã8~ÛôÍþÙ™©Ô¢FÐÉMSã¾×úO¥5‚n-§ŽôhꙩԢFÐ}nˆDr@$’"‘܉ä€H$7D"¹ É ‘HnˆÄ5ÔÊ-®¹¾ÈTâ ë«Hf^£êû«/huÃ\®žEâ*žW̨Ö;wSßÛæ/¤Ï]h ‹G~>!sª‹EWÔàšT_à(}îS¦þÄ^ã›~¢> âƒ2½*1i~+§ïm7gš~pý†›æsÉ|Ûô¤«%Æ9rÖü׸èAîu(ë|QÏ5õM/Ãæ2/^¨¾ðNúÜ%Ö­öâNˆ?ŸPeòÿžþÝK¯Ä ÓsOÌëç¤UYùo{ô ‹ë»÷øÛê }Óç~ÒÏ#å‹nßú™9ï°÷´u[Ÿ¹{1Ÿûº¿5Eźë6Ÿ”žE-éÏrr=d¾íâñÅ#WÔ÷³Záé'ï-gûõ…wÒçîÓ<ïÏ\)kv/?ùέ£ºîzH¬ÞºaoÞP_x }î'ý<Î]ÝÛ3o^Ã-¬Þ–¸”ä.ñ'\Ÿ+4ï|¤ŸpÛrÎÿ],CÝ&~ú°›GIï鉲W‹â¥½tõnÚ;?îŠeW_è˜ä>¥àdŸC//6í:äÌt±ŸqèCmOŸ…®'=hså|™×K»p²âµì}„Íex[}¡{ŽsJíË|BÎ{N ò`½<‰÷ŸèC3Ú;À¹>©~ïß§ì-ÕÏU”ÿž‡T©obÞV_è[Éa¿·;ú¶Ï›pç˜ÿ¡Õ~Q9Æñ fëbF!ƒ>èsÿŽöJîç87ük}üX? h俀7ÄØ>ãž7vÄh9D¢Ï д½s/Œ‘¼–>7—pÊçEô¹š–¾ãôàæÅvÖ¯¦.® ¼xÉÑ篗dzŽ_ÄU§Ï Þ<&7_ÿ›ó’œë0¦ÿå úÜám^ÌnóRÇß?ö&%.©;äÝÚuqû†õ²qžäèGñýf¥ì:ž¹“äèÇ™kàO'”é%7Îqn€NL¹›¾ÃОCw XüÁô¹ºR6Nþs’[»¶Crg±_Ù>5‚*G7cxqk×C±íWaÕ¹Ë'гFÚ«F#ŸÞ-sœ"‘܉ãÜ,'o™ä擈Er_(Ü9)¯âœ¯ô©ÈgÖí."¹éßf8 •¯ÍßY9Ð2É}?/~Öâ÷¦®±¶·‰VY?¶ÿ÷0|u?É} ÍÖ#~ƳÆå3[Kë[SX?}±ÝÜõ½¼LìµT_!{w$¼aÖ±¤÷i®[9u×2…îÞŽ×wÒúNëý³ôÓO^L2vÕÉ]YÐÞvÅñ«ïú§íÈdEõ'çã‹çìí<%ž¶7)ó¹ä®)hlþÆm­¶~ó{¾ùHÎ\ò;Ö4’Öq¿[ÜŽsq-ñÏjÀoº5È÷Öa{÷ùÓ;ñë‚õK®û¼ì‘Ü5…¾ÉübáÏ÷Â7›’…½³¾I (^~€†…i3H·H9O[lQ™ïÌÕ$w}óN±\ñmL¯‡Íºõ ÜÏ÷y¡(;7A¿ Ð2É}‰¸MÕ»ÒGo¸~0îz.%­§á––²c{G¦ ž¶<Ïyg®&¹k꣩Z«ø¡Š³çÐ`{Á‚µÌ`8_9¿{œŽ@'ž™xÚú”—CïÌ=$7æG³>çöH.=¿¬ã8×>’þdzãYœ°¶~ÚÞ$' ´CrWÓG‡{ÓÑÎ÷³_ï¸q®Y$-óúB{ÏÞéf™3Ê_®&¹É2?¸µ¹¿~rSšsi %¹ëè¸Ã=7<ï rŒs‡®b’›\uíݬë⼃}žbƒaNrWÐq‡ûç©.›÷§8Î¥5Päf)?l^þ³Ýf}“Ügu['Ï/»âgßeÞyiï9™$÷KÕÅMŸyÞ±ÌÕ¨wN‹K˜Í·–ù#é[ûÝðr.i¾·÷ßò¼>y«1gÖŸÛ»p’û”@î~¸õ’ÁóZ;=âœCN^ég†ÕºÚÒÏwkm^™ËósÖw6}’»gœÕëàù=¿ãçl*¸Nø¢3ºÈ•Ÿ½ð½÷Ù;”èû¶9¯Å;g~¡Ò]üÛHîr v.¹"J7ƒçœ.Î9c¼þ6B{ûÑç5Y·fãß ¼\ü{5É^#é²tð¼Ùõ¹ Îߦnïì26>¯›C· É]èÙ2GI—OœÁó>.j&Î)sçöÐë¼î$¹chd¼L³ƒçv€Îç¬õŸeówîmÚ,É]âžwg¹ÒÈàygkµ˜8iþ½¾zý¢y¥ÏX¨?Û‘Ü Ý±ÎôÈ๴Î$ΣØì#¦¯ÿ“ÿçCžÑG÷îœWEôË%÷au{/ •ÛÏû8tý8qÞˆõϺò[«Êx[¯ãÒyåt»Ó³¾ï\÷‚9=>àù¬“ÿ ë|Õ·¥î µ#ýãÚ§4Ò^_ŒÛÒÝùS«JSÅÍC‰ú܇•íëœyyǪ ž[½°ò¯vuÔ5þó-&’û*â$Sñà¹Áp^åæ ª\íæ‹¨\º÷Ÿ»#¹«1 ~Fæ™çö‡x3Û|VÚýu‘ܧ’º6Ï­d€9É}˜ ¹Ô|ð|þ_’û0Ar+`Ï?O/p€ä€H$7Dò–äö«ÿö©QgÒUn(ö–äþ|>±nâöNjÔ™tA•ʼ%¹Çq\ÿ܈¦¨QgÒUn(ö®_…Ýv—*Š©QûU']På†oésÏÙÓoŸµìÛ]>4Ö.¨rÃ!oLîϬéyzAØ¥F›â6³Fé‚*7ä{iri)Ú§F-+8V.¨rCŽW'÷GK5îhwYxÃIoOn  ‰ ·‘ÜZœÔ(„ü2évÃ’¨Fè $7D"¹ýŠ45êŒKšÃ’ûóqñ¦Ô¨3é‚*7$¼=¹‡aÐF4N:“.¨rÃOïºnù×|,NÑ&5êLº Ê ‡¼+¹ÝØ }jںǜ.¨rC%·Q¸ö©Q\›l`®ð–äÖF´OÂù9.¶á oIn .Ñ Oyû¹å@± Òçމr¥”i9Ggô¹Ã¤ 5êŒ[yÂEÞ’Ü-EjB”ûpëgÓ«%÷G0D FÑó†êÞ•Ü-EjÔ¾C5z<¼u¾éÌ6o)|¥Û¤FíûÖ(³:é‚*7òº>÷×8ŽßBß®YjÔ¾oâæóNô¢rÛ K%BêéòIöôÛ§F;z“tA{-wOÍ&{{rUhÒ¬*zãqn :Ǫá6/=Î T7«.%¹š„7\Mr@$’"‘܉ä€H$7D"¹ ’ç¯Ä²yõ†>.épôªÍZÌ} ô±ü“?È™5¼w…óô»íÍ1z­y•†úÜÓ÷ð¢;Läßf)b¾õŠjdÕ)"œô|Ÿ;Óü«¾Ù;Ÿ?¾~p¾[ØÝž¦š]âñÏj_~óßÍå?tÅÍ—öûÈÞ2+âmÖµX„ez%¬«™®rþ*Í)bæRí•ã绥_•óZxVC}FgóñD •9—C³û9ÇĽ Ó/ŒÕ/™/í¡«ˆ×™GÎ:ê&éÞùÏ¿ËVéZñR¼[z„¯©"ÂZ+}îùÞôâßDÿfzòúñŃÓ&vŸ“Ö]„‚Åȱ÷ÂX{ú‰5óQÄ–lv¦=à½U”¿êöVõ^Ï,UB¢ Ó’ì Š\q¿p¨¢•äΗùMþÙÄïIïŒ'f]Üš¼°iPÄÇMŸ}±Ó|ÔÑU}ÏR}~Åüwêæ\âÖ”÷h+¹~UßÉïÔ⦿®ùÞý³KÒ Eä ÊDÇÚJîS;ž3òYüíý™‹¹çäJ:¢^Eï1^¯êŠ£ÁGWuâiU–êçvõS³5…OSɽwìûøÞw/}Kñb¬ïí-ÆÞóï_öÂ_TE¼Yb„`sRñ2]Õ‡øäšLÌý»‹ßà´PS˜Ä8·|ó´££Gξrš’ÍÙ<~ôÁ–[‡ó‹ªˆ7[/É¡[<£Ì¹lîRÔZªüá“X5…¯’_š~d ÐÍ&Åès_’"‘܉ä€H$7D"¹ É ‘Hnˆ¤ðê§®æpYˆÄh9D"¹ É ‘HnˆDrÿæDú¨BgŠInˆDrÿ gÐU茂Â’"‘ÜYtZ  QP(#¹ É¢OÐU茂ÂI’"‘ܹtZ  QP(à^a¿ ƒµô}n€÷Z¤}í:¶3ßVl“ÜýÛÌ×ùÔy‚NÿH¿öóߎø¢·½ùþëqˆäx©ù¨x~o{/nyŸ9k Hn€WøÙu¾_S ˆäx‹½!ë¹Ä ×A;þ¼_¶$d’Ü/5åñ\æk÷Î$_œ|ž3÷£‹äx‘E ¦ÿMw²ó߇ºÜ"÷77n*tFA¡˜>7D"¹ É ‘HnˆDr@$’"‘܉ä€Hן€¾‘­Ï ‘¸!Dò?„ý‹®'RIEND®B`‚‰PNG  IHDR“=ébd4 pHYs M MÒέNtEXtSoftwareGPL Ghostscript 8.15R « IDATxœíÕA À0À¿çC/²¤U°ßöÌ, âü87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”87”\=wI518IEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/images/PullPushAdapter.fig0000644000175000017500000000401611366547366025774 0ustar twernertwerner#FIG 3.2 Landscape Center Inches Letter 301.00 Single -2 1200 2 6 3750 2700 4050 3300 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3750 2700 3750 3300 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 4050 2700 4050 3300 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3750 2775 4050 2775 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3750 2925 4050 2925 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3750 3075 4050 3075 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 2 3750 3225 4050 3225 -6 6 3075 1875 3300 2325 3 2 0 1 0 7 100 0 -1 0.000 0 0 0 7 3131 1932 3244 2020 3075 2063 3244 2151 3075 2194 3244 2282 3131 2325 0.000 -1.000 -1.000 -1.000 -1.000 -1.000 0.000 -6 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 2100 2550 4350 2550 4350 3450 2100 3450 2100 2550 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 2775 1800 4350 1800 4350 2400 2775 2400 2775 1800 2 4 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5 4350 1275 4350 600 2025 600 2025 1275 4350 1275 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 3900 1950 2850 1350 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 3375 975 4200 975 4200 1275 3375 1275 3375 975 2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 2775 975 3300 975 3300 1275 2775 1275 2775 975 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2475 1275 2475 2550 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 2475 2925 2475 3750 2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 3900 3750 3900 3300 2 2 0 1 0 7 100 0 20 0.000 0 0 -1 0 0 5 2175 2550 2775 2550 2775 2850 2175 2850 2175 2550 3 0 0 1 0 7 100 0 -1 0.000 0 1 0 4 0 0 1.00 60.00 120.00 3375 2250 3825 2700 3900 2775 3900 1275 0.000 1.000 1.000 0.000 4 0 0 50 0 16 11 0.0000 4 120 1035 2700 4125 Protocol Stack\001 4 0 0 50 0 16 11 0.0000 4 120 360 2250 2775 Send\001 4 0 0 50 0 16 11 0.0000 4 120 330 2850 1200 View\001 4 0 0 50 0 16 11 0.0000 4 120 525 3450 1200 Receive\001 4 0 0 50 0 16 11 0.0000 4 150 765 4575 1050 Application\001 4 0 0 50 0 16 11 0.0000 4 150 1140 4575 2175 PullPushAdapter\001 4 0 0 50 0 16 11 0.0000 4 120 570 4575 3075 Channel\001 libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/master.xml0000644000175000017500000002302611366547366023002 0ustar twernertwerner ]> Reliable Multicasting with the JGroups Toolkit $Date: 2007/07/16 07:48:34 $ $Revision: 1.9 $ Bela Ban JGroups Project

belaban@yahoo.com
1998-2008 Bela Ban 2006-2008 Red Hat Inc This document is copyrighted. Copies are allowed for personal use. Redistribution only with written permission of the author(s). Foreword This is the Programmer's and User's Guide for JGroups. It provides information about the following areas: Installation and configuration. Using JGroups. Architecture and implementation of JGroups. Focus on the protocol stack and protocols. Most of the Installation and User's Guide has been copied from what is freely available on the JGroups web site . However, the focus of this document is to introduce programmers who want to learn more about JGroups to the architecture and implementation of JGroups. I will for example go into the details of the protocol stack, how a message traverses the stack, and how protocols can process it. I will also explain the various design decisions I had to make when designing JGroups, which hopefully leads to a better understanding of why things are the way they are. Here are a couple of points I want to abide by throughout this book: I like brevity. I will strive to describe concepts as clearly as possible (for a non-native English speaker) and will refrain from saying more than I have to to make a point. I like simplicity. Keep It Simple and Stupid. This is one of the biggest goals I have both in writing this book and in writing JGroups. It is easy to explain simple concepts in complex terms, but it is hard to explain a complex system in simple terms. I'll try to do the latter. So, how did it all start? I spent 1998-1999 at the Computer Science Department at Cornell University for a post-doc, in Ken Birman's group. Ken is credited with inventing the group communication paradigm, especially the Virtual Synchrony model. At the time they were working on their third generation group communication prototype, called Ensemble. Ensemble followed Horus (written in C by Robbert VanRenesse), which followed ISIS (written by Ken Birman, also in C). Ensemble was written in OCaml, developed at INRIA, which is a functional language and related to ML. I never liked the OCaml language, which in my opinion has a hideous syntax. Therefore I never got warm with Ensemble either. However, Ensemble had a Java interface (implemented by a student in a semester project) which allowed me to program in Java and use Ensemble underneath. The Java part would require that an Ensemble process was running somewhere on the same machine, or within the same network, and would connect to it via a bidirectional pipe. The student had developed a simple protocol for talking to the Ensemble engine, and extended the engine as well to talk back to Java. However, I still needed to compile and install the Ensemble runtime for each different platform, which is exactly why Java was developed in the first place: portability. Therefore I started writing a simple framework (now JChannel ), which would allow me to treat Ensemble as just another group communication transport, which could be replaced at any time by a pure Java solution. And soon I found myself working on a pure Java implementation of the group communication transport (now: ProtocolStack ). I figured that a pure Java implementation would have a much bigger impact that something written in Ensemble. In the end I didn't spend much time writing scientific papers that nobody would read anyway (I guess I'm not a good scientist, at least not a theoretical one), but rather code for JGroups, which could have a much bigger impact. For me, knowing that real-life projects/products are using JGroups is much more satisfactory than having a paper accepted at a conference/journal. That's why, after my time was up, I left Cornell and academia altogether, and started a job in the industry: with Fujitsu Network Communications in Silicon Valley. At around that time (May 2000), SourceForge had just opened its site, and I decided to use it for hosting JGroups. I guess this was a major boost for JGroups because now other developers could work on the code. From then on, the page hit and download numbers for JGroups have steadily risen. In the fall of 2002, Sacha Labourey contacted me, letting me know that JGroups was being used by JBoss for their clustering implementation. I joined JBoss in 2003 and have been working on JGroups and JBossCache. My goal is to make JGroups the most widely used clustering software in the Java space... Bela Ban, San Jose, Aug 2002, Kreuzlingen Switzerland 2006 Acknowledgments I want to thank all contributors to JGroups, present and past, for their work. Without you, this project would never have taken off the ground. I also want to thank Ken Birman and Robbert VanRenesse for many fruitful discussions of all aspects of group communication in particular and distributed systems in general. I want to dedicate this book to Jeannette and Michelle. &overview; &installation; &api; &blocks; &advanced; &protocols; The Ensemble Distributed Communication System , CS Dept Cornell University , 1997 . . Erich Gamma , Richard Helm , Ralph Johnson , and John Vlissides . Design Patterns: Elements of Reusable Object-Oriented Software . Addison-Wesley , 1995 . libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/0000755000175000017500000000000011621261107022406 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/installation.xml0000644000175000017500000004653711366547366025674 0ustar twernertwerner Installation and Configuration The installation refers to version 2.5 of JGroups. Refer to the installation instructions that are shipped with JGroups for details. Note that these instructions are also available in the JGroups distribution (INSTALL.HTML). JGroups comes in a binary and a source version: the binary version is JGroups-2.x.x.bin.zip, the source version is JGroups-2.x.x.src.zip. The binary version contains the JGroups JAR file, plus a number of JARs needed by JGroups. The source version contains all source files, plus several JAR files needed by JGroups, e.g. ANT to build JGroups from source.
Requirements JGroups 2.5 requires JDK 5 or higher. There is no JNI code present so it should run on all platforms. If you want to generate HTML-based test reports from the unittests, then xalan.jar needs to be in the CLASSPATH (also available in the lib directory)
Installing the binary distribution The binary version contains jgroups-all.jar: the JGroups library including the demos CREDITS: list of contributors INSTALL.html: this file commons-logging.jar log4j.jar. This JAR is optional, for example if JDK logging is used, we don't need it. Place the JAR files somewhere in your CLASSPATH, and you're ready to start using JGroups. If you want to use the JGroups JMS protocol (org.jgroups.protocols.JMS), then you will also need to place jms.jar somewhere in your CLASSPATH.
Installing the source distribution The source version consists of the following directories and files: src: the sources test: unit and stress tests conf: configuration files needed by JGroups, plus default protocol stack definitions doc: documentation lib: various JARs needed to build and run JGroups: Ant JARs: used to build JGroups. If you already have Ant installed, you won't need these files jms.jar: JMS library. Needed only if you intend to run the org.jgroups.protocols.JMS protocol junit.jar: to run the JUnit test cases xalan.jar: to format the output of the JUnit tests using an XSLT converter to HTML commons-logging.jar log4j.jar etc
Building JGroups (source distribution only) Unzip the source distribution, e.g. unzip JGroups-2.x.x.src.zip. This will create the JGroups-2.x.x directory (root directory) under the current directory. cd to the root directory Modify build.properties if you want to use a Java compiler other than javac (e.g. jikes), or if you want to change the interface JGroups uses for sending and receiving messages On UNIX systems use build.sh, on Windows build.bat: $> ./build.sh compile This will compile all Java files (into the classes directory). To generate the JARs: $> ./build.sh jar This will generate the following JAR files in the dist directory: jgroups-core.jar - the core JGroups libraries jgroups-all.jar - the complete JGroups libraries including demos and unit tests The CLASSPATH now has to be set accordingly: the following directories and/or JARs have to be included: <JGroups rootdir>/classes <JGroups rootdir>/conf All needed JAR files in <JGroups rootdir>/lib . To build from sources, the two Ant JARs are required. To run unit tests, the JUnit (and possibly Xalan) JARs are needed. To generate JavaDocs simple run $> ./build.sh javadoc and the Javadoc documentation will be generated in the dist/javadoc directory Note that - if you already have Ant installed on your system - you do not need to use build.sh or build.bat, simply invoke ant on the build.xml file. To be able to invoked ant from any directory below the root directory, place ANT_ARGS="-find build.xml -emacs" into the .antrc file in your home directory. For more details on Ant see .
Testing your Setup To see whether your system can find the JGroups classes, execute the following command: java org.jgroups.Version or (from JGroups 2.2.8 on) java -jar jgroups-all.jar You should see the following output (more or less) if the class is found: bela@dell /cygdrive/c/JGroups/dist $ java -jar jgroups-all.jar Version: 2.6.0 pre-alpha CVS: $Id: installation.xml,v 1.5 2007/07/24 16:30:46 belaban Exp $
Running a Demo Program To test whether JGroups works okay on your machine, run the following command twice: java org.jgroups.demos.Draw 2 whiteboard windows should appear as shown in .
Screenshot of 2 Draw instances
Both windows should show 2 in their title bars. This means that the two instances found each other and formed a group.
When drawing in one window, the second instance should also be updated. As the default group transport uses IP multicast, make sure that - if you want start the 2 instances in different subnets - IP multicast is enabled. If this is not the case, the 2 instances won't find each other and the sample won't work. You can change the properties of the demo to for example use a different transport if multicast doesn't work (it should always work on the same machine). Please consult the documentation to see how to do this.
Using IP Multicasting without a network connection Sometimes there isn't a network connection (e.g. DSL modem is down), or we want to multicast only on the local machine. For this the loopback interface (typically lo) can be configured, e.g. route add -net 224.0.0.0 netmask 240.0.0.0 dev lo This means that all traffic directed to the 224.0.0.0 network will be sent to the loopback interface, which means it doesn't need any network to be running. Note that the 224.0.0.0 network is a placeholder for all multicast addresses in most UNIX implementations: it will catch all multicast traffic. This is an undocumented feature of /sbin/route and may not work across all UNIX flavors. The above instructions may also work for Windows systems, but this hasn't been tested. Note that not all systems allow multicast traffic to use the loopback interface. Typical home networks have a gateway/firewall with 2 NICs: the first (eth0) is connected to the outside world (Internet Service Provider), the second (eth1) to the internal network, with the gateway firewalling/masquerading traffic between the internal and external networks. If no route for multicast traffic is added, the default will be to use the fdefault gateway, which will typically direct the multicast traffic towards the ISP. To prevent this (e.g. ISP drops multicast traffic, or latency is too high), we recommend to add a route for multicast traffic which goes to the internal network (e.g. eth1).
It doesn't work ! Make sure your machine is set up correctly for IP multicast. There are 2 test programs that can be used to detect this: McastReceiverTest and McastSenderTest. Start McastReceiverTest, e.g. java org.jgroups.tests.McastReceiverTest -mcast_addr 224.10.10.10 -port 5555 Then start McastSenderTest: java org.jgroups.tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555 If you want to bind to a specific network interface card (NIC), use -bind_addr 192.168.0.2, where 192.168.0.2 is the IP address of the NIC to which you want to bind. Use this parameter in both sender and receiver. You should be able to type in the McastSenderTest window and see the output in the McastReceiverTest. If not, try to use -ttl 32 in the sender. If this still fails, consult a system administrator to help you setup IP multicast correctly. If you are the system administrator, look for another job :-) Other means of getting help: there is a public forum on JIRA for questions. Also consider subscribing to the javagroups-users mailing list to discuss such and other problems.
The instances still don't find each other ! In this case we have to use a sledgehammer (running only under JDK 1.4. and higher): we can enable the above sender and receiver test to use all available interfaces for sending and receiving. One of them will certainly be the right one... Start the receiver as follows: java org.jgroups.tests.McastReceiverTest1_4 -mcast_addr 228.8.8.8 -use_all_interfaces The multicast receiver uses the 1.4 functionality to list all available network interfaces and bind to all of them (including the loopback interface). This means that whichever interface a packet comes in on, we will receive it. Now start the sender: java org.jgroups.tests.McastSenderTest1_4 -mcast_addr 228.8.8.8 -use_all_interfaces The sender will also determine the available network interfaces and send each packet over all interfaces. This test can be used to find out which network interface to bind to when previously no packets were received. E.g. when you see the following output in the receiver: bash-2.03$ java org.jgroups.tests.McastReceiverTest1_4 -mcast_addr 228.8.8.8 -bind_addr 192.168.168.4 Socket=0.0.0.0/0.0.0.0:5555, bind interface=/192.168.168.4 dd [sender=192.168.168.4:5555] dd [sender=192.168.168.1:5555] dd [sender=192.168.168.2:5555] you know that you can bind to any of the 192.168.168.{1,2,4} interfaces to receive your multicast packets. In this case you would need to modify your protocol spec to include bind_addr=192.168.168.2 in UDP, e.g. "UDP(mcast_addr=228.8.8.8;bind_addr=192.168.168.2):..." .
Problems with IPv6 Another source of problems might be the use of IPv6, and/or misconfiguration of /etc/hosts. If you communicate between an IPv4 and an IPv6 host, and they are not able to find each other, try the java.net.preferIP4Stack=true property, e.g. java -Djava.net.preferIPv4Stack=true org.jgroups.demos.Draw -props file:/home/bela/udp.xml JDK 1.4.1 uses IPv6 by default, although is has a dual stack, that is, it also supports IPv4. Here's more details on the subject.
Wiki There is a wiki which lists FAQs and their solutions at . It is frequently updated and a useful companion to this user's guide.
I have discovered a bug ! If you think that you discovered a bug, submit a bug report on JIRA or send email to javagroups-developers if you're unsure about it. Please include the following information: Version of JGroups (java org.jgroups.Version) Platform (e.g. Solaris 8) Version of JDK (e.g. JDK 1.4.2_07) Stack trace. Use kill -3 PID on UNIX systems or CTRL-BREAK on windows machines Small program that reproduces the bug
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/overview.xml0000644000175000017500000002415111366547366025025 0ustar twernertwernerOverview Group communication uses the terms group and member. Members are part of a group. In the more common terminology, a member is a node and a groups is a cluster. We use these words interchangeably. A node is a process, residing on some host. A cluster can have one or more nodes belonging to it. There can be multiple nodes on the same host, and all may or may not be part of the same cluster. JGroups is toolkit for reliable group communication. Processes can join a group, send messages to all members or single members and receive messages from members in the group. The system keeps track of the members in every group, and notifies group members when a new member joins, or an existing member leaves or crashes. A group is identified by its name. Groups do not have to be created explicitly; when a process joins a non-existing group, that group will be created automatically. Member processes of a group can be located on the same host, within the same LAN, or across a WAN. A member can be part of multiple groups. The architecture of JGroups is shown in .
The architecture of JGroups
It consists of 3 parts: (1) the Channel API used by application programmers to build reliable group communication applications, (2) the building blocks, which are layered on top of the channel and provide a higher abstraction level and (3) the protocol stack, which implements the properties specified for a given channel. This document describes how to install and use JGroups, ie. the Channel API and the building blocks. The targeted audience is application programmers who want to use JGroups to build reliable distributed programs that need group communication. Programmers who want to implement their own protocols to be used with JGroups should consult the Programmer's Guide for more details about the architecture and implementation of JGroups. A channel is connected to a protocol stack. Whenever the application sends a message, the channel passes it on to the protocol stack, which passes it to the topmost protocol. The protocol processes the message and the passes it on to the protocol below it. Thus the message is handed from protocol to protocol until the bottom protocol puts it on the network. The same happens in the reverse direction: the bottom (transport) protocol listens for messages on the network. When a message is received it will be handed up the protocol stack until it reaches the channel. The channel stores the message in a queue until the application consumes it. When an application connects to the channel, the protocol stack will be started, and when it disconnects the stack will be stopped. When the channel is closed, the stack will be destroyed, releasing its resources. The following three sections give an overview of channels, building blocks and the protocol stack.
Channel To join a group and send messages, a process has to create a channel and connect to it using the group name (all channels with the same name form a group). The channel is the handle to the group. While connected, a member may send and receive messages to/from all other group members. The client leaves a group by disconnecting from the channel. A channel can be reused: clients can connect to it again after having disconnected. However, a channel allows only 1 client to be connected at a time. If multiple groups are to be joined, multiple channels can be created and connected to. A client signals that it no longer wants to use a channel by closing it. After this operation, the channel cannot be used any longer. Each channel has a unique address. Channels always know who the other members are in the same group: a list of member addresses can be retrieved from any channel. This list is called a view. A process can select an address from this list and send a unicast message to it (also to itself), or it may send a multicast message to all members of the current view. Whenever a process joins or leaves a group, or when a crashed process has been detected, a new view is sent to all remaining group members. When a member process is suspected of having crashed, a suspicion message is received by all non-faulty members. Thus, channels receive regular messages, view messages and suspicion messages. A client may choose to turn reception of views and suspicions on/off on a channel basis. Channels are similar to BSD sockets: messages are stored in a channel until a client removes the next one (pull-principle). When no message is currently available, a client is blocked until the next available message has been received. There is currently only one implementation of Channel: JChannel. The properties of a channel are typically defined in an XML file, but JGroups also allows for configuration through simple strings, URIs, DOM trees or even programming. The Channel API and its related classes is described in .
Building Blocks Channels are simple and primitive. They offer the bare functionality of group communication, and have on purpose been designed after the simple model of BSD sockets, which are widely used and well understood. The reason is that an application can make use of just this small subset of JGroups, without having to include a whole set of sophisticated classes, that it may not even need. Also, a somewhat minimalistic interface is simple to understand: a client needs to know about 12 methods to be able to create and use a channel (and oftentimes will only use 3-4 methods frequently). Channels provide asynchronous message sending/reception, somewhat similar to UDP. A message sent is essentially put on the network and the send() method will return immediately. Conceptual requests, or responses to previous requests, are received in undefined order, and the application has to take care of matching responses with requests. Also, an application has to actively retrieve messages from a channel (pull-style); it is not notified when a message has been received. Note that pull-style message reception often needs another thread of execution, or some form of event-loop, in which a channel is periodically polled for messages. JGroups offers building blocks that provide more sophisticated APIs on top of a Channel. Building blocks either create and use channels internally, or require an existing channel to be specified when creating a building block. Applications communicate directly with the building block, rather than the channel. Building blocks are intended to save the application programmer from having to write tedious and recurring code, e.g. request-response correlation. Building blocks are described in .
The Protocol Stack The protocol stack containins a number of protocol layers in a bidirectional list. All messages sent and received over the channel have to pass through the protocol stack. Every layer may modify, reorder, pass or drop a message, or add a header to a message. A fragmentation layer might break up a message into several smaller messages, adding a header with an id to each fragment, and re-assemble the fragments on the receiver's side. The composition of the protocol stack, i.e. its layers, is determined by the creator of the channel: an XML file defines the layers to be used (and the parameters for each layer). This string might be interpreted differently by each channel implementation; in JChannel it is used to create the stack, depending on the protocol names given in the property. Knowledge about the protocol stack is not necessary when only using channels in an application. However, when an application wishes to ignore the default properties for a protocol stack, and configure their own stack, then knowledge about what the individual layers are supposed to do is needed. Although it is syntactically possible to stack any layer on top of each other (they all have the same interface), this wouldn't make sense semantically in most cases.
Header A header is a custom bit of information that can be added to each message. JGroups uses headers extensively, for example to add sequence numbers to each message (NAKACK and UNICAST), so that those messages can be delivered in the order in which they were sent.
Event Events are means by which JGroups protcols can talk to each other. Contrary to Messages, which travel over the network between group members, events only travel up and down the stack.
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/tracing.xml0000644000175000017500000004020511366547366024604 0ustar twernertwernerTracing The following information is not accurate anymore. JGroups switched to Apache commons-logging a long time ago, and can now use either log4j or the JDK logging subsystem, or any other implementation. I want to thank Jim Menard for this (now deprecated) Tracing subsystem anyway, this was incredibly useful in finding out bugs, especially when the only debugger available was jdb ! JGroups provides its own tracing module, located in the org.javagroups.log package. The reason for having its own tracing is that tracing was written before log4j or the JDK 1.4 logging API were available. We will probably switch to the JDK 1.4 logging API once JGroups is moved to 1.4, but in the meantime, the current tracing module is doing a fine job. Tracing allows a programmer to write a message to some output, which can be a file, stdout/stderr or a socket. Outputs are associated with a module name and a trace level. A module name specifies a class name and optionally a method name. A trace level is an integer specifying a level of output verbosity. The Trace class defines trace level constants DEBUG, TEST, INFO, WARN, ERROR or FATAL (in increasing order of importance). Messages sent to an output are only printed if the trace level of the message is greater than or equal to the trace level set for the output. Outputs are flushed when a program terminates. Outputs may be set to auto-flush after every call to print. By default, auto-flushing is turned off. As of Java 1.3, the Runtime.addShutdownHook() method allows us to register a thread that is used to flush and close all outputs when the program exits. Note that this will not be the case if a program is killed via kill -9. If a module is not associated with any output, then a default output is used. The initial value of the default output is null. Messages sent to a null output are ignored.
Module names A module name is a string used to associate a message with a particular logging output. Module names should be of the form classname[.methodname([args...])]]: a class name optionally followed by a method name with parenthesis that optionally contain arguments. Here are some examples of legal module names: MyClass (classname) JGroups.Common.MyClass (class names can be fully qualified) MyClass.method() (classname and method, no args) MyClass.method(int, String) (classname and method, with args) MyClass.method (legal, but this is a fully qualified class name because there are no parenthesis) Currently, method arguments are always ignored. In the future, we may want to be able to distinguish between methods with the same name and different arguments (for example, MyClass.method(int) and MyClass.method(String)).
Output lookup When calling Trace.print(), the first argument, the first argument is a module name. When looking for which output to use, Trace uses the following set of rules. If no output is found (the returned output is null), then nothing is printed. If the module name contains no method name (there are no parenthesis), look for the output associated with this module name. If found, return it. Else, return the default output (initially null). Look for the output associated with this module and method name, excluding arguments if any. Strip off the method name and look for the output associated with the class name. Use the default output (initially null). For example, Trace.print("JChannel.foo()", Trace.FATAL, "ouch!"); would first look for JChannel.foo(), then JChannel, then use the default output (which may be null, which means no output will be produced).
Using Trace To use Trace in a program, the following things have to be done: Define trace outputs Use trace statements in your code
Defining Trace outputs There are two ways to define Trace outputs: programmatically and file-based. The setOutput() methods allow a programmer to add trace output as in the following example: Trace.setOutput("MyClass", Trace.WARN, "/tmp/logfile"); This means that all trace statements whose module names start with MyClass and whose level is WARN or greater will be written to /tmp/logfile. Any number of such statements can be given, and the one that matches most of the module name will be used. For example, we can have an additional statement like: Trace.setOutput("MyClass.foo", Trace.WARN, "/tmp/logfile2"); In this case, a trace statement whose module name is MyClass.foo() would be written to /tmp/logfile2 rather than /tmp/logfile. Tracing can also be configured through a property file which is read by Trace.init(). This has the advantage that tracing can be modified by changing a file rather than changing code. In order to configure Trace from a file, one of the two Trace.init() methods can be called: public static void init(String fname) throws IOException, SecurityException; public static void init(); The first method takes a filename as argument. It can either be a fully qualified filename or a relative filename. In the latter case, the file will be looked up in the directory from which the program was started. If fname refers to a directory, JGroups will automatically add a directory separator followed by javagroups.properties, e.g. if fname is /tmp, then the full filename will be /tmp/javagroups.properties. If the file is not found, or cannot be opened, an exception will be thrown. The second init() method takes no parameters and throws no exceptions. The following algorithm is used to determine the filename: If there is a javagroups.properties file in the user's home directory (user.home property), then it will be used. Otherwise javagroups.properties will be searched in the CLASSPATH and the first one found will be used (the Class.getResourceAsStream() method call is used for this). A property file looks as follows: trace=true timestamp_format=HH:mm:ss[SSS] default_output=WARN STDERR # trace2=ClientGmsImpl.join DEBUG STDOUT # trace2=CoordGmsImpl.handleJoin DEBUG STDOUT # trace3=GMS.castViewChange DEBUG STDOUT trace4=SMACK DEBUG STDOUT trace5=AckMcastSenderWindow DEBUG /tmp/trace.ack trace8=NAKACK.up DEBUG STDOUT The trace=true statement enables or disabled tracing. This sets the public static Trace.trace variable, which should be queried by trace statements before executing a trace statement (see . This flag is on by default. If tracing should be disabled, set it to false. The nextNote that statements can occur in any order.statement is timestamp_format. It defines the format of the timestamp which occurs before every line of output. The format used is the one defined by java.text.SimpleDateFormat. In the above example, we will print the hour, minute, second and millisecond when the event occurred. For example, a trace statement Trace.info("NAKACK.up()", "hello world") will yield the following output: [14:07:07[539]] [INFO] NAKACK.up(): hello world]. The next statement is default_output. It defines an output that is used as a fallback, if there are no other outputs defined. In the example, all warnings or higher (error and fatal messages) will by default be printed to stderr. To enable all output on all levels to go to file c:\tmp\trace.all, the following statement can be used: default_ouput=DEBUG c:\tmp\trace.all This is not recommended, as all output for all levels (DEBUG is the lowest level) will be dumped to c:\tmp\trace.all. Next we have a couple of comments, indicated by a line starting with a hash (#). Note that this is the only accepted comment sign; C- or Java-like comments will not be accepted. Next come zero or more trace statements. In the example we're tracing class SMACK to stdout, AckMcastSenderWindow to file /tmp/trace.ack and method NAKACK.up() to stdout. All of these statements have to start with the trace keyword, followed by a unique ID. Any ID can be used, e.g. traceA, trace-01 and traceMyTrace are all valid trace statements. After the equals sign, the module name has to be given. This can be either a class name, or a class name followed by a method name (no parameters must be given !). Note that an inner class method to be traced might look like MyClass.MyInnerClass.method1(). The next entry defines the trace level: all statements with a level equal or higher to the one given are printed. The last entry in the trace statement is the output: it can be either STDERR, STDOUT The standard error or output streams <filename> A fully qualified file name <host:port> The name and port of a host. The output will be sent to a host on which a listener has to be running (similar to UNIX syslogd).
Using Trace statements Trace statements can be used anywhere in the code (after org.javagroups.log.* has been imported). There are several public static methods in the Trace class which can be used: public static void println(String module, int level, String message); public static void debug(String module, String message); public static void test(String module, String message); public static void info(String module, String message); public static void warn(String module, String message); public static void error(String module, String message); public static void fatal(String module, String message); Method print() takes the module, level and message to be printed as arguments. Methods debug(), test(), info(), warn(), error() and fatal() are mere convenience methods and all use println() internally.
Conditional tracing Naive tracing statements can lead to bad performance and a lot of memory used. Consider the following case: for(int i=0; i < 1000; i++) Trace.info("MyClass.foo()", "hello world"); When tracing is enabled, this will print the trace statement 1000 times. When tracing is disabled, it will not be printed. However, Trace.info() will still be evaluated, resulting in 1000 superfluous string creationsActually, there will be more than 1000 strings created because of concatenation.. Therefore to prevent evaluating trace statements when tracing is disabled, we can add a check before executing the trace statement: for(int i=0; i < 1000; i++) { if(Trace.trace) Trace.info("MyClass.foo()", "hello world"); } If trace is disabled, the ony thing we do here is the check for Trace.trace; the trace statement will not be evaluated. Note that it is very important to use such checks in code that is executed frequently. For example, the UDP protocol has such conditional trace statements whenever it receives or sends a datagram packet. Otherwise, if tracing was disabled, a lot of memory would be used for superfluous temporary string creation and subsequent deletion.
Removing tracing altogether The Trace.trace flag can be used to enable/disable tracing of certain statements. However, if we want to add trace statements for testing purposes that are not supposed to be shipped in the code for the official release, we can use the Trace.debug flag. This flag is defined as public static final in Trace and is set to true by default. A trace statement can use it as follows: for(int i=0; i < 1000; i++) { if(Trace.debug) Trace.info("MyClass.foo()", "hello world"); } In this case if Trace.debug is set to true, the trace statement will be printed. However, to remove all code using Trace.debug altogether, one has to set Trace.debug to false (in Trace.java) and recompile JGroups. The above example would not include the conditional trace statement in the generated code.
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/protocols.xml0000644000175000017500000020474611366547366025215 0ustar twernertwerner List of Protocols This section is work in progress; we strive to update the documentation as we make changes to the code. The most important properties are described on the wiki. The idea is that users take one of the predefined configurations (shipped with JGroups) and make only minor changes to it. For each protocol define: Properties provided Required services Provided services Behavior
Transport
UDP
TCP
TCP_NIO
TUNNEL
JMS
LOOPBACK
Initial membership discovery
PING
TCPPING
TCPGOSSIP
MPING
Merging after a network partition
MERGE2, MERGE3, MERGEFAST
Failure Detection The task of failure detection is to probe members of a group and see whether they are alive. When a member is suspected (= deemed dead), then a SUSPECT message is sent to all nodes of the cluster. It is not the task of the failure detection layer to exclude a crashed member (this is done by the group membership protocol, GMS), but simply to notify everyone that a node in the cluster is suspected of having crashed.
FD Failure detection based on heartbeat messages. If reply is not received without timeout ms, max_tries times, a member is declared suspected, and will be excluded by GMS Each member send a message containing a "FD" - HEARTBEAT header to its neighbor to the right (identified by the ping_dest address). The heartbeats are sent by the inner class Monitor. When the neighbor receives the HEARTBEAT, it replies with a message containing a "FD" - HEARTBEAT_ACK header. The first member watches for "FD" - HEARTBEAT_ACK replies from its neigbor. For each received reply, it resets the last_ack timestamp (sets it to current time) and num_tries counter (sets it to 0). The same Monitor instance that sends heartbeats whatches the difference between current time and last_ack. If this difference grows over timeout, the Monitor cycles several more times (until max_tries) is reached) and then sends a SUSPECT message for the neighbor's address. The SUSPECT message is sent down the stack, is addressed to all members, and is as a regular message with a FdHeader.SUSPECT header. Properties Name Description timeout Max number of ms to wait for a response, e.g timeout="2500" max_tries Max number of missed responses until a member is declare suspected., e.g. max_tries="5" shun Once a member is excluded from the group, and then rejoins (e.g. because it didn't crash, but was just slow, or a router that had crashed came back), it will be excluded (shunned) and then has to rejoin. JGroups allows to configure itself such that shunning leads to automatic rejoins and state transfer (default in JBoss), e.g. shun="true". Automatic rejoins can be enabled by setting channel option AUTO_RECONNECT to true: channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE). Same for automatically fetching the state after automatic reconnection: channel.setOpt(Channel.AUTO_GETSTATE, Boolean.TRUE).
FD_ALL Failure detection based on simple heartbeat protocol. Every member periodically multicasts a heartbeat. Every member also maintains a table of all members (minus itself). When data or a heartbeat from P are received, we reset the timestamp for P to the current time. Periodically, we check for expired members, and suspect those. Example: <FD_ALL interval="3000" timeout="10000"/> In the exampe above, we send a heartbeat every 3 seconds and suspect members if we haven't received a heartbeat (or traffic) for more than 10 seconds. Note that since we check the timestamps every 'interval' milliseconds, we will suspect a member after roughly 4 * 3s == 12 seconds. If we set the timeout to 8500, then we would suspect a member after 3 * 3 secs == 9 seconds. Properties Name Description timeout Max number of milliseconds until a member is suspected interval Interval (in milliseconds) to multicast heartbeat messages to the cluster msg_counts_as_heartbeat If this is true, we treat traffic from P as if P sent a heartbeat, ie. we set the timestamp for P to the current time. Default is true shun Once a member is excluded from the group, and then rejoins (e.g. because it didn't crash, but was just slow, or a router that had crashed came back), it will be excluded (shunned) and then has to rejoin. JGroups allows to configure itself such that shunning leads to automatic rejoins and state transfer (default in JBoss). , e.g. shun="true"
FD_SIMPLE
FD_PING FD_PING uses a script or command that is run with 1 argument (the host to be pinged) and needs to return 0 (success) or 1 (failure). The default command is /sbin/ping (ping.exe on Windows), but this is user configurable and can be replaced with any user-provided script or executable. Properties Name Description cmd The command to be executed, e.g. "/sbin/ping" (or "ping" if found on path) verbose Whether or not to show the output of the command. Valid: "true" or "false"
FD_ICMP Uses InetAddress.isReachable() to determine whether a host is up or not. Note that this is only available in JDK 5, so reflection is used to determine whether InetAddress provides such a method. If not, an exception will be thrown at protocol initialization time. The problem with InetAddress.isReachable() is that it may or may not use ICMP in its implementation ! For example, an implementation might try to establish a TCP connection to port 9 (echo service), and - if the echo service is not running - the host would be suspected, although a real ICMP packet would not have suspected the host ! Please check your JDK/OS combo before running this protocol. Properties Name Description bind_addr The network interface to be used for sending ICMP packets, e.g. bind_addr="192.16.8.0.2"
FD_SOCK Failure detection protocol based on a ring of TCP sockets created between group members. Each member in a group connects to its neighbor (last member connects to first) thus forming a ring. Member B is suspected when its neighbor A detects abnormally closed TCP socket (presumably due to a node B crash). However, if a member B is about to leave gracefully, it lets its neighbor A know, so that it does not become suspected. If you are using a multi NIC machine note that JGroups versions prior to 2.2.8 have FD_SOCK implementation that does not assume this possibility. Therefore JVM can possibly select NIC unreachable to its neighbor and setup FD_SOCK server socket on it. Neighbor would be unable to connect to that server socket thus resulting in immediate suspecting of a member. Suspected member is kicked out of the group, tries to rejoin, and thus goes into join/leave loop. JGroups version 2.2.8 introduces srv_sock_bind_addr property so you can specify network interface where FD_SOCK TCP server socket should be bound. This network interface is most likely the same interface used for other JGroups traffic. JGroups versions 2.2.9 and newer consult bind.address system property or you can specify network interface directly as FD_SOCK bind_addr property. Properties Name Description bind_addr The network interface to be used for sending ICMP packets, e.g. bind_addr="192.16.8.0.2"
VERIFY_SUSPECT
Reliable message transmission
pbcast.NAKACK NAKACK provides reliable delivery and FIFO (= First In First Out) properties for messages sent to all nodes in a cluster. Reliable delivery means that no message sent by a sender will ever be lost, as all messages are numbered with sequence numbers (by sender) and retransmission requests are sent to the sender of a message Note that NAKACK can also be configured to send retransmission requests for M to anyone in the cluster, rather than only to the sender of M. if that sequence number is not received. FIFO order means that all messages from a given sender are received in exactly the order in which they were sent. Properties Name Description retransmit_timeout A comma-separated list of milliseconds, e.g. 100,200,400,800,1600. We ask for retransmission of a given message 100ms after it wasn't received, then 200ms, and so on, until we're at 1600ms. From then on, we will send retransmit requests for that message every 100ms, until the message is received, or the original sender crashed. Example: retransmit_timeout="300,600,1200,2400" exponential_backoff Value in milliseconds. If greater than 0, exponential backoff for retransmission is enabled. The value is then the initial value, and we'll double it every time we ask for retransmission, until a max value of 15000ms. If the value is greater than 0, exponential backoff is enabled and retransmit_timeout will be ignored. Note that this property is experimental, and may be removed at any time. Example: exponential_backoff="30" use_stats_for_retransmission Boolean. If true, we ignore both retransmit_timeout and exponential_backoff, and use statistics that we collect during retransmission to compute the ideal retransmission times, based on actual retransmission times. Note that this property is experimental, and may be removed at any time. Example: use_stats_for_retransmission="true" use_mcast_xmit When we get a retransmission request from P for a message M, then we send the retransmitted M to P. However, assuming that many nodes lost M, we might as well send M to the entire cluster, so that we can satisfy many retransmit requests at the same time. Setting this option to true only makes sense for an IP multicast capable transport (e.g. UDP), where we send the retransmitted message one time. Otherwise, e.g. if we use TCP, we send the message N-1 times, one time for each node. Example: use_mcast_xmit="true" use_mcast_xmit_req Similar to use_mcast_xmit, but for requests: if enabled, we'll send a retransmit reuest via a multicast. Setting this option to true only makes sense for an IP multicast capable transport (e.g. UDP), where we send the retransmit requests one time. Otherwise, e.g. if we use TCP, we send the message N-1 times, one time for each node ! Example: use_mcast_xmit_req="true" xmit_from_random_member Instead of sending all retransmit requests to the original sender of a message, we could also pick a random member of the cluster. Setting xmit_from_random_member="true" does this. The advantage is that the retransmission load is distributed more equally across the cluster. The down side is that a random member may or may not yet have received a given message, so it may not be able to satisfy the retransmission request. In this case, it may take a few retransmission requests to different members in the cluster to finally get the requested message. Note that discard_delivered_msgs must be set to false if this option is enabled. Example: xmit_from_random_member="true" discard_delivered_msgs If set to true, messages received from other members are not buffered until stability purges them, but instead discarded immediately. This means that retransmission requests can only be satisfied by the original sender (and xmit_from_random_member won't work). However, since we don't have to wait for stability to kick in and purge messages seen by everyone, we conserve memory. Example: discard_delivered_msgs="true" max_xmit_buf_size Number of entries to keep in the retransmission tables. Since messages are buffered in the retransmission tables, we have to wait for a given message M to be delivered until all messages preceeding M have been delivered as well. Using max_xmit_buf_size makes the retransmission tables bounded, and older messages will get discarded if they haven't been received when max_xmit_buf_size elements are exceeded. Note that setting this property to true will cause message loss in the case where a message is lost and not retransmitted because the retransmission buffer is full ! eager_lock_release By default, we release the lock on the sender in up() after the up() method call passed up the stack returns. However, with eager_lock_release enabled (default), we release the lock as soon as the application calls Channel.down() within the receive() callback. This leads to issues as the one described in http://jira.jboss.com/jira/browse/JGRP-656. Note that ordering is still correct , but messages from self might get delivered concurrently. This can be turned off by setting eager_lock_release to false.
SMACK
UNICAST
Fragmentation
FRAG and FRAG2
Ordering (FIFO covered by NAKACK)
Total Order (SEQUENCER)
Group Membership Group membership takes care of joining new members, handling leave requests by existing members, and handling SUSPECT messages for crashed members, as emitted by failure detection protocols. The algorithm for joining a new member is essentially: - loop - find initial members (discovery) - if no responses: - become singleton group and break out of the loop - else: - determine the coordinator (oldest member) from the responses - send JOIN request to coordinator - wait for JOIN response - if JOIN response received: - install view and break out of the loop - else - sleep for 5 seconds and continue the loop
pbcast.GMS Properties Name Description join_timeout Number of milliseconds to wait for a JOIN response from the coordinator, until we send a new JOIN request. Default=5000 join_retry_timeou Number of ms to wait before sending a new JOIN request leave_timeout Number of ms to wait until a LEAVE response has been received from the coordinator. Once this time has elapsed, we leave anyway. shun merge_leader print_local_addr Whether or not to print to stdout the local address of a newly started member. Default is "true". Example: ------------------------------------------------------- GMS: address is 192.168.5.2:4682 ------------------------------------------------------- merge_timeout digest_timeout view_ack_collection_timeout resume_task_timeout disable_initial_coord This feature is deprecated and will be phased out in 3.0 handle_concurrent_startup Default: "true". Handles concurrent starting of N initial members. Setting it to false is only used for unit tests, where the correctness of the subsequent merge is tested, we don't recommend setting it to false. num_prev_mbrs use_flush flush_timeout reject_join_from_existing_member If we receive a JOIN request from P and P is already in the current membership, then we send back a JOIN response with an error message when this property is set to true (Channel.connect() will fail). Otherwise, we return the current view view_bundling Whether to enable view bundling (default is true). View bundling means that multiple view-affecting requests to GNS, such as JOIN, LEAVE or SUSPECT, are bundled for a number of milliseconds in order to avoid having to generate one view per request. This is especially interesting if we have many members joining or leaving a group at the same time. max_bundling_time Max number of milliseconds to wait for subsequent JOIN/LEAVE/SUSPECT requests (default is 50). Therefore, when 5 JOIN or LEAVE requests are received within 50ms, only 1 view will be generated
Disabling the initial coordinator Consider the following situation: a new member wants to join a group. The prodedure to do so is: Multicast an (unreliable) discovery request (ping) Wait for n responses or m milliseconds (whichever is first) Every member responds with the address of the coordinator If the initial responses are > 0: determine the coordinator and start the JOIN protocolg If the initial response are 0: become coordinator, assuming that no one else is out there However, the problem is that the initial mcast discovery request might get lost, e.g. when multiple members start at the same time, the outgoing network buffer might overflow, and the mcast packet might get dropped. Nobody receives it and thus the sender will not receive any responses, resulting in an initial membership of 0. This could result in multiple coordinators, and multiple subgroups forming. How can we overcome this problem ? There are 3 solutions: Increase the timeout, or number of responses received. This will only help if the reason of the empty membership was a slow host. If the mcast packet was dropped, this solution won't help Add the MERGE(2) protocol. This doesn't actually prevent multiple initial cordinators, but rectifies the problem by merging different subgroups back into one. Note that this involves state merging which needs to be done by the application. (new) Prevent members from becoming coordinator on initial startup. This solution is applicable when we know which member is going to be the initial coordinator of a fresh group. We don't care about afterwards, then coordinatorship can migrate to another member. In this case, we configure the member that is always supposed to be started first with disable_initial_coord=false (the default) and all other members with disable_initial_coord=true.This works as described below. When the initial membership is received, and is null, and the property disable_initial_coord is true, then we just continue in the loop and retry receving the initial membership (until it is non-null). If the property is false, we are allowed to become coordinator, and will do so. Note that - if a member is started as first member of a group - but its property is set to true, then it will loop until another member whose disable_initial_coord property is set to false, is started.
Security
ENCRYPT
AUTH
State Transfer
pbcast.STATE_TRANSFER
pbcast.STREAMING_STATE_TRANSFER
Overview In order to transfer application state to a joining member of a group pbcast.STATE_TRANSFER has to load entire state into memory and send it to a joining member. Major limitation of this approach is that the state transfer that is very large (>1Gb) would likely result in OutOfMemoryException. In order to alleviate this problem a new state transfer methodology, based on a streaming state transfer, was introduced in JGroups 2.4 Streaming state transfer supports both partial and full state transfer. Streaming state transfer provides an InputStream to a state reader and an OutputStream to a state writer. OutputStream and InputStream abstractions enable state transfer in byte chunks thus resulting in smaller memory requirements. For example, if application state consists a huge DOM tree, whose aggregate size is 2GB (and which has partly been passivated to disk), then the state provider (ie. the coordinator) can simply iterate over the DOM tree (activating the parts which have been passivated out to disk), and write to the OutputStream as it traverses the tree. The state receiver will simply read from the InputStream and reconstruct the tree on its side, possibly again passivating parts to disk. Rather than having to provide a 2GB byte[] buffer, streaming state transfer transfers the state in chunks of N bytes where N is user configurable.
API Streaming state transfer, just as regular byte based state transfer, can be used in both pull and push mode. Similarly to the current getState and setState methods of org.jgroups.MessageListener, application interested in streaming state transfer in a push mode would implement streaming getState method(s) by sending/writing state through a provided OutputStream reference and setState method(s) by receiving/reading state through a provided InputStream reference. In order to use streaming state transfer in a push mode, existing ExtendedMessageListener has been expanded to include additional four methods: public interface ExtendedMessageListener { /*non-streaming callback methods ommitted for clarity*/ /** * Allows an application to write a state through a provided OutputStream. * An application is obligated to always close the given OutputStream reference. * * @param ostream the OutputStream * @see OutputStream#close() */ public void getState(OutputStream ostream); /** * Allows an application to write a partial state through a provided OutputStream. * An application is obligated to always close the given OutputStream reference. * * @param state_id id of the partial state requested * @param ostream the OutputStream * * @see OutputStream#close() */ public void getState(String state_id, OutputStream ostream); /** * Allows an application to read a state through a provided InputStream. * An application is obligated to always close the given InputStream reference. * * @param istream the InputStream * @see InputStream#close() */ public void setState(InputStream istream); /** * Allows an application to read a partial state through a provided InputStream. * An application is obligated to always close the given InputStream reference. * * @param state_id id of the partial state requested * @param istream the InputStream * * @see InputStream#close() */ public void setState(String state_id, InputStream istream); } For a pull mode (when application uses channel.receive() to fetch events) two new event classes will be introduced: StreamingGetStateEvent StreamingSetStateEvent These two events/classes are very similar to existing GetStateEvent and SetStateEvent but introduce a new field; StreamingGetStateEvent has an OutputStream and StreamingSetStateEvent has an InputStream. The following code snippet demonstrates how to pull events from a channel, processing StreamingGetStateEvent and sending hypothetical state through a provided OutputStream reference. Handling of StreamingSetStateEvent is analogous to this example: ... Object obj=channel.receive(0); if(obj instanceof StreamingGetStateEvent) { StreamingGetStateEvent evt=(StreamingGetStateEvent)obj; OutputStream oos = null; try { oos = new ObjectOutputStream(evt.getArg()); oos.writeObject(state); oos.flush(); } catch (Exception e) {} finally{ try { oos.close(); } catch (IOException e) { System.err.println(e); } } } ... API that initiates state transfer on a JChannel level has the following methods: public boolean getState(Address target,long timeout)throws ChannelNotConnectedException,ChannelClosedException; public boolean getState(Address target,String state_id,long timeout)throws ChannelNotConnectedException,ChannelClosedException; Introduction of STREAMING_STATE_TRANSFER does not change the current API.
Configuration State transfer type choice is static, implicit and mutually exclusive. JChannel cannot use both STREAMING_STATE_TRANSFER and STATE_TRANSFER in one JChannel configuration. STREAMING_STATE_TRANSFER allows the following confguration parameters: Properties Name Description bind_addr The network interface to be used for receiving of state requests, e.g. bind_addr="192.16.8.0.2" start_port Port on the bind_addr network interface to be used for receiving of state requests, e.g. start_port="4444" max_pool Maximum number of threads in the pool providing state requests, default=5, e.g. max_pool="10" pool_thread_keep_alive Pool thread keep alive in msec, default=30000, e.g. pool_thread_keep_alive="60000" use_reading_thread Use separate thread for reading state, default=false, e.g. use_reading_thread="true" socket_buffer_size Chunk size used for state transfer, default=8192 e.g. socket_buffer_size="32768"
Other considerations Threading model used for state writing in a member providing state and state reading in a member receiving a state is tunable. For state provider thread pool is used to spawn threads providing state. Thus member providing state, in a push mode, will be able to concurrently serve N state requests where N is max_threads configuration parameter of the thread pool. If there are no further state transfer requests pool threads will be automatically reaped after configurable "pool_thread_keep_alive" timeout expires. For a channel operating in the push mode state reader channel can read state by piggybacking on jgroups protocol stack thread or optionally use a separate thread. State reader should use a separate thread if state reading is expensive (eg. large state, serialization) thus potentially affecting liveness of jgroups protocol thread. Since most state transfers are very short (<2-3 sec) by default we do not use a separate thread.
Flow control Flow control takes care of adjusting the rate of a message sender to the rate of the slowest receiver over time. If a sender continuously sends messages at a rate that is faster than the receiver(s), the receivers will either queue up messages, or the messages will get discarded by the receiver(s), triggering costly retransmissions. In addition, there is spurious traffic on the cluster, causing even more retransmissions. Flow control throttles the sender so the receivers are not overrun with messages.
FC FC uses a credit based system, where each sender has max_credits credits and decrements them whenever a message is sent. The sender blocks when the credits fall below 0, and only resumes sending messages when it receives a replenishment message from the receivers. The receivers maintain a table of credits for all senders and decrement the given sender's credits as well, when a message is received. When a sender's credits drops below a threshold, the receiver will send a replenishment message to the sender. The threshold is defined by min_bytes or min_threshold. Properties Name Description max_credits Max number of bytes the sender is allowed to send before blocking until replenishments from the receivers are received min_credits Min credits in bytes. If the available credits for a sender drop below this value, a receiver will send a replenishment message to the sender min_threshold Same as min_credits, but expressed as a percentage of max_credits, e.g. 0.1 (10% of max_credits) max_block_time The maximum time in milliseconds a sender can be blocked. After this time has elapsed, and no replenishment has been received, the sender replenishes itself and continues sending. Set it to 0 to prevent this
SFC A simplified version of FC. FC can actually still overrun receivers when the transport's latency is very small. SFC is a simple flow control protocol for group (= multipoint) messages. Every sender has max_credits bytes for sending multicast messages to the group. Every multicast message (we don't consider unicast messages) decrements max_credits by its size. When max_credits falls below 0, the sender asks all receivers for new credits and blocks until *all* credits have been received from all members. When the receiver receives a credit request, it checks whether it has received max_credits bytes from the requester since the last credit request. If yes, it sends new credits to the requester and resets the max_credits for the requester. Else, it takes a note of the credit request from P and - when max_credits bytes have finally been received from P - it sends the credits to P and resets max_credits for P. The maximum amount of memory for received messages is therefore <number of senders> * max_credits. The relationship with STABLE is as follows: when a member Q is slow, it will prevent STABLE from collecting messages above the ones seen by Q (everybody else has seen more messages). However, because Q will *not* send credits back to the senders until it has processed all messages worth max_credits bytes, the senders will block. This in turn allows STABLE to progress and eventually garbage collect most messages from all senders. Therefore, SFC and STABLE complement each other, with SFC blocking senders so that STABLE can catch up. Properties Name Description max_credits Max number of bytes the sender is allowed to send before blocking until replenishments from the receivers are received
Message stability To serve potential retransmission requests, a member has to store received messages until it is known that every member in the cluster has received them. Message stability for a given message M means that M has been seen by everyone in the cluster. The stability protocol periodically (or when a certain number of bytes have been received) initiates a consensus protocol, which multicasts a stable message containing the highest message numbers for a given member. This is called a digest. When everyone has received everybody else's stable messages, a digest is computed which consists of the minimum sequence numbers of all received digests so far. This is the stability vector, and contain only message sequence numbers that have been seen by everyone. This stability vector is the broadcast to the group and everyone can remove messages from their retransmission tables whose sequence numbers are smaller than the ones received in the stability vector. These messages can then be garbage collected.
STABLE Properties Name Description desired_avg_gossip Interval in milliseconds at which a stable message is broadcast to the cluster. This is randomized to prevent all members from sending the message at the same time. If set to 0, it is disabled. max_bytes Maximum number of bytes received after which a stable message is broadcast to the cluster. A high number means fewer stability rounds which purge more messages. A smaller value means a higher frequency of stability rounds which purge fewer messages. This is similar to garbage collection in the JVM. stability_delay When sending a stability message, we wait a randomized time between 1 and stability_delay milliseconds before sending it. If, when about to send the message, a stability message is received, we cancel our own message. This is to prevent everyone from sending the message at the same time.
Diagnostics
PERF
SIZE
TRACE
PRINTOBJS
Misc
COMPRESS
pbcast.FLUSH Flushing forces group members to send all their pending messages prior to a certain event. The process of flushing acquiesces the cluster so that state transfer or a join can be done. It is also called the stop-the-world model as nobody will be able to send messages while a flush is in process. Flush is used: State transfer When a member requests state transfer it tells everyone to stop sending messages and waits for everyone's ack. Then it asks the application for its state and ships it back to the requester. After the requester has received and set the state successfully, the requester tells everyone to resume sending messages. View changes (e.g.a join). Before installing a new view V2, flushing would ensure that all messages *sent* in the current view V1 are indeed *delivered* in V1, rather than in V2 (in all non-faulty members). This is essentially Virtual Synchrony. FLUSH is designed as another protocol positioned just below the channel, e.g. above STATE_TRANSFER and FC. STATE_TRANSFER and GMS protocol request flush by sending a SUSPEND event up the stack, where it is handled by the FLUSH protcol. The SUSPEND_OK ack sent back by the FLUSH protocol let's the caller know that the flush has completed. When done (e.g. view was installed or state transferred), the protocol sends up a RESUME event, which will allow everyone in the cluster to resume sending. Channel can be notified that FLUSH phase has been started by turning channel block option on. By default it is turned off. If channel blocking is turned on FLUSH notifies application layer that channel has been blocked by sending EVENT.BLOCK event. Channel responds by sending EVENT.BLOCK_OK event down to FLUSH protocol. We recommend turning on channel block notification only if channel is used in push mode. In push mode application that uses channel can perform block logic by implementing MembershipListener.block() callback method. Properties Name Description timeout Maximum time that FLUSH.down() will be blocked before being unblocked. Should be sufficient enough to allow large state transfers,default=8000 msec block_timeout Maximum amount of time that FLUSH will be waiting for EVENT.BLOCK_OK once Event.BLOCK has been sent to application level, default=10000 msec
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/api.xml0000644000175000017500000020200311366547366023722 0ustar twernertwerner API This chapter explains the classes available in JGroups that will be used by applications to build reliable group communication applications. The focus is on creating and using channels. Information in this document may not be up-to-date, but the nature of the classes in the JGroups toolkit described here is the same. For the most up-to-date information refer to the Javadoc-generated documentation in the doc/javadoc directory. All of the classes discussed below reside in the org.jgroups package unless otherwise mentioned.
Utility classes The org.jgroups.util.Util class contains a collection of useful functionality which cannot be assigned to any particular other package.
objectToByteBuffer(), objectFromByteBuffer() The first method takes an object as argument and serializes it into a byte buffer (the object has to be serializable or externalizable). The byte array is then returned. This method is often used to serialize objects into the byte buffer of a message. The second method returns a reconstructed object from a buffer. Both methods throw an exception if the object cannot be serialized or unseriali
printMessage() Prints the message given as argument in readable format. Returns a string.
activeThreads() Returns a strings containing a pretty-printed list of currently running threads.
printMembers() Given a list of member addresses, pretty-prints the members and returns a string.
Interfaces These interfaces are used with some of the APIs presented below, therefore they are listed first.
Transport Interface Transport looks as follows: public interface Transport { public void send(Message msg) throws Exception; public Object receive(long timeout) throws Exception; } It defines a very small subset of the functionality of a channel, essentially only the methods for sending and receiving messages. There are a number of classes that implement Transport , among others Channel . Many building blocks (see ) require nothing else than a bare-bone facility to send and receive messages; therefore the Transport interface was created. It increases the genericness and portability of building blocks: being so simple, the Transport interface can easily be ported to a different toolkit, without requiring any modifications to building blocks.
MessageListener Contrary to the pull-style of channels, some building blocks (e.g. PullPushAdapter ) provide an event-like push-style message delivery model. In this case, the entity to be notified of message reception needs to provide a callback to be invoked whenever a message has been received. The MessageListener interface below provides a method to do so: public interface MessageListener { public void receive(Message msg); byte[] getState(); void setState(byte[] state); } Method receive() will be called when a message is received. The getState() and setState() methods are used to fetch and set the group state (e.g. when joining). Refer to for a discussion of state transfer.
ExtendedMessageListener JGroups release 2.3 introduces ExtendedMessageListener enabling partial state transfer (refer to ) while release 2.4 further expands ExtendedMessageListener with streaming state transfer callbacks: public interface ExtendedMessageListener extends MessageListener { byte[] getState(String state_id); void setState(String state_id, byte[] state); /*** since JGroups 2.4 *****/ void getState(OutputStream ostream); void getState(String state_id, OutputStream ostream); void setState(InputStream istream); void setState(String state_id, InputStream istream); } Method receive() will be called when a message is received. The getState() and setState() methods are used to fetch and set the group state (e.g. when joining). Refer to for a discussion of state transfer.
MembershipListener The MembershipListener interface is similar to the MessageListener interface above: every time a new view, a suspicion message, or a block event is received, the corresponding method of the class implementing MembershipListener will be called. public interface MembershipListener { public void viewAccepted(View new_view); public void suspect(Object suspected_mbr); public void block(); } Oftentimes the only method containing any functionality will be viewAccepted() which notifies the receiver that a new member has joined the group or that an existing member has left or crashed. The suspect() callback is invoked by JGroups whenever a member if suspected of having crashed, but not yet excluded It could be that the member is suspected falsely, in which case the next view would still contain the suspected member (there is currently no unsuspect() method . The block() method is called to notify the member that it will soon be blocked sending messages. This is done by the FLUSH protocol, for example to ensure that nobody is sending messages while a state transfer is in progress. When block() returns, any thread sending messages will be blocked, until FLUSH unblocks the thread again, e.g. after the state has been transferred successfully. Therefore, block() can be used to send pending messages or complete some other work. However, sending of messages should be done on a different thread, e.g. the current thread blocks on a mutex, starts a different thread which notifies the mutex once the work has been done. Note that block() should take a small amount of time to complete, otherwise the entire FLUSH protocol is blocked.
ExtendedMembershipListener The ExtendedMembershipListener interface extends MembershipListener: public interface ExtendedMembershipListener extends MembershipListener { public void unblock(); } The unblock() method is called to notify the member that the flush protocol has completed and the member can resume sending messages. If the member did not stop sending messages on block(), FLUSH simply blocked them and will resume, so no action is required from a member. Implementation of the unblock() callback is optional.
ChannelListener public interface ChannelListener { void channelConnected(Channel channel); void channelDisconnected(Channel channel); void channelClosed(Channel channel); void channelShunned(); void channelReconnected(Address addr); } A class implementing ChannelListener can use the Channel.setChannelListener() method to register with a channel to obtain information about state changes in a channel. Whenever a channel is closed, disconnected or opened a callback will be invoked.
Receiver public interface Receiver extends MessageListener, MembershipListener { } A Receiver can be used to receive all relevant messages and view changes in push-style; rather than having to pull these events from a channel, they will be dispatched to the receiver as soon as they have been received. This saves one thread (application thread, pulling messages from a channel, or the PullPushAdapter thread
ExtendedReceiver public interface ExtendedReceiver extends ExtendedMessageListener, MembershipListener { } This is a receiver who will be able to handle partial state transfer
Merging of Extended interfaces with their super interfaces The Extended- interfaces (ExtendedMessageListener, ExtendedReceiver) will be merged with their parents in the 3.0 release of JGroups. The reason is that this will create an API backwards incompatibility, which we didn't want to introduce in the 2.x series.
Address Each member of a group has an address, which uniquely identifies the member. The interface for such an address is Address, which requires concrete implementations to provide methods for comparison and sorting of addresses, and for determination whether the address is a multicast address. JGroups addresses have to implement the following interface: public interface Address extends Externalizable, Comparable, Cloneable { boolean isMulticastAddress(); int compareTo(Object o) throws ClassCastException; boolean equals(Object obj); int hashCode(); String toString(); } Actual implementations of addresses are often generated by the bottommost protocol layer (e.g. UDP or TCP). This allows for all possible sorts of addresses to be used with JGroups, e.g. ATM. In JChannel, it is the IP address of the host on which the stack is running and the port on which the stack is receiving incoming messages; it is represented by the concrete class org.jgroups.stack.IpAddress . Instances of this class are only used within the JChannel protocol stack; users of a channel see addresses (of any kind) only as Addresses . Since an address uniquely identifies a channel, and therefore a group member, it can be used to send messages to that group member, e.g. in Messages (see next section).
Message Data is sent between members in the form of messages ( Message ). A message can be sent by a member to a single member , or to all members of the group of which the channel is an endpoint. The structure of a message is shown in .
Structure of a message
A message contains 5 fields: Destination address The address of the receiver. If null , the message will be sent to all current group members Source address The address of the sender. Can be left null , and will be filled in by the transport protocol (e.g. UDP) before the message is put on the network Flags This is one byte used for flags. The currently recognized flags are OOB, LOW_PRIO and HIGH_PRIO. See the discussion on the concurrent stack for OOB. Payload The actual data (as a byte buffer). The Message class contains convenience methods to set a serializable object and to retrieve it again, using serialization to convert the object to/from a byte buffer. Headers A list of headers that can be attached to a message. Anything that should not be in the payload can be attached to a message as a header. Methods putHeader() , getHeader() and removeHeader() of Message can be used to manipulate headers. A message is similar to an IP packet and consists of the payload (a byte buffer) and the addresses of the sender and receiver (as Addresses). Any message put on the network can be routed to its destination (receiver address), and replies can be returned to the sender's address. A message usually does not need to fill in the sender's address when sending a message; this is done automatically by the protocol stack before a message is put on the network. However, there may be cases, when the sender of a message wants to give an address different from its own, so that for example, a response should be returned to some other member. The destination address (receiver) can be an Address, denoting the address of a member, determined e.g. from a message received previously, or it can be null , which means that the message will be sent to all members of the group. A typical multicast message, sending string "Hello" to all members would look like this: Message msg=new Message(null, null, "Hello".getBytes()); channel.send(msg);
View A View ( View ) is a list of the current members of a group. It consists of a ViewId , which uniquely identifies the view (see below), and a list of members. Views are set in a channel automatically by the underlying protocol stack whenever a new member joins or an existing one leaves (or crashes). All members of a group see the same sequence of views. Note that there is a comparison function which orders all the members of a group in the same way. Usually, the first member of the list is the coordinator (the one who emits new views). Thus, whenever the membership changes, every member can determine the coordinator easily and without having to contact other members. The code below shows how to send a (unicast) message to the first member of a view (error checking code omitted): View myview=channel.getView(); Address first=myview.getMembers().first(); Message msg=new Message(first, null, "Hello world"); channel.send(msg); Whenever an application is notified that a new view has been installed (e.g. by MembershipListener.viewAccepted() or Channel.receive() ), the view is already set in the channel. For example, calling Channel.getView() in a viewAccepted() callback would return the same view (or possibly the next one in case there has already been a new view !).
ViewId The ViewId is used to uniquely number views. It consists of the address of the view creator and a sequence number. ViewIds can be compared for equality and put in a hashtable as they implement equals() and hashCode() methods.
MergeView Whenever a group splits into subgroups, e.g. due to a network partition, and later the subgroups merge back together, a MergeView instead of a View will be received by the application. The MergeView class is a subclass of View and contains as additional instance variable the list of views that were merged. As an example if the group denoted by view V1:(p,q,r,s,t) split into subgroups V2:(p,q,r) and V2:(s,t) , the merged view might be V3:(p,q,r,s,t) . In this case the MergeView would contains a list of 2 views: V2:(p,q,r) and V2:(s,t) .
Membership This class can be used for keeping rack of members instead of a Vector class. It adds several functions, such as duplicate elimination, merging with other Membership instances and sorting.
Channel In order to join a group and send messages, a process has to create a channel. A channel is like a socket. When a client connects to a channel, it gives the the name of the group it would like to join. Thus, a channel is (in its connected state) always associated with a particular group. The protocol stack takes care that channels with the same group name find each other: whenever a client connects to a channel given group name G, then it tries to find existing channels with the same name, and joins them, resulting in a new view being installed (which contains the new member). If no members exist, a new group will be created. A state transition diagram for the major states a channel can assume are shown in .
Channel states
When a channel is first created, it is in the unconnected state. An attempt to perform certain operations which are only valid in the connected state (e.g. send/receive messages) will result in an exception. After a successful connection by a client, it moves to the connected state. Now channels will receive messages, views and suspicions from other members and may send messages to other members or to the group. Getting the local address of a channel is guaranteed to be a valid operation in this state (see below). When the channel is disconnected, it moves back to the unconnected state. Both a connected and unconnected channel may be closed, which makes the channel unusable for further operations. Any attempt to do so will result in an exception. When a channel is closed directly from a connected state, it will first be disconnected, and then closed. The methods available for creating and manipulating channels are discussed now.
Creating a channel A channel can be created in two ways: an instance of a subclass of Channel is created directly using its public constructor (e.g. new JChannel() ), or a channel factory is created, which -- upon request -- creates instances of channels. We will only look at the first method of creating channel: by direct instantiation. Note that instantiation may differ between the various channel implementations. As example we will look at JChannel . The public constructor of JChannel looks as follows: public JChannel(Object properties) throws ChannelException {} It creates an instance of JChannel . The properties argument defines the composition of the protocol stack (number and type of layers, parameters for each layer, and their order). For JChannel, this has to be a String. An example of a channel creation is: String props="UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=32):" + "PING(timeout=3000;num_initial_members=6):" + "FD(timeout=5000):" + "VERIFY_SUSPECT(timeout=1500):" + "pbcast.STABLE(desired_avg_gossip=10000):" + "pbcast.NAKACK(gc_lag=10;retransmit_timeout=3000):" + "UNICAST(timeout=5000;min_wait_time=2000):" + "FRAG:" + "pbcast.GMS(initial_mbrs_timeout=4000;join_timeout=5000;" + "shun=false;print_local_addr=false)"; JChannel channel; try { channel=new JChannel(props); } catch(Exception ex) { // channel creation failed } The argument is a colon-delimited string of protocols, specified from bottom to top (left to right). The example properties argument will be used to create a protocol stack that uses IP Multicast (UDP) as bottom protocol, the PING protocol to locate the initial members, FD for failure detection, VERIFY_SUSPECT for double-checking of suspected members, STABLE for garbage collection of messages received by all members, NAKACK for lossless delivery of multicast messages, UNICAST for lossless delivery of unicast messages and GMS for group membership (handling of join or leave requests). If the properties argument is null, the default properties will be used. An exception will be thrown if the channel cannot be created. Possible causes include protocols that were specified in the property argument, but were not found, or wrong parameters to protocols.
Using XML to define a protocol stack In version 2.0 of JGroups an XML-based scheme to define protocol stacks was introduced. Instead of specifying a string containing the protocol spec, an URL pointing to a valid protocol stack definition can be given. For example, the Draw demo can be launched as follows: java org.javagroups.demos.Draw -props file:/home/bela/vsync.xml or java org.javagroups.demos.Draw -props http://www.jgroups.org/udp.xml In the latter case, an application downloads its protocol stack specification from a server, which allows for central administration of application properties. A sample XML configuration looks like this (edited from udp.xml): <config> <UDP mcast_addr="${jgroups.udp.mcast_addr:228.10.10.10}" mcast_port="${jgroups.udp.mcast_port:45588}" discard_incompatible_packets="true" max_bundle_size="60000" max_bundle_timeout="30" ip_ttl="${jgroups.udp.ip_ttl:2}" enable_bundling="true" use_concurrent_stack="true" thread_pool.enabled="true" thread_pool.min_threads="1" thread_pool.max_threads="25" thread_pool.keep_alive_time="5000" thread_pool.queue_enabled="false" thread_pool.queue_max_size="100" thread_pool.rejection_policy="Run" oob_thread_pool.enabled="true" oob_thread_pool.min_threads="1" oob_thread_pool.max_threads="8" oob_thread_pool.keep_alive_time="5000" oob_thread_pool.queue_enabled="false" oob_thread_pool.queue_max_size="100" oob_thread_pool.rejection_policy="Run"/> <PING timeout="2000" num_initial_members="3"/> <MERGE2 max_interval="30000" min_interval="10000"/> <FD_SOCK/> <FD timeout="10000" max_tries="5" shun="true"/> <VERIFY_SUSPECT timeout="1500" /> <BARRIER /> <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0" retransmit_timeout="300,600,1200,2400,4800" discard_delivered_msgs="true"/> <UNICAST timeout="300,600,1200,2400,3600"/> <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000" max_bytes="400000"/> <VIEW_SYNC avg_send_interval="60000" /> <pbcast.GMS print_local_addr="true" join_timeout="3000" shun="false" view_bundling="true"/> <FC max_credits="20000000" min_threshold="0.10"/> <FRAG2 frag_size="60000" /> <pbcast.STATE_TRANSFER /> </config> A stack is wrapped by <config> and </config> elements and lists all protocols from bottom (UDP) to top (STATE_TRANSFER). Each element defines one protocol. Each protocol is implemented as a Java class. When a protocol stack is created based on the above XML configuration, the first element ("UDP") becomes the bottom-most layer, the second one will be placed on the first, etc: the stack is created from the bottom to the top. Each element has to be the name of a Java class that resides in the org.jgroups.stack.protocols package. Note that only the base name has to be given, not the fully specified class name ( UDP instead of org.jgroups.stack.protocols.UDP ). If the protocol class is not found, JGroups assumes that the name given is a fully qualified classname and will therefore try to instantiate that class. If this does not work an exception is thrown. This allows for protocol classes to reside in different packages altogether, e.g. a valid protocol name could be com.sun.eng.protocols.reliable.UCAST . Each layer may have zero or more arguments, which are specified as a list of name/value pairs in parentheses directly after the protocol name. In the example above, UDP is configured with some options, one of them being the IP multicast address (mcast_addr) which is set to 228.10.10.10, or to the value of the system property jgroups.udp.mcast_addr, if set. Note that all members in a group have to have the same protocol stack.
Setting options A number of options can be set in a channel. To do so, the following method is used: public void setOpt(int option, Object value); Arguments are the options number and a value. The following options are currently recognized: Channel.BLOCK The argument is a boolean object. If true, block messages will be received. If this option is set to true, views will also be set to true. Default is false. Channel.LOCAL Local delivery. The argument is a boolean value. If set to true, a member will receive all messages it sent to itself. Otherwise, all messages sent by itself will be discarded. This option allows to send messages to the group, without receiving a copy. Default is true (members will receive their own copy of messages multicast to the group). Channel.AUTO_RECONNECT When set to true, a shunned channel will leave the group and then try to automatically re-join. Default is false Channel.AUTO_GETSTATE When set to true a shunned channel, after reconnection, will attempt to fetch the state from the coordinator. This requires AUTO_RECONNECT to be true as well. Default is false. The equivalent method to get options is getOpt() : public Object getOpt(int option); Given an option, the current value of the option is returned.
Connecting to a channel When a client wants to join a group, it connects to a channel giving the name of the group to be joined: public void connect(String clustername) throws ChannelClosed; The cluster name is a string, naming the cluster to be joined. All channels that are connected to the same name form a cluster. Messages multicast on any channel in the cluster will be received by all members (including the one who sent it Local delivery can be turned on/off using setOpt() . ). The method returns as soon as the group has been joined successfully. If the channel is in the closed state (see ), an exception will be thrown. If there are no other members, i.e. no other client has connected to a group with this name, then a new group is created and the member joined. The first member of a group becomes its coordinator . A coordinator is in charge of multicasting new views whenever the membership changes This is managed internally however, and an application programmer does not need to be concerned about it. .
Connecting to a channel and getting the state in one operation Clients can also join a cluster group and fetch cluster state in one operation. The best way to conceptualize connect and fetch state connect method is to think of it as an invocation of regular connect and getstate methods executed in succession. However, there are several advantages of using connect and fetch state connect method over regular connect. First of all, underlying message exchange is heavily optimized, especially if the flush protocol is used in the stack. But more importantly, from clients perspective, connect and join operations become one atomic operation. public void connect(string cluster_name, address target, string state_id, long timeout) throws channelexception; Just as in regular connect method cluster name represents a cluster to be joined. Address parameter indicates a cluster member to fetch state from. Null address parameter indicates that state should be fetched from the cluster coordinator. If state should be fetched from a particular member other than coordinator clients can provide an address of that member. State id used for partial state transfer while timeout bounds entire join and fetch operation.
Getting the local address and the group name Method getLocalAddress() returns the local address of the channel. In the case of JChannel , the local address is generated by the bottom-most layer of the protocol stack when the stack is connected to. That means that -- depending on the channel implementation -- the local address may or may not be available when a channel is in the unconnected state. public Address getLocalAddress(); Method getClusterlName() returns the name of the cluster in which the channel is a member: public String getClusterName(); Again, the result is undefined if the channel is in the unconnected or closed state.
Getting the current view The following method can be used to get the current view of a channel: public View getView(); This method does not retrieve a new view (message) from the channel, but only returns the current view of the channel. The current view is updated every time a view message is received: when method receive() is called, and the return value is a view, before the view is returned, it will be installed in the channel, i.e. it will become the current view. Calling this method on an unconnected or closed channel is implementation defined. A channel may return null, or it may return the last view it knew of.
Sending a message Once the channel is connected, messages can be sent using the send() methods: public void send(Message msg) throws ChannelNotConnected, ChannelClosed; public void send(Address dst, Address src, Object obj) throws ChannelNotConnected, ChannelClosed; The first send() method has only one argument, which is the message to be sent. The message's destination should either be the address of the receiver (unicast) or null (multicast). When it is null, the message will be sent to all members of the group (including itself). The source address may be null; if it is, it will be set to the channel's address (so that recipients may generate a response and send it back to the sender). The second send() method is a helper method and uses the former method internally. It requires the address of receiver and sender and an object (which has to be serializable), constructs a Message and sends it. If the channel is not connected, or was closed, an exception will be thrown upon attempting to send a message. Here's an example of sending a (multicast) message to all members of a group: Hashtable data; // any serializable data try { channel.send(null, null, data); } catch(Exception ex) { // handle errors } The null value as destination address means that the message will be sent to all members in the group. The sender's address will be filled in by the bottom-most protocol. The payload is a hashtable, which will be serialized into the message's buffer and unserialized at the receiver's end. Alternatively, any other means of generating a byte buffer and setting the message's buffer to it (e.g. using Message.setBuffer()) would also work. Here's an example of sending a (unicast) message to the first member (coordinator) of a group: Address receiver; Message msg; Hashtable data; try { receiver=channel.getView().getMembers().first(); channel.send(receiver, null, data); } catch(Exception ex) { // handle errors } It creates a Message with a specific address for the receiver (the first member of the group). Again, the sender's address can be left null as it will be filled in by the bottom-most protocol.
Receiving a message Method receive() is used to receive messages, views, suspicions and blocks: public Object receive(long timeout) throws ChannelNotConnected, ChannelClosed, Timeout; A channel receives messages asynchronously from the network and stores them in a queue. When receive() is called, the next available message from the top of that queue is removed and returned. When there are no messages on the queue, the method will block. If timeout is greater than 0, it will wait the specified number of milliseconds for a message to be received, and throw a TimeoutException exception if none was received during that time. If the timeout is 0 or negative, the method will wait indefinitely for the next available message. Depending on the channel options (see ), the following types of objects may be received: Message A regular message. To send a response to the sender, a new message can be created. Its destination address would be the received message's source address. Method Message.makeReply() is a helper method to create a response. View A view change, signalling that a member has joined, left or crashed. The application may or may not perform some action upon receiving a view change (e.g. updating a GUI object of the membership, or redistributing a load-balanced collaborative task to all members). Note that a longer action, or any action that blocks should be performed in a separate thread. A MergeView will be received when 2 or more subgroups merged into one (see for details). Here, a possible state merge by the application needs to be done in a separate thread. SuspectEvent Notification of a member that is suspected. Method SuspectEvent.getMember() retrieves the address of the suspected member. Usually this message will be followed by a view change. BlockEvent The application has to stop sending messages. When the application has stopped sending messages, it needs to acknowledge this message with a Channel.blockOk() method. The BlockEvent reception can be used to complete pending tasks, e.g. send pending messages, but once Channel.blockOk() has been called, all threads that send messages (calling Channel.send() or Channel.down()) will be blocked until FLUSH unblocks them. UnblockEvent The application can resume sending messages. Any previously messages blocked by FLUSH will be unblocked; when the UnblockEvent is received the channel has already been unblocked. GetStateEvent Received when the application's current state should be saved (for a later state transfer. A copy of the current state should be made (possibly wrapped in a synchronized statement and returned calling method Channel.returnState() . If state transfer events are not enabled on the channel (default), then this event will never be received. This message will only be received with the Virtual Synchrony suite of protocols (see the Programmer's Guide). StreamingGetStateEvent Received when the application's current state should be provided to a state requesting group member. If state transfer events are not enabled on the channel (default), or if channel is not configured with pbcast.STREAMING_STATE_TRANSFER then this event will never be received. SetStateEvent Received as response to a getState(s) method call. The argument contains the state of a single member ( byte[] ) or of all members ( Vector ). Since the state of a single member could also be a vector, the interpretation of the argument is left to the application. StreamingSetStateEvent Received at state requesting member when the state InputStream becomes ready for reading. If state transfer events are not enabled on the channel (default), or if channel is not configured with pbcast.STREAMING_STATE_TRANSFER then this event will never be received. The caller has to check the type of the object returned. This can be done using the instanceof operator, as follows: Object obj; Message msg; View v; obj=channel.receive(0); // wait forever if(obj instanceof Message) msg=(Message)obj; else if(obj instanceof View) v=(View)obj; else ; // don't handle suspicions or blocks If for example views, suspicions and blocks are disabled, then the caller is guaranteed to only receive return values of type Message . In this case, the return value can be cast to a Message directly, without using the instanceof operator. If the channel is not connected, or was closed, a corresponding exception will be thrown. The example below shows how to retrieve the "Hello world" string from a message: Message msg; // received above String s; try { s=(String)msg.getObject(); // error if object not Serializable // alternative: s=new String(msg.getBuffer()); } catch(Exception ex) { // handle errors, e.g. casting error above) } The Message.getObject() method retrieves the message's byte buffer, converts it into a (serializable) object and returns the object.
Using a Receiver to receive messages Instead of pulling messages from a channel in an application thread, a Receiver can be registered with a channel; all received messages, view changes and state transfer requests will invoke callbacks on the registered Receiver: JChannel ch=new JChannel(); ch.setReceiver(new ExtendedReceiverAdapter() { public void receive(Message msg) { System.out.println("received message " + msg); } public void viewAccepted(View new_view) { System.out.println("received view " + new_view); } }); ch.connect("bla"); The ExtendedReceiverAdapter class implements all callbacks of ExtendedReceiver with no-ops, in the example above we override receive() and viewAccepted(). The advantage of using a Receiver is that the application doesn't have to waste 1 thread for pulling messages out of a channel. In addition, the channel doesn't have to maintain an (unbounded) queue of messages/views, which can quickly get large if the receiver cannot process messages fast enough, and the sender keeps sending messages. Note that the Channel.receive() method has been deprecated, and will be removed in 3.0. Use the Receiver interface instead and register as a Receiver with Channel.setReceiver(Receiver r).
Peeking at a message Instead of removing the next available message from the channel, peek() just returns a reference to the next message, but does not remove it. This is useful when one has to check the type of the next message, e.g. whether it is a regular message, or a view change. The signature of this method is not shown here, it is the same as for receive() . The peek() method has also been deprecated, and will be removed in 3.0.
Getting the group's state A newly joined member may wish to retrieve the state of the group before starting work. This is done with getState(). This method returns the state of one member (in most cases, of the oldest member, the coordinator). It returns true or false, depending on whether a valid state could be retrieved. For example, if a member is a singleton, then calling this method would always return false A member will never retrieve the state from itself ! . The actual state is returned as the return value of one of the subsequent receive() calls, in the form of a SetStateEvent object. If getState() returned true, then a valid state (non-null) will be returned, otherwise a null state will be returned. Alternatively if an application uses MembershipListener (see ) instead of pulling messages from a channel, the getState() method will be invoked and a copy of the current state should be returned. By the same token, setting a state would be accomplished by JGroups calling the setState() method of the state fetcher. The reason for not directly returning the state as a result of getState() is that the state has to be returned in the correct position relative to other messages. Returning it directly would violate the FIFO properties of a channel, and state transfer would not be correct. The following code fragment shows how a group member participates in state transfers: channel=new JChannel(); channel.connect("TestChannel"); boolean rc=channel.getState(null, 5000); ... Object state, copy; Object ret=channel.receive(0); if(ret instanceof Message) ; else if(ret instanceof GetStateEvent) { copy=copyState(state); // make a copy so that other msgs don't change the state channel.returnState(Util.objectToByteBuffer(copy)); } else if(ret instanceof SetStateEvent) { SetStateEvent e=(SetStateEvent)ret; state=e.getArg(); } A JChannel has to be created whose stack includes the STATE_TRANSFER or pbcast.STATE_TRANSFER protocols (see ). Method getState() subsequently asks the channel to return the current state. If there is a current state (there may not be any other members in the group !), then true is returned. In this case, one of the subsequent receive() method invocations on the channel will return a SetStateEvent object which contains the current state. In this case, the caller sets its state to the one received from the channel. Method receive() might return a GetStateEvent object, requesting the state of the member to be returned. In this case, a copy of the current state should be made and returned using JChannel.returnState() . It is important to a) synchronize access to the state when returning it since other accesses may modify it while it is being returned and b) make a copy of the state since other accesses after returning the state may still be able to modify it ! This is possible because the state is not immediately returned, but travels down the stack (in the same address space), and a reference to it could still alter it.
Getting the state with a Receiver As an alternative to handling the GetStateEvent and SetStateEvent events, and calling Channel.returnState(), a Receiver could be used. The example above would look like this: class MyReceiver extends ReceiverAdapter { final Map m=new HashMap(); public byte[] getState() { synchronized(m) { // so nobody else can modify the map while we serialize it byte[] state=Util.objectToByteBuffer(m); return state; } } public void setState(byte[] state) { synchronized(m) { Map new_m=(Map)Util.objectFromByteBuffer(state); m.clear(); m.addAll(new_m); } } } channel=new JChannel(); // use default properties (has to include pbcast.STATE_TRANSFER protocol) channel.setReceiver(new MyReceiver()); channel.connect("TestChannel"); boolean rc=channel.getState(null, 5000); In a group consisting of A,B and C, with D joining the group and calling Channel.getState(), the following sequence of callbacks happens: D calls Channel.getState(). The state will be retrieved from the oldest member, A A.MyReceiver.getState() is called. A returns a copy of its hashmap D: getState() returns true D.MyReceiver.setState() is called with the serialized state. D unserializes the state and sets it
Partial state transfer Partial state transfer means that instead of transferring the entire state, we may want to transfer only a substate. For example, with HTTP session replication, a new node in a cluster may want to transfer only the state of a specific session, not all HTTP sessions. This can be done with either the pull or push model. The method to call would be Channel.getState(), including the ID of the substate (a string). In the pull model, GetStateEvent and SetStateEvent have an additional member, state_id, and in the push model, there are 2 additional getState() and setState() callbacks. The example below shows partial state transfer for the push model: class MyReceiver extends ExtendedReceiverAdapter { final Map m=new HashMap(); public byte[] getState() { return getState(null); } public byte[] getState(String substate_id) { synchronized(m) { // so nobody else can modify the map while we serialize it byte[] state=null; if(substate_id == null) { state=Util.objectToByteBuffer(m); } else { Object value=m.get(substate_id); if(value != null) { return Util.objectToByteBuffer(value); } } return state; } } public void setState(byte[] state) { setState(null, state); } public void setState(String substate_id, byte[] state) { synchronized(m) { if(substate_id != null) { Object value=Util.objectFromByteBuffer(state); m.put(substate_id, value); } else { Map new_m=(Map)Util.objectFromByteBuffer(state); m.clear(); m.addAll(new_m); } } } } channel=new JChannel(); // use default properties (has to include pbcast.STATE_TRANSFER protocol) channel.setReceiver(new MyReceiver()); channel.connect("TestChannel"); boolean rc=channel.getState(null, "MyID", 5000); The example shows that the Channel.getState() method specifies the ID of the substate, in this case "MyID". The getState(String substate_id) method checks whether the substate ID is not null, and returns the substate pertaining to the ID, or the entire state if the substate_id is null. The same goes for setting the substate: if setState(String substate_id, byte[] state) has a non-null substate_id, only that part of the current state will be overwritten, otherwise (if null) the entire state will be overwritten.
Streaming state transfer Streaming state transfer allows transfer of application (partial) state without having to load entire state into memory prior to sending it to a joining member. Streaming state transfer is especially useful if the state is very large (>1Gb), and use of regular state transfer would likely result in OutOfMemoryException. Streaming state transfer was introduced in JGroups 2.4. JGroups channel has to be configured with either regular or streaming state transfer. The JChannel API that invokes state transfer (i.e. JChannel.getState(long timeout, Address member)) remains the same. Streaming state transfer, just as regular byte based state transfer, can be used in both pull and push mode. Similarly to the current getState and setState methods of org.jgroups.MessageListener, the application interested in streaming state transfer in a push mode would implement streaming getState method(s) by sending/writing state through a provided OutputStream reference and setState method(s) by receiving/reading state through a provided InputStream reference. In order to use streaming state transfer in a push mode, existing ExtendedMessageListener has been expanded to include additional four methods: public interface ExtendedMessageListener { /*non-streaming callback methods ommitted for clarity*/ void getState(OutputStream ostream); void getState(String state_id, OutputStream ostream); void setState(InputStream istream); void setState(String state_id, InputStream istream); } For a pull mode (when application uses channel.receive() to fetch events) two new event classes will be introduced: StreamingGetStateEvent StreamingSetStateEvent These two events/classes are very similar to existing GetStateEvent and SetStateEvent but introduce a new field; StreamingGetStateEvent has an OutputStream and StreamingSetStateEvent has an InputStream. The following code snippet demonstrates how to pull events from a channel, processing StreamingGetStateEvent and sending hypothetical state through a provided OutputStream reference. Handling of StreamingSetStateEvent is analogous to this example: ... Object obj=channel.receive(0); if(obj instanceof StreamingGetStateEvent) { StreamingGetStateEvent evt=(StreamingGetStateEvent)obj; OutputStream oos = null; try { oos = new ObjectOutputStream(evt.getArg()); oos.writeObject(state); oos.flush(); } catch (Exception e) {} finally { try { oos.close(); } catch (IOException e) { System.err.println(e); } } } ... JGroups has a great flexibility with state transfer methodology by allowing application developers to implement both byte based and streaming based state transfers. Application can, for example, implement streaming and byte based state transfer callbacks and then interchange state transfer protocol in channel configuration to use either streaming or byte based state transfer. However, one cannot configure a channel with both state transfers at the same time and then in runtime choose which particular state transfer type to use.
Disconnecting from a channel Disconnecting from a channel is done using the following method: public void disconnect(); It will have no effect if the channel is already in the disconnected or closed state. If connected, it will remove itself from the group membership. This is done (transparently for a channel user) by sending a leave request to the current coordinator. The latter will subsequently remove the channel's address from its local view and send the new view to all remaining members. After a successful disconnect, the channel will be in the unconnected state, and may subsequently be re-connected to.
Closing a channel To destroy a channel instance (destroy the associated protocol stack, and release all resources), method close() is used: public void close(); It moves the channel to the closed state, in which no further operations are allowed (most throw an exception when invoked on a closed channel). In this state, a channel instance is not considered used any longer by an application and -- when the reference to the instance is reset -- the channel essentially only lingers around until it is garbage collected by the Java runtime system.
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/advanced.xml0000644000175000017500000022352611366547366024733 0ustar twernertwerner Advanced Concepts This chapter discusses some of the more advanced concepts of JGroups with respect to using it and setting it up correctly.
Using multiple channels When using a fully virtual synchronous protocol stack, the performance may not be great because of the larger number of protocols present. For certain applications, however, throughput is more important than ordering, e.g. for video/audio streams or airplane tracking. In the latter case, it is important that airplanes are handed over between control domains correctly, but if there are a (small) number of radar tracking messages (which determine the exact location of the plane) missing, it is not a problem. The first type of messages do not occur very often (typically a number of messages per hour), whereas the second type of messages would be sent at a rate of 10-30 messages/second. The same applies for a distributed whiteboard: messages that represent a video or audio stream have to be delivered as quick as possible, whereas messages that represent figures drawn on the whiteboard, or new participants joining the whiteboard have to be delivered according to a certain order. The requirements for such applications can be solved by using two separate stacks: one for control messages such as group membership, floor control etc and the other one for data messages such as video/audio streams (actually one might consider using one channel for audio and one for video). The control channel might use virtual synchrony, which is relatively slow, but enforces ordering and retransmission, and the data channel might use a simple UDP channel, possibly including a fragmentation layer, but no retransmission layer (losing packets is preferred to costly retransmission). The Draw2Channels demo program (in the org.jgroups.demos package) demonstrates how to use two different channels.
Using the Multiplexer to run multiple building blocks over the same channel In JBoss we have multiple JGroups channels, one for each application (e.g. JBossCache, ClusterPartition etc). The goal of the Multiplexer is to combine all stacks with the same configuration into one, and have multiple services on top of that same channel. To do this, we have to introduce multiplexing and demultiplexing functionality, ie. each service will have to have a unique service ID (a string), and when sending a message, the message has to be tagged with that ID. When receiving a message, it will be dispatched to the right destination service based on the ID attached to the message. We require special handling for VIEW and SUSPECT messages: those need to be dispatched to *all* services. State transfer also needs to be handled specially, here we probably have to use thread locals, or change the API (TBD). When deployed into JBoss, the Multiplexer will be exposed as an MBean, and all services that depend on it will be deployed with dependency injection on the Multiplexer. Of course, the old configuration will still be supported. The config of the Multiplexer is done via a config file, which lists a number of stacks, each keyed by a name, e.g. "udp", "tcp", "tcp-nio" etc. See ./conf/stacks.xml for an example. An app is configured with the name of a stack, e.g. "udp", and a reference to the Multiplexer MBean. It will get a proxy channel through which all of its communication will take place. The proxy channel (MuxChannel) will mux/demux messages to the real JGroups channel. The advantage of the Multiplexer is that we can reduce N channels into M where M < N. This means fewer threads, therefore fewer context switches, less memory consumption and easier configuration and better support.
The Multiplexer API The Multiplexer is actually a JChannelFactory, which is configured with a reference to an XML configuration file, and has a few additional methods to get a Channel. The channel returned is actually an instance of MuxChannel, which transparently forwards all invocations to the underlying JChannel, and performs multiplexing and demultiplexing. Multiple MuxChannels can share the same underlying JChannel, and each message sent by a service over the MuxChannel will add the services's ID to the message (as a header). That ID is then used to demultiplex the message to the correct MuxChannel when received. The methods of the JChannelFactory are: public Channel createMultiplexerChannel(String stack_name, String id) throws Exception; public Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception; The stack_name parameter refers to a channel configuration defined in a separate file (see below). The id parameter is the service ID and has to be unique for all services sitting on the same channel. If an ID is used more than once, when trying to call createMultiplexerChannel(), an exception will be thrown. the register_for_state_transfer and substate_id parameters are discussed below (in ). The stack_name parameter is a reference to a stack, for example defined in stacks.xml. A shortened version of stacks.xml is shown below: <protocol_stacks> <stack name="fc-fast-minimalthreads" description="Flow control, no up or down threads"> <config> <UDP mcast_port="45566" enable_bundling="true"/> ... <pbcast.STATE_TRANSFER down_thread="false" up_thread="false"/> </config> </stack> <stack name="sequencer" description="Totally ordered multicast using a sequencer"> <config> // config </config> </stack> <stack name="tcp" description="Using TCP as transport"> <config> <TCP start_port="7800" loopback="true" send_buf_size="100000" recv_buf_size="200000"/> <TCPPING timeout="3000" initial_hosts="localhost[7800]" port_range="3" num_initial_members="3"/> <FD timeout="2000" max_tries="4"/> <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false"/> <pbcast.NAKACK gc_lag="100" retransmit_timeout="600,1200,2400,4800"/> <pbcast.STABLE stability_delay="1000" desired_avg_gossip="20000" down_thread="false" max_bytes="0" up_thread="false"/> <VIEW_SYNC avg_send_interval="60000" down_thread="false" up_thread="false" /> <pbcast.GMS print_local_addr="true" join_timeout="5000" shun="true"/> </config> </stack> <stack name="discovery" description="Simple UDP-only stack for discovery"> <config> <UDP mcast_port="7609" use_incoming_packet_handler="false" mcast_addr="228.15.15.15" use_outgoing_packet_handler="false" ip_ttl="32"/> </config> </stack> </protocol_stacks> This file defines 4 configurations: fc-fast-minimalthreads, sequencer, tcp and discovery. The first service to call JChannelFactory.createMultiplexerChannel() with a stack_name of "tcp" will create the JChannel with the "tcp" configuration, all subsequent method calls for the same stack_name ("tcp") will simply get a MuxChannel which has a reference to the same underlying JChannel. When a service closes a MuxChannel, the underlying JChannel will only be closed when there are no more MuxChannels referring to it. For more information on Multiplexing refer to JGroups/doc/design/Multiplexer.txt
Batching state transfers Note that this feature is currently not used in JBoss, because JBoss doesn't call all create() methods of all dependent beans first, and then all start() methods. The call sequence is indeterministic unless all dependent beans are defined in the same XML file, which is unrealistic. We're looking into using a barrier service to provide the guarantee that all create() methods are called before all start() methods, possibly in JBoss 5. When multiple services are sharing a JChannel, and each of the services requires state transfer at a different time, then we need FLUSH (see ./doc/design/PartialStateTransfer.txt for a description of the problem). FLUSH is also called the stop-the-world model, and essentially stops everyone in a group from sending messages until the state has been transferred, and then everyone can resume again. The 2.3 release of JGroups will not have the FLUSH protocol integrated, so state transfer for the Multiplexer might be incorrect. 2.4 will have FLUSH, so that situation will be corrected. The main reason for putting Multiplexing into 2.3 is that people can start programming against the API, and then use it when FLUSH is available. When multiple services share one JChannel, then we have to run the FLUSH protocol for every service which requires state, so if we have services A, B, C, D and E running on top of a JChannel J, and B,C and E require state, then the FLUSH protocol has to be run 3 times, which slows down startup (e.g.) of JBoss. To remedy this, we can batch state transfers, so that we suspend everyone from sending messages, then fetch the states for B, C and E at once, and then resume everyone. Thus, the FLUSH protocol has to be run only once. To do this, a service has to register with the JChannelFactory when creating the MuxChannel, and know that getState() will be a no-op until the last registered application has called getState(). This works as follows: B, C and D register for state transfer B calls MuxChannel.getState(). Nothing happens. D calls MuxChannel.getState(). Nothing happens. E calls MuxChannel.getState(). Now everyone who registered has called getState() and therefore we transfer the state for B, C and E (using partial state transfer). At this point B, C and D's setState() will be called, so that they can set the state. The code below (a snipper from MultiplexerTest) shows how services can register for state transfers. In an MBean (JBoss) environment, the registration could be done in the create() callback, and the actual getState() call in start(). public void testStateTransferWithRegistration() throws Exception { final String STACK_NAME="fc-fast-minimalthreads"; Channel ch1, ch2; ch1=factory.createMultiplexerChannel(STACK_NAME, "c1", true, null); // register for (entire) state transfer ch1.connect("bla"); // will create a new JChannel ch2=factory.createMultiplexerChannel(STACK_NAME, "c2", true, null); // register for (entire) state transfer ch2.connect("bla"); // will share the JChannel created above (same STACK_NAME) boolean rc=ch1.getState(null, 5000); // this will *not* trigger the state transfer protocol rc=ch2.getState(null, 5000); // only *this* will trigger the state transfer } The example above shows that 2 services ("c1" and "c2") share a common JChannel because they use the same stack_name (STACK_NAME). It also shows that only the second getState() invocation will actually transfer the 2 states (for "c1" and "c2").
Service views When we have multiple service running on the same channel, then some services might get redeployed or stopped independently from the other services. So we might have a situation where we have services S1, S2 and S3 running on host H1, but on host H2, only services S2 and S3 are running. The cluster view is {H1, H2}, but the service views are: S1: {H1} S2: {H1, H2} S3: {H1, H2} This can also be seen as ordered by hosts: H1: {S1, S2, S3} H2: {S2, S3} So here we host H1 running services S1, S2 and S3, whereas H2 is only running S2 and S3. S1 might be in the process of being redeployed on H2, or is simply not running. A service view is essentially a list of nodes of a cluster on which a given service S is currently running. Service views are always subsets of cluster views. Here's a reason we need service views: consider the example above. Let's say service S1 on H1 wants to make a cluster-wide method invocation on all instances of S1 running on any host. Now, S1 is only running on H1, therefore we have to make the invocation only on S1. However, if we took the cluster view rather than the service view, the invocation would be across H1 and H2, and we'd be waiting for a response from the (non-existent) service S1 on H2 forever ! So, by default, calling MuxChannel.getView() will return the service view rather than the cluster view. The cluster view can be retrieved calling MuxChannel.getClusterView(). There are example unit tests in MultiplexerTest and MultiplexerViewTest. The latter tests service views versus cluster views.
Transport protocols A transport protocol refers to the protocol at the bottom of the protocol stack which is responsible for sending and receiving messages to/from the network. There are a number of transport protocols in JGroups. They are discussed in the following sections. A typical protocol stack configuration using UDP is: <config> <UDP mcast_addr="${jgroups.udp.mcast_addr:228.10.10.10}" mcast_port="${jgroups.udp.mcast_port:45588}" discard_incompatible_packets="true" max_bundle_size="60000" max_bundle_timeout="30" ip_ttl="${jgroups.udp.ip_ttl:2}" enable_bundling="true" use_concurrent_stack="true" thread_pool.enabled="true" thread_pool.min_threads="1" thread_pool.max_threads="25" thread_pool.keep_alive_time="5000" thread_pool.queue_enabled="false" thread_pool.queue_max_size="100" thread_pool.rejection_policy="Run" oob_thread_pool.enabled="true" oob_thread_pool.min_threads="1" oob_thread_pool.max_threads="8" oob_thread_pool.keep_alive_time="5000" oob_thread_pool.queue_enabled="false" oob_thread_pool.queue_max_size="100" oob_thread_pool.rejection_policy="Run"/> <PING timeout="2000" num_initial_members="3"/> <MERGE2 max_interval="30000" min_interval="10000"/> <FD_SOCK/> <FD timeout="10000" max_tries="5" shun="true"/> <VERIFY_SUSPECT timeout="1500" /> <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0" retransmit_timeout="300,600,1200,2400,4800" discard_delivered_msgs="true"/> <UNICAST timeout="300,600,1200,2400,3600"/> <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000" max_bytes="400000"/> <pbcast.GMS print_local_addr="true" join_timeout="3000" shun="false" view_bundling="true"/> <FC max_credits="20000000" min_threshold="0.10"/> <FRAG2 frag_size="60000" /> <pbcast.STATE_TRANSFER /> </config> In a nutshell the properties of the protocols are: UDP This is the transport protocol. It uses IP multicasting to send messages to the entire cluster, or individual nodes. Other transports include TCP, TCP_NIO and TUNNEL. PING Uses IP multicast (by default) to find initial members. Once found, the current coordinator can be determined and a unicast JOIN request will be sent to it in order to join the cluster. MERGE2 Will merge subgroups back into one group, kicks in after a cluster partition. FD_SOCK Failure detection based on sockets (in a ring form between members). Generates notification if a member fails FD Failure detection based on heartbeats and are-you-alive messages (in a ring form between members). Generates notification if a member fails VERIFY_SUSPECT Double-checks whether a suspected member is really dead, otherwise the suspicion generated from protocol below is discarded pbcast.NAKACK Ensures (a) message reliability and (b) FIFO. Message reliability guarantees that a message will be received. If not, the receiver(s) will request retransmission. FIFO guarantees that all messages from sender P will be received in the order P sent them UNICAST Same as NAKACK for unicast messages: messages from sender P will not be lost (retransmission if necessary) and will be in FIFO order (conceptually the same as TCP in TCP/IP) pbcast.STABLE Deletes messages that have been seen by all members (distributed message garbage collection) pbcast.GMS Membership protocol. Responsible for joining/leaving members and installing new views. FRAG2 Fragments large messages into smaller ones and reassembles them back at the receiver side. For both multicast and unicast messages STATE_TRANSFER Ensures that state is correctly transferred from an existing member (usually the coordinator) to a new member.
UDP UDP uses IP multicast for sending messages to all members of a group and UDP datagrams for unicast messages (sent to a single member). When started, it opens a unicast and multicast socket: the unicast socket is used to send/receive unicast messages, whereas the multicast socket sends/receives multicast messages. The channel's address will be the address and port number of the unicast socket.
Using UDP and plain IP multicasting A protocol stack with UDP as transport protocol is typically used with groups whose members run on the same host or are distributed across a LAN. Before running such a stack a programmer has to ensure that IP multicast is enabled across subnets. It is often the case that IP multicast is not enabled across subnets. Refer to section for running a test program that determines whether members can reach each other via IP multicast. If this does not work, the protocol stack cannot use UDP with IP multicast as transport. In this case, the stack has to either use UDP without IP multicasting or other transports such as TCP.
Using UDP without IP multicasting The protocol stack with UDP and PING as the bottom protocols use IP multicasting by default to send messages to all members (UDP) and for discovery of the initial members (PING). However, if multicasting cannot be used, the UDP and PING protocols can be configured to send multiple unicast messages instead of one multicast message Although not as efficient (and using more bandwidth), it is sometimes the only possibility to reach group members. (UDP) and to access a well-known server ( GossipRouter ) for initial membership information (PING). To configure UDP to use multiple unicast messages to send a group message instead of using IP multicasting, the ip_mcast property has to be set to false . To configure PING to access a GossipRouter instead of using IP multicast the following properties have to be set: gossip_host The name of the host on which GossipRouter is started gossip_port The port on which GossipRouter is listening gossip_refresh The number of milliseconds to wait until refreshing our address entry with the GossipRouter Before any members are started the GossipRouter has to be started, e.g. java org.jgroups.stack.GossipRouter -port 5555 -bindaddress localhost This starts the GossipRouter on the local host on port 5555. The GossipRouter is essentially a lookup service for groups and members. It is a process that runs on a well-known host and port and accepts GET(group) and REGISTER(group, member) requests. The REGISTER request registers a member's address and group with the GossipRouter. The GET request retrieves all member addresses given a group name. Each member has to periodically ( gossip_refresh ) re-register their address with the GossipRouter, otherwise the entry for that member will be removed (accommodating for crashed members). The following example shows how to disable the use of IP multicasting and use a GossipRouter instead. Only the bottom two protocols are shown, the rest of the stack is the same as in the previous example: <UDP ip_mcast="false" mcast_addr="224.0.0.35" mcast_port="45566" ip_ttl="32" mcast_send_buf_size="150000" mcast_recv_buf_size="80000"/> <PING gossip_host="localhost" gossip_port="5555" gossip_refresh="15000" timeout="2000" num_initial_members="3"/> The property ip_mcast is set to false in UDP and the gossip properties in PING define the GossipRouter to be on the local host at port 5555 with a refresh rate of 15 seconds. If PING is parameterized with the GossipRouter's address and port, then gossiping is enabled, otherwise it is disabled. If only one parameter is given, gossiping will be disabled . Make sure to run the GossipRouter before starting any members, otherwise the members will not find each other and each member will form its own group This can actually be used to test the MERGE2 protocol: start two members (forming two singleton groups because they don't find each other), then start the GossipRouter. After some time, the two members will merge into one group .
TCP TCP is a replacement of UDP as bottom layer in cases where IP Multicast based on UDP is not desired. This may be the case when operating over a WAN, where routers will discard IP MCAST. As a rule of thumb UDP is used as transport for LANs, whereas TCP is used for WANs. The properties for a typical stack based on TCP might look like this (edited/protocols removed for brevity): <TCP start_port="7800" /> <TCPPING timeout="3000" initial_hosts="${jgroups.tcpping.initial_hosts:localhost[7800],localhost[7801]}" port_range="1" num_initial_members="3"/> <VERIFY_SUSPECT timeout="1500" /> <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0" retransmit_timeout="300,600,1200,2400,4800" discard_delivered_msgs="true"/> <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000" max_bytes="400000"/> <pbcast.GMS print_local_addr="true" join_timeout="3000" shun="true" view_bundling="true"/> TCP The transport protocol, uses TCP (from TCP/IP) to send unicast and multicast messages. In the latter case, it sends multiple unicast messages. TCPPING Discovers the initial membership to determine coordinator. Join request will then be sent to coordinator. VERIFY_SUSPECT Double checks that a suspected member is really dead pbcast.NAKACK Reliable and FIFO message delivery pbcast.STABLE Distributed garbage collection of messages seen by all members pbcast.GMS Membership services. Takes care of joining and removing new/old members, emits view changes Since TCP already offers some of the reliability guarantees that UDP doesn't, some protocols (e.g. FRAG and UNICAST) are not needed on top of TCP. When using TCP, each message to the group is sent as multiple unicast messages (one to each member). Due to the fact that IP multicasting cannot be used to discover the initial members, another mechanism has to be used to find the initial membership. There are a number of alternatives: PING with GossipRouter: same solution as described in . The ip_mcast property has to be set to false . GossipRouter has to be started before the first member is started. TCPPING: uses a list of well-known group members that it solicits for initial membership TCPGOSSIP: essentially the same as the above PING PING and TCPGOSSIP will be merged in the future. . The only difference is that TCPGOSSIP allows for multiple GossipRouters instead of only one. The next two section illustrate the use of TCP with both TCPPING and TCPGOSSIP.
Using TCP and TCPPING A protocol stack using TCP and TCPPING looks like this (other protocols omitted): <TCP start_port="7800" /> + <TCPPING initial_hosts="HostA[7800],HostB[7800]" port_range="5" timeout="3000" num_initial_members="3" /> The concept behind TCPPING is that no external daemon such as GossipRouter is needed. Instead some selected group members assume the role of well-known hosts from which initial membership information can be retrieved. In the example HostA and HostB are designated members that will be used by TCPPING to lookup the initial membership. The property start_port in TCP means that each member should try to assign port 7800 for itself. If this is not possible it will try the next higher port ( 7801 ) and so on, until it finds an unused port. TCPPING will try to contact both HostA and HostB , starting at port 7800 and ending at port 7800 + port_range , in the above example ports 7800 - 7804 . Assuming that at least one of HostA or HostB is up, a response will be received. To be absolutely sure to receive a response all the hosts on which members of the group will be running can be added to the configuration string.
Using TCP and TCPGOSSIP As mentioned before TCPGOSSIP is essentially the same as PING with properties gossip_host , gossip_port and gossip_refresh set. However, in TCPGOSSIP these properties are called differently as shown below (only the bottom two protocols are shown): <TCP /> <TCPGOSSIP initial_hosts="localhost[5555],localhost[5556]" gossip_refresh_rate="10000" num_initial_members="3" /> The initial_hosts properties combines both the host and port of a GossipRouter, and it is possible to specify more than one GossipRouter. In the example there are two GossipRouters at ports 5555 and 5556 on the local host. Also, gossip_refresh_rate defines how many milliseconds to wait between refreshing the entry with the GossipRouters. The advantage of having multiple GossipRouters is that, as long as at least one is running, new members will always be able to retrieve the initial membership. Note that the GossipRouter should be started before any of the members.
TUNNEL
Using TUNNEL to tunnel a firewall Firewalls are usually placed at the connection to the internet. They shield local networks from outside attacks by screening incoming traffic and rejecting connection attempts to host inside the firewalls by outside machines. Most firewall systems allow hosts inside the firewall to connect to hosts outside it (outgoing traffic), however, incoming traffic is most often disabled entirely. Tunnels are host protocols which encapsulate other protocols by multiplexing them at one end and demultiplexing them at the other end. Any protocol can be tunneled by a tunnel protocol. The most restrictive setups of firewalls usually disable all incoming traffic, and only enable a few selected ports for outgoing traffic. In the solution below, it is assumed that one TCP port is enabled for outgoing connections to the GossipRouter. JGroups has a mechanism that allows a programmer to tunnel a firewall. The solution involves a GossipRouter, which has to be outside of the firewall, so other members (possibly also behind firewalls) can access it. The solution works as follows. A channel inside a firewall has to use protocol TUNNEL instead of UDP as bottommost layer in the stack, plus either PING or TCPGOSSIP, as shown below (only the bottom two protocols shown): <TUNNEL router_host="localhost" router_port="12001" /> <TCPGOSSIP initial_hosts="localhost[12001]" gossip_refresh_rate="10000" num_initial_members="3" /> TCPGOSSIP uses the GossipRouter (outside the firewall) at port 12001 to register its address (periodically) and to retrieve the initial membership for its group. TUNNEL establishes a TCP connection to the GossipRouter process (also outside the firewall) that accepts messages from members and passes them on to other members. This connection is initiated by the host inside the firewall and persists as long as the channel is connected to a group. GossipRouter will use the same connection to send incoming messages to the channel that initiated the connection. This is perfectly legal, as TCP connections are fully duplex. Note that, if GossipRouter tried to establish its own TCP connection to the channel behind the firewall, it would fail. But it is okay to reuse the existing TCP connection, established by the channel. Note that TUNNEL has to be given the hostname and port of the GossipRouter process. This example assumes a GossipRouter is running on the local host at port 12001. Both TUNNEL and TCPGOSSIP (or PING) access the same GossipRouter. Any time a message has to be sent, TUNNEL forwards the message to GossipRouter, which distributes it to its destination: if the message's destination field is null (send to all group members), then GossipRouter looks up the members that belong to that group and forwards the message to all of them via the TCP connection they established when connecting to GossipRouter. If the destination is a valid member address, then that member's TCP connection is looked up, and the message is forwarded to it To do so, GossipRouter has to maintain a table between groups, member addresses and TCP connections. . To tunnel a firewall using JGroups, the following steps have to be taken: Check that a TCP port (e.g. 12001) is enabled in the firewall for outgoing traffic Start the GossipRouter: start org.jgroups.stack.GossipRouter -port 12001 Configure the TUNNEL protocol layer as instructed above. Create a channel The general setup is shown in .
Tunneling a firewall A diagram representing tunneling a firewall.
First, the GossipRouter process is created on host B. Note that host B should be outside the firewall, and all channels in the same group should use the same GossipRouter process. When a channel on host A is created, its TCPGOSSIP protocol will register its address with the GossipRouter and retrieve the initial membership (assume this is C). Now, a TCP connection with the GossipRouter is established by A; this will persist until A crashes or voluntarily leaves the group. When A multicasts a message to the group, GossipRouter looks up all group members (in this case, A and C) and forwards the message to all members, using their TCP connections. In the example, A would receive its own copy of the multicast message it sent, and another copy would be sent to C. This scheme allows for example Java applets , which are only allowed to connect back to the host from which they were downloaded, to use JGroups: the HTTP server would be located on host B and the gossip and GossipRouter daemon would also run on that host. An applet downloaded to either A or C would be allowed to make a TCP connection to B. Also, applications behind a firewall would be able to talk to each other, joining a group. However, there are several drawbacks: first, the central GossipRouter process constitute a single point of failure (if host B crashes) Although multiple GossipRouters could be started , second, having to maintain a TCP connection for the duration of the connection might use up resources in the host system (e.g. in the GossipRouter), leading to scalability problems, third, this scheme is inappropriate when only a few channels are located behind firewalls, and the vast majority can indeed use IP multicast to communicate, and finally, it is not always possible to enable outgoing traffic on 2 ports in a firewall, e.g. when a user does not 'own' the firewall. There will be a major overhaul of GossipRouter/TUNNEL in 2.6, where we'll streamline the connection table and possibly introduce new functionality such as connecting to multiple GossipRouters, connecting to both IP multicasting and TCP based clients etc.
The concurrent stack The concurrent stack (introduced in 2.5) provides a number of improvements over previous releases, which has some deficiencies: Large number of threads: each protocol had by default 2 threads, one for the up and one for the down queue. They could be disabled per protocol by setting up_thread or down_thread to false. In the new model, these threads have been removed. Sequential delivery of messages: JGroups used to have a single queue for incoming messages, processed by one thread. Therefore, messages from different senders were still processed in FIFO order. In 2.5 these messages can be processed in parallel. Out-of-band messages: when an application doesn't care about the ordering properties of a message, the OOB flag can be set and JGroups will deliver this particular message without regard for any ordering.
Overview The architecture of the concurrent stack is shown in . The changes were made entirely inside of the transport protocol (TP, with subclasses UDP, TCP and TCP_NIO). Therefore, to configure the concurrent stack, the user has to modify the config for (e.g.) UDP in the XML file.
The concurrent stack
The concurrent stack consists of 2 thread pools (java.util.concurrent.Executor): the out-of-band (OOB) thread pool and the regular thread pool. Packets are received by multicast or unicast receiver threads (UDP) or a ConnectionTable (TCP, TCP_NIO). Packets marked as OOB (with Message.setFlag(Message.OOB)) are dispatched to the OOB thread pool, and all other packets are dispatched to the regular thread pool. When a thread pool is disabled, then we use the thread of the caller (e.g. multicast or unicast receiver threads or the ConnectionTable) to send the message up the stack and into the application. Otherwise, the packet will be processed by a thread from the thread pool, which sends the message up the stack. When all current threads are busy, another thread might be created, up to the maximum number of threads defined. Alternatively, the packet might get queued up until a thread becomes available. The point of using a thread pool is that the receiver threads should only receive the packets and forward them to the thread pools for processing, because unmarshalling and processing is slower than simply receiving the message and can benefit from parallelization.
Configuration Note that this is preliminary and names or properties might change We are thinking of exposing the thread pools programmatically, meaning that a developer might be able to set both threads pools programmatically, e.g. using something like TP.setOOBThreadPool(Executor executor). Here's an example of the new configuration: ]]> The concurrent stack can be completely eliminated by setting use_concurrent_stack to false. (Note that this attribute might be removed in a future release). The attributes for the 2 thread pools are prefixed with thread_pool and oob_thread_pool respectively. The attributes are listed below. The roughly correspond to the options of a java.util.concurrent.ThreadPoolExecutor in JDK 5. Attributes of thread pools Name Description enabled Whether of not to use a thread pool. If set to false, the caller's thread is used. min_threads The minimum number of threads to use. max_threads The maximum number of threads to use. keep_alive_time Number of milliseconds until an idle thread is removed from the pool queue_enabled Whether of not to use a (bounded) queue. If enabled, when all minimum threads are busy, work items are added to the queue. When the queue is full, additional threads are created, up to max_threads. When max_threads have been reached, the rejection policy is consulted. max_size The maximum number of elements in the queue. Ignored if the queue is disabled rejection_policy Determines what happens when the thread pool (and queue, if enabled) is full. The default is to run on the caller's thread. "Abort" throws an runtime exception. "Discard" discards the message, "DiscardOldest" discards the oldest entry in the queue. Note that these values might change, for example a "Wait" value might get added in the future. thread_naming_pattern Determines how threads are named that are running from thread pools in concurrent stack. Valid values include any combination of "pcsl" letters, where p includes a pool name in a compound thread name, c includes cluster name, s includes sender address, and finally l includes local address of the channel. By default, thread names are composed of corresponding pool name and a thread id.
Elimination of up and down threads By removing the 2 queues/protocol and the associated 2 threads, we effectively reduce the number of threads needed to handle a message, and thus context switching overhead. We also get clear and unambiguous semantics for Channel.send(): now, all messages are sent down the stack on the caller's thread and the send() call only returns once the message has been put on the network. In addition, an exception will only be propagated back to the caller if the message has not yet been placed in a retransmit buffer. Otherwise, JGroups simply logs the error message but keeps retransmitting the message. Therefore, if the caller gets an exception, the message should be re-sent. On the receiving side, a message is handled by a thread pool, either the regular or OOB thread pool. Both thread pools can be completely eliminated, so that we can save even more threads and thus further reduce context switching. The point is that the developer is now able to control the threading behavior almost completely.
Concurrent message delivery Up to version 2.5, all messages received were processed by a single thread, even if the messages were sent by different senders. For instance, if sender A sent messages 1,2 and 3, and B sent message 34 and 45, and if A's messages were all received first, then B's messages 34 and 35 could only be processed after messages 1-3 from A were processed ! Now, we can process messages from different senders in parallel, e.g. messages 1, 2 and 3 from A can be processed by one thread from the thread pool and messages 34 and 35 from B can be processed on a different thread. As a result, we get a speedup of almost N for a cluster of N if every node is sending messages and we configure the thread pool to have at least N threads. There is actually a unit test (ConcurrentStackTest.java) which demonstrates this.
Out-of-band messages OOB messages completely ignore any ordering constraints the stack might have. Any message marked as OOB will be processed by the OOB thread pool. This is necessary in cases where we don't want the message processing to wait until all other messages from the same sender have been processed, e.g. in the heartbeat case: if sender P sends 5 messages and then a response to a heartbeat request received from some other node, then the time taken to process P's 5 messages might take longer than the heartbeat timeout, so that P might get falsely suspected ! However, if the heartbeat response is marked as OOB, then it will get processed by the OOB thread pool and therefore might be concurrent to its previously sent 5 messages and not trigger a false suspicion. The 2 unit tests UNICAST_OOB_Test and NAKACK_OOB_Test demonstrate how OOB messages influence the ordering, for both unicast and multicast messages.
Misc
Shunning Let's say we have 4 members in a group: {A,B,C,D}. When a member (say D) is expelled from the group, e.g. because it didn't respond to are-you-alive messages, and later comes back, then it is shunned. Shunning causes a member to leave the group and re-join, if this is enabled on the Channel. To enable automatic re-connects, the AUTO_RECONNECT option has to be set on the Channel: channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); To enable shunning, set FD.shun and GMS.shun to true. Let's look at a more detailed example. Say member D is overloaded, and doesn't respond to are-you-alive messages (done by the failure detection (FD) protocol). It is therefore suspected and excluded. The new view for A, B and C will be {A,B,C}, however for D the view is still {A,B,C,D}. So when D comes back and sends messages to the group, or any individiual member, those messages will be discarded, because A,B and C don't see D in their view. D is shunned when A,B or C receive an are-you-alive message from D, or D shuns itself when it receives a view which doesn't include D. So shunning is always a unilateral decision. However, things may be different if all members exclude each other from the group. For example, say we have a switch connecting A, B, C and D. If someone pulls all plugs on the switch, or powers the switch down, then A, B, C and D will all form singleton groups, that is, each member thinks it's the only member in the group. When the switch goes back to normal, then each member will shun everybody else (a real shun fest :-)). This is clearly not desirable, so in this case shunning should be turned off: <FD timeout="2000" max_tries="3" shun="false"/> ... <pbcast.GMS join_timeout="3000" shun="false"/>
Handling network partitions Network partitions can be caused by switch, router or network interface crashes, among other things. If we have a cluster {A,B,C,D,E} spread across 2 subnets {A,B,C} and {D,E} and the switch to which D and E are connected crashes, then we end up with a network partition, with subclusters {A,B,C} and {D,E}. A, B and C can ping each other, but not D or E, and vice versa. We now have 2 coordinators, A and D. Both subclusters operate independently, for example, if we maintain a shared state, subcluster {A,B,C} replicate changes to A, B and C. This means, that if during the partition, some clients access {A,B,C}, and others {D,E}, then we end up with different states in both subclusters. When a partition heals, the merge protocol (e.g. MERGE2) will notify A and D that there were 2 subclusters and merge them back into {A,B,C,D,E}, with A being the new coordinator and D ceasing to be coordinator. The question is what happens with the 2 diverged substates ? There are 2 solutions to merging substates: first we can attempt to create a new state from the 2 substates, and secondly we can shut down all members of the non primary partition, such that they have to re-join and possibly reacquire the state from a member in the primary partition. In both cases, the application has to handle a MergeView (subclass of View), as shown in the code below: public void viewAccepted(View view) { if(view instanceof MergeView) { MergeView tmp=(MergeView)view; Vector<View> subgroups=tmp.getSubgroups(); // merge state or determine primary partition // run this in a separate thread ! } } It is essential that the merge view handling code run on a separate thread if it needs more than a few milliseconds, or else it would block the calling thread. The MergeView contains a list of views, each view represents a subgroups and has the list of members which formed this group.
Merging substates The application has to merge the substates from the various subgroups ({A,B,C} and {D,E}) back into one single state for {A,B,C,D,E}. This task has to be done by the application because JGroups knows nothing about the application state, other than it is a byte buffer. If the in-memory state is backed by a database, then the solution is easy: simply discard the in-memory state and fetch it (eagerly or lazily) from the DB again. This of course assumes that the members of the 2 subgroups were able to write their changes to the DB. However, this is often not the case, as connectivity to the DB might have been severed by the network partition. Another solution could involve tagging the state with time stamps. On merging, we could compare the time stamps for the substates and let the substate with the more recent time stamps win. Yet another solution could increase a counter for a state each time the state has been modified. The state with the highest counter wins. Again, the merging of state can only be done by the application. Whatever algorithm is picked to merge state, it has to be deterministic.
The primary partition approach
The primary partition approach is simple: on merging, one subgroup is designated as the primary partition and all others as non-primary partitions. The members in the primary partition don't do anything, whereas the members in the non-primary partitions need to drop their state and re-initialize their state from fresh state obtained from a member of the primary partition. The code to find the primary partition needs to be deterministic, so that all members pick the same primary partition. This could be for example the first view in the MergeView, or we could sort all members of the new MergeView and pick the subgroup which contained the new coordinator (the one from the consolidated MergeView). Another possible solution could be to pick the largest subgroup, and, if there is a tie, sort the tied views lexicographically (all Addresses have a compareTo() method) and pick the subgroup with the lowest ranked member. Here's code which picks as primary partition the first view in the MergeView, then re-acquires the state from the new coordinator of the combined view: public static void main(String[] args) throws Exception { final JChannel ch=new JChannel("/home/bela/udp.xml"); ch.setReceiver(new ExtendedReceiverAdapter() { public void viewAccepted(View new_view) { handleView(ch, new_view); } }); ch.connect("x"); while(ch.isConnected()) Util.sleep(5000); } private static void handleView(JChannel ch, View new_view) { if(new_view instanceof MergeView) { ViewHandler handler=new ViewHandler(ch, (MergeView)new_view); handler.start(); // requires separate thread as we don't want to block JGroups } } private static class ViewHandler extends Thread { JChannel ch; MergeView view; private ViewHandler(JChannel ch, MergeView view) { this.ch=ch; this.view=view; } public void run() { Vector<View> subgroups=view.getSubgroups(); View tmp_view=subgroups.firstElement(); // picks the first Address local_addr=ch.getLocalAddress(); if(!tmp_view.getMembers().contains(local_addr)) { System.out.println("I (" + local_addr + ") am not member of the new primary partition (" + tmp_view + "), will re-acquire the state"); try { ch.getState(null, 30000); } catch(Exception ex) { } } else { System.out.println("I (" + local_addr + ") am member of the new primary partition (" + tmp_view + "), will do nothing"); } } } The handleView() method is called from viewAccepted(), which is called whenever there is a new view. It spawns a new thread which gets the subgroups from the MergeView, and picks the first subgroup to be the primary partition. Then, if it was a member of the primary partition, it does nothing, and if not, it reaqcuires the state from the coordinator of the primary partition (A). The downside to the primary partition approach is that work (= state changes) on the non-primary partition is discarded on merging. However, that's only problematic if the data was purely in-memory data, and not backed by persistent storage. If the latter's the case, use state merging discussed above. It would be simpler to shut down the non-primary partition as soon as the network partition is detected, but that a non trivial problem, as we don't know whether {D,E} simply crashed, or whether they're still alive, but were partitioned away by the crash of a switch. This is called a split brain syndrome, and means that none of the members has enough information to determine whether it is in the primary or non-primary partition, by simply exchanging messages.
The Split Brain syndrome and primary partitions In certain situations, we can avoid having multiple subgroups where every subgroup is able to make progress, and on merging having to discard state of the non-primary partitions. If we have a fixed membership, e.g. the cluster always consists of 5 nodes, then we can run code on a view reception that determines the primary partition. This code assumes that the primary partition has to have at least 3 nodes any cluster which has less than 3 nodes doesn't accept modfications. This could be done for shared state for example, by simply making the {D,E} partition read-only. Clients can access the {D,E} partition and read state, but not modify it. As an alternative, clusters without at least 3 members could shut down, so in this case D and E would leave the cluster. The algorithm is shown in pseudo code below: On initialization: - Mark the node as read-only On view change V: - If V has >= N members: - If not read-write: get state from coordinator and switch to read-write - Else: switch to read-only Of course, the above mechanism requires that at least 3 nodes are up at any given time, so upgrades have to be done in a staggered way, taking only one node down at a time. In the worst case, however, this mechanism leaves the cluster read-only and notifies a system admin, who can fix the issue. This is still better than shutting the entire cluster down.
Flushing: making sure every node in the cluster received a message When sending messages, the properties of the default stacks (udp.xml, tcp.xml) are that all messages are delivered reliably to all (non-crashed) members. However, there are no guarantees with respect to the view in which a message will get delivered. For example, when a member A with view V1={A,B,C} multicasts message M1 to the group and D joins at about the same time, then D may or may not receive M1, and there is no guarantee that A, B and C receive M1 in V1 or V2={A,B,C,D}. To change this, we can turn on virtual synchrony (by adding FLUSH to the top of the stack), which guarantees that A message M sent in V1 will be delivered in V1. So, in the example above, M1 would get delivered in view V1; by A, B and C, but not by D. The set of messages seen by members in V1 is the same for all members before a new view V2 is installed. This is important, as it ensures that all members in a given view see the same messages. For example, in a group {A,B,C}, C sends 5 messages. A receives all 5 messages, but B doesn't. Now C crashes before it can retransmit the messages to B. FLUSH will now ensure, that before installing V2={A,B} (excluding C), B gets C's 5 messages. This is done through the flush protocol, which has all members reconcile their messages before a new view is installed. In this case, A will send C's 5 messages to B. Sometimes it is important to know that every node in the cluster received all messages up to a certain point, even if there is no new view being installed. To do this (initiate a manual flush), an application programmer can call Channel.startFlush() to start a flush and Channel.stopFlush() to terminate it. Channel.startFlush() flushes all pending messages out of the system. This stops all senders (calling Channel.down() during a flush will block until the flush has completed)Note that block() will be called in a Receiver when the flush is about to start and unblock() will be called when it ends. When startFlush() returns, the caller knows that (a) no messages will get sent anymore until stopFlush() is called and (b) all members have received all messages sent before startFlush() was called. Channel.stopFlush() terminates the flush protocol, no blocked senders can resume sending messages. Note that the FLUSH protocol has to be present on top of the stack, or else the flush will fail.
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/blocks.xml0000644000175000017500000007321711366547366024443 0ustar twernertwernerBuilding Blocks Building blocks are layered on top of channels. Most of them do not even need a channel, all they need is a class that implements interface Transport (channels do). This enables them to work on any type of group transport that obeys this interface. Building blocks can be used instead of channels whenever a higher-level interface is required. Whereas channels are simple socket-like constructs, building blocks may offer a far more sophisticated interface. In some cases, building blocks offer access to the underlying channel, so that -- if the building block at hand does not offer a certain functionality -- the channel can be accessed directly. Building blocks are located in the org.jgroups.blocks package. Only the ones that are relevant for application programmers are discussed below.
PullPushAdapter This class is a converter (or adapter, as used in between the pull-style of actively receiving messages from the channel and the push-style where clients register a callback which is invoked whenever a message has been received. Clients of a channel do not have to allocate a separate thread for message reception. A PullPushAdapter is always created on top of a class that implements interface Transport (e.g. a channel). Clients interested in being called when a message is received can register with the PullPushAdapter using method setListener(). They have to implement interface MessageListener, whose receive() method will be called when a message arrives. When a client is interested in getting view, suspicion messages and blocks, then it must additionally register as a MembershipListener using method setMembershipListener(). Whenever a view, suspicion or block is received, the corresponding method will be called. Upon creation, an instance of PullPushAdapter creates a thread which constantly calls the receive() method of the underlying Transport instance, blocking until a message is available. When a message is received, if there is a registered message listener, its receive() method will be called. As this class does not implement interface Transport, but merely uses it for receiving messages, an underlying object has to be used to send messages (e.g. the channel on top of which an object of this class resides). This is shown in .
Class <classname>PullPushAdapter</classname> A diagram representing the PullPushAdapter.
As is shown, the thread constantly pulls messages from the channel and forwards them to the registered listeners. An application thus does not have to actively pull for messages, but the PullPushAdapter does this for it. Note however, that the application has to directly access the channel if it wants to send a message.
Example This section shows sample code for using a PullPushAdapter. The example has been shortened for readability (error handling has been removed). public class PullPushTest implements MessageListener { Channel channel; PullPushAdapter adapter; byte[] data="Hello world".getBytes(); String props; // fetch properties public void receive(Message msg) { System.out.println("Received msg: " + msg); } public void start() throws Exception { channel=new JChannel(props); channel.connect("PullPushTest"); adapter=new PullPushAdapter(channel); adapter.setListener(this); for(int i=0; i < 10; i++) { System.out.println("Sending msg #" + i); channel.send(new Message(null, null, data)); Thread.currentThread().sleep(1000); } adapter.stop(); channel.close(); } public static void main(String args[]) { try { new PullPushTest().start(); } catch(Exception e) { /* error */ } } } First a channel is created and connected to. Then an instance of PullPushAdapter is created with the channel as argument. The constructor of PullPushAdapter starts its own thread which continually reads on the channel. Then the MessageListener is set, which causes all messages received on the channel to be sent to receive(). Then a number of messages are sent via the channel to the entire group. As group messages are also received by the sender, the receive() method will be called every time a message is received. Finally the PullPushAdapter is stopped and the channel closed. Note that explicitly stopping the PullPushAdapter is not actually necessary, a closing the channel would cause the PullPushAdapter to terminate anyway. Note that, compared to the pull-style example, push-style message reception is considerably easier (no separate thread management) and requires less code to program. The PullPushAdapter has been deprecated, and will be removed in 3.0. Use a Receiver implementation instead. The advantage of the Receiver-based (push) model is that we save 1 thread.
MessageDispatcher Channels are simple patterns to asynchronously send a receive messages. However, a significant number of communication patterns in group communication require synchronous communication. For example, a sender would like to send a message to the group and wait for all responses. Or another application would like to send a message to the group and wait only until the majority of the receivers have sent a response, or until a timeout occurred. MessageDispatcher offers a combination of the above pattern with other patterns. It provides synchronous (as well as asynchronous) message sending with request-response correlation, e.g. matching responses with the original request. It also offers push-style message reception (by internally using the PullPushAdapter). An instance of MessageDispatcher is created with a channel as argument. It can now be used in both client and server role: a client sends requests and receives responses and a server receives requests and send responses. MessageDispatcher allows a application to be both at the same time. To be able to serve requests, the RequestHandler.handle() method has to be implemented: Object handle(Message msg); The handle() method is called any time a request is received. It must return a return value (must be serializable, but can be null) or throw an exception. The return value will be returned to the sender (as a null response, see below). The exception will also be propagated to the requester. The two methods to send requests are: public RspList castMessage(Vector dests, Message msg, int mode, long timeout); public Object sendMessage(Message msg, int mode, long timeout) throws TimeoutException; The castMessage() method sends a message to all members defined in dests. If dests is null the message will be sent to all members of the current group. Note that a possible destination set in the message will be overridden. If a message is sent synchronously then the timeout argument defines the maximum amount of time in milliseconds to wait for the responses. The mode parameter defines whether the message will be sent synchronously or asynchronously. The following values are valid (from org.jgroups.blocks.GroupRequest): GET_FIRST Returns the first response received. GET_ALL Waits for all responses (minus the ones from suspected members) GET_MAJORITY Waits for a majority of all responses (relative to the group size) GET_ABS_MAJORITY Waits for the majority (absolute, computed once) GET_N Wait for n responses (may block if n > group size) GET_NONE Wait for no responses, return immediately (non-blocking). This make the call asynchronous. The sendMessage() method allows an application programmer to send a unicast message to a receiver and optionally receive the response. The destination of the message has to be non-null (valid address of a receiver). The mode argument is ignored (it is by default set to GroupRequest.GET_FIRST) unless it is set to GET_NONE in which case the request becomes asynchronous, ie. we will not wait for the response. One advantage of using this building block is that failed members are removed from the set of expected responses. For example, when sending a message to 10 members and waiting for all responses, and 2 members crash before being able to send a response, the call will return with 8 valid responses and 2 marked as failed. The return value of castMessage() is a RspList which contains all responses (not all methods shown): public class RspList { public boolean isReceived(Address sender); public int numSuspectedMembers(); public Vector getResults(); public Vector getSuspectedMembers(); public boolean isSuspected(Address sender); public Object get(Address sender); public int size(); public Object elementAt(int i) throws ArrayIndexOutOfBoundsException; } Method isReceived() checks whether a response from sender has already been received. Note that this is only true as long as no response has yet been received, and the member has not been marked as failed. numSuspectedMembers() returns the number of members that failed (e.g. crashed) during the wait for responses. getResults() returns a list of return values. get() returns the return value for a specific member.
Example This section describes an example of how to use a MessageDispatcher. public class MessageDispatcherTest implements RequestHandler { Channel channel; MessageDispatcher disp; RspList rsp_list; String props; // to be set by application programmer public void start() throws Exception { channel=new JChannel(props); disp=new MessageDispatcher(channel, null, null, this); channel.connect("MessageDispatcherTestGroup"); for(int i=0; i < 10; i++) { Util.sleep(100); System.out.println("Casting message #" + i); rsp_list=disp.castMessage(null, new Message(null, null, new String("Number #" + i)), GroupRequest.GET_ALL, 0); System.out.println("Responses:\n" +rsp_list); } channel.close(); disp.stop(); } public Object handle(Message msg) { System.out.println("handle(): " + msg); return new String("Success !"); } public static void main(String[] args) { try { new MessageDispatcherTest().start(); } catch(Exception e) { System.err.println(e); } } } The example starts with the creation of a channel. Next, an instance of MessageDispatcher is created on top of the channel. Then the channel is connected. The MessageDispatcher will from now on send requests, receive matching responses (client role) and receive requests and send responses (server role). We then send 10 messages to the group and wait for all responses. The timeout argument is 0, which causes the call to block until all responses have been received. The handle() method simply prints out a message and returns a string. Finally both the MessageDispatcher and channel are closed.
RpcDispatcher This class is derived from MessageDispatcher. It allows a programmer to invoke remote methods in all (or single) group members and optionally wait for the return value(s). An application will typically create a channel and layer the RpcDispatcher building block on top of it, which allows it to dispatch remote methods (client role) and at the same time be called by other members (server role). Compared to MessageDispatcher, no handle() method needs to be implemented. Instead the methods to be called can be placed directly in the class using regular method definitions (see example below). The invoke remote method calls (unicast and multicast) the following methods are used (not all methods shown): public RspList callRemoteMethods(Vector dests, String method_name, int mode, long timeout); public RspList callRemoteMethods(Vector dests, String method_name, Object arg1, int mode, long timeout); public Object callRemoteMethod(Address dest, String method_name, int mode, long timeout); public Object callRemoteMethod(Address dest, String method_name, Object arg1, int mode, long timeout); The family of callRemoteMethods() is invoked with a list of receiver addresses. If null, the method will be invoked in all group members (including the sender). Each call takes the name of the method to be invoked and the mode and timeout parameters, which are the same as for MessageDispatcher. Additionally, each method takes zero or more parameters: there are callRemoteMethods() methods with up to 3 arguments. As shown in the example above, the first 2 methods take zero and one parameters respectively. The family of callRemoteMethod() methods takes almost the same parameters, except that there is only one destination address instead of a list. If the dest argument is null, the call will fail. If a sender needs to use more than 3 arguments, it can use the generic versions of callRemoteMethod() and callRemoteMethods() which use a MethodCallSee the Programmer's Guide and the Javadoc documentation for more information about this class. instance rather than explicit arguments. Java's Reflection API is used to find the correct method in the receiver according to the method name and number and types of supplied arguments. There is a runtime exception if a method cannot be resolved. (* Update: these methods are deprecated; must use MethodCall argument now *)
Example The code below shows an example: public class RpcDispatcherTest { Channel channel; RpcDispatcher disp; RspList rsp_list; String props; // set by application public int print(int number) throws Exception { return number * 2; } public void start() throws Exception { channel=new JChannel(props); disp=new RpcDispatcher(channel, null, null, this); channel.connect("RpcDispatcherTestGroup"); for(int i=0; i < 10; i++) { Util.sleep(100); rsp_list=disp.callRemoteMethods(null, "print", new Integer(i), GroupRequest.GET_ALL, 0); System.out.println("Responses: " +rsp_list); } channel.close(); disp.stop(); } public static void main(String[] args) { try { new RpcDispatcherTest().start(); } catch(Exception e) { System.err.println(e); } } } Class RpcDispatcher defines method print() which will be called subsequently. The entry point start() method creates a channel and an RpcDispatcher which is layered on top. Method callRemoteMethods() then invokes the remote print() method in all group members (also in the caller). When all responses have been received, the call returns and the responses are printed. As can be seen, the RpcDispatcher building block reduces the amount of code that needs to be written to implement RPC-based group communication applications by providing a higher abstraction level between the application and the primitive channels.
DistributedHashtable A DistributedHashtable is derived from java.util.Hashtable and allows to create several instances of hashtables in different processes. All of these instances have exactly the same state at all times. When creating such an instance, a group name determines which group of hashtables will be joined. The new instance will then query the state from existing members and update itself before starting to service requests. If there are no existing members, it will simply start with an empty state. Modifications such as put(), clear() or remove() will be propagated in orderly fashion to all replicas. Read-only requests such as get() will only be sent to the local copy. Since both keys and values of a hashtable will be sent across the network as copies, both of them have to be serializable. This allows for example to register remote RMI objects with any local instance of a hashtable, which can subsequently be looked up by another process which can then invoke remote methods (remote RMI objects are serializable). Thus, a distributed naming and registration service can be built in just a couple of lines. A DistributedHashtable allows to register for notifications, e.g. when a new item is set, or an existing one removed. All registered listeners will notified when such an event occurs. Notification is always local; for example in the case of removing an element, first the element is removed in all replicas, which then notify their listener(s) of the removal (after the fact). DistributedHashtable allow members in a group to share common state across process and machine boundaries.
ReplicatedHashtable ReplicatedHashtable provides exactly the same methods as as DistributedHashtable. However, it is implemented differently. Whereas the latter uses synchronous remote group method invocation (similar to RpcDispatcher), the former uses asynchronous communication to keep the replicas up-to-date.
DistributedTree Similar to DistributedHashtable this class also provides replication of a data structure across multiple processes. However, a tree structure instead of a hashtable is replicated by DistributedTree. Updates are multicast to all group members reliably and in the same order using the underlying channel. The tree consists of a root and zero or more child nodes. Each node can be either another subtree, or a leaf node. A node has a name and a value. The value can be any object that is serializable. A node in the tree is identified by concatenating all nodes from the root to it, separated with '/' characters, e.g. /a/b/c. New nodes can be added dynamically. Existing nodes (also entire subtrees) can be removed. Values can be attached to an existing node. Whenever the tree is modified events will be sent for which listeners can register. Listeners have to implement interface DistributedTreeListener: public interface DistributedTreeListener { void nodeAdded(String fqn, Serializable element); void nodeRemoved(String fqn); void nodeModified(String fqn, Serializable old_element, Serializable new_element); } The methods provided by DistributedTree are listed below (not all methods shown): public class DistributedTree { public void add(String fqn); public void add(String fqn, Serializable element); public void remove(String fqn); public boolean exists(String fqn); public Serializable get(String fqn); public void set(String fqn, Serializable element); public Vector getChildrenNames(String fqn); } The two add() methods add a new node. The first method assigns no value to the node, whereas the second does. Note that it does not matter whether or not parent nodes exists: an addition of "/a/b/c/d" to a tree "/a/b" would create nodes "/a/b/c" and "/a/b/c/d". However, if a value was given, it would be assigned only to the latter. The remove() method removes a node from the tree. If the node is a subtree itself, all nodes under it will be removed recursively. E.g. the removal of "/" from "/a/b" would trigger 3 nodeRemoved() notifications: "/a/b", "/a" and "/" (in this order)Assuming that these are the only nodes in the entire tree, e.g. "/a" has no other children. The exists() method tests whether a given node exists in the tree. The get() method returns either the value associated with the given node, or null if the node cannot be found or there is no value attached. Method set() attaches a value to a given node. It fails if the node does not exist. Use add(String, Serializable) instead if the node should be created if not existent. Method getChildrenNames() furnishes a list of the fully qualified names of all children nodes of a given node. This gives a programmer modest navigation possibilities within the tree. There is a demo application in org.jgroups.demos.DistributedTreeDemo.
NotificationBus This class provides notification sending and handling capability. Also, it allows an application programmer to maintain a local cache which is replicated by all instances. NotificationBus also sits on top of a channel, however it creates its channel itself, so the application programmers do not have to provide their own channel. Notification consumers can subscribe to receive notifications by calling setConsumer() and implementing interface NotificationBus.Consumer: public interface Consumer { void handleNotification(Serializable n); Serializable getCache(); void memberJoined(Address mbr); void memberLeft(Address mbr); } Method handleNotification() is called whenever a notification is received from the channel. A notification is any object that is serializable. Method getCache() is called when someone wants to retrieve our state; the state can be returned as a serializable object. The memberJoined() and memberLeft() callbacks are invoked whenever a member joins or leaves (or crashes). The most important methods of NotificationBus are: public class NotificationBus { public void setConsumer(Consumer c); public void start() throws Exception; public void stop(); public void sendNotification(Serializable n); public Serializable getCacheFromCoordinator(long timeout, int max_tries); public Serializable getCacheFromMember(Address mbr, long timeout, int max_tries); } Method setConsumer() allows a consumer to register itself for notifications. The start() and stop() methods start and stop the NotificationBus. Method sendNotification() sends the serializable object given as argument to all members of the group, invoking their handleNotification() methods on reception. Methods getCacheFromCoordinator() and getCacheFromMember() provide functionality to fetch the group state from the coordinator (first member in membership list) or any other member (if its address is known). They take as arguments a timeout and a maximum number of unsuccessful attempts until they return null. Typically one of these methods would be called just after creating a new NotificationBus to acquire the group state. Note that if these methods are used, then the consumers must implement Consumer.getCache(), otherwise the two methods above would always return null.
libjgroups2.6-java-2.6.15.GA.orig/doc/manual/en/modules/eventlist.xml0000644000175000017500000000012311366547366025165 0ustar twernertwernerList of events libjgroups2.6-java-2.6.15.GA.orig/doc/manual/build.xml0000644000175000017500000000112111366547366022174 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/0000755000175000017500000000000011621261107020722 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/0000755000175000017500000000000011621261107021324 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/images/0000755000175000017500000000000011621261107022571 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/images/BinaryContents.png0000644000175000017500000021211411366547366026266 0ustar twernertwerner‰PNG  IHDRI3¹|ÅiCCPICC Profilexœ•’ËkQ‡¿›`£ãƒ»)Œ _ʘ"í2mµvˆC1Én23M¢™É8™Ä碠®\‚"]t+Š«"]ˆâB_øOHA„RÇÅ8‰ ¥xàÂwçu÷Bô›æ8`Ùž[žŸ“ªµºûDŒ}Œ1Î~Mï9³ªª°£ýøˆø0©9NGš:ugNŒ}»ulã¾oï\@­Öê Ò@²pH6®ÉkžãhI½¥ ni·RÎXÍ€_‰FÀ¯Ä@oz >²m´mˆìf ³§C$ ,=Ý‚È*ðÕ²ºDï'uÇõ ºLVku)X¹;3¿ zoÓrðLÀø_±ã‹pxž‹Ql3…DêMoy*€ˆ»°ç§ïoN@ì%l»¾¿õÐ÷·Ÿ@ô ¬_Õûîà^B¼ƒÝüàÎï½vâ@dx¼•PÖáÁ œ8‡n T²D¦¦Âh@<_P)#ËÓEu—'ÿ³:ýpÎ Þö*@ ØXv‹åíFéBÈf¯°4ÌiBv?BÂú_Bt˜&AÓ4Œ1ùÇ{{{C¡ªª_VaÆhŒÆh”Äó¼Ûíöù|™™™’$!„8ŽK‚;kš¦iZggg[[›¢(.—+===--M–e’!„1¦õ‡&͉áI{ z̉¾î„Ò“RŸh®“(†M2«G£ÕÞó¥_öù¹o6œ¿tÙN”F¬j½¥(“X,600Ðß߇Aðûý¹¹¹Ç%á4MkmmmkkE1??cF£Ñ¨¢(ÄžëØ¶jë´pDeÐÀ3!ý”å©ÿ4g#í³³¹8Žc[I8b2«ÒÑu2"VìQ­é>¶ ]­‚„þí ÜX1ì±é{ 1V‚Ù$3-#C'0˜A0›n©Í»4M OÓH&zM"„Ap:.— ÚÛÛEñûýÛ‚ªªÝÝÝííí²,§§§÷÷÷h4ªiqËu¬ê/6¼ÏCc›N`øê6-Û&lˆ± Øüdqn*’•¾0Äë}CJZwŒ(Õ(Kªišƒ}•êœ4)öÛ†T웚᳚ bPÄzaÀ4wVHmÜlí“Yé «réh¤ØÈlHv¢uK>®â0cÌó<ÇqN§Óëõ¦¥¥  Íqœ ( qÅ%Ijmm ‰DÂÀÎÛVhgí6˜µ?{<Ѐ±±Ò¦úÕ$ s¶·ç6Ø6¼tÄ”UmÚ"é\Òý‘ÛcMÓâñ¸(Š‚ ˜V”!`ƒm§©y¤sgm6¯ õŸöØ6À’­:™iþôÒ?õWЉ aš?ý‰­°Í>5•Í´~LU$]cEQ ú|>—ËEàœžžÎó¼ÐßßF ‰„ÃaŒ± ¦À l†âØc›µuÀ@ ˜oÇ`«‰ýðÂ#"Ÿ ‘!¥A›˜âŸ-¯•ª³O iZWW÷Î]u¥%Å&”išÖÝÝíñxÒÓÓ­¸aÊmaôÑ3ìO½þm8ƒú¬ð†zÐ3Ú Û0hþäéyõFKKÅ–ˆ&>­¸CØÚnÓ1´+A§×ÿ†B!ŽD"ýýýiiiÂàà`,K$cò×ðn„Pssó¶mÛêëë= ãÇ///Ÿ5kVii©© zC$Ü:´kÇŽæ}û;€œâ¢²iÓN«®.++£ßbª HöÚÚÚ½{÷êoŸ>}úìÙ³'Nœh 6`šµ!™ZzÀÔ‘¶âi#9ýÓJUCj«5å ©MÓ ¹:;»ÞÿýÃ÷m÷f^xÑEà@W(›;o¾Ãá€áökø`ÍñŒy‹gäЬë=¸é£ƒÂ‚óg{-|ZZË€ Š€j|@jÉ4ÆP ¬À,Y½Ý´ÔôSºŸœUFj·éô¦šËŠP×FYU†Ê‡¡úŒÇã‰D‚ã8Y–=úàƒÚÚÚH"ÒÁ6Ôò† >þè£Á¬Óf`€¶ÖÖí;vnk=sþü%K–@j»§± ï¯^½{Æ ùÓ«gå €®ã­{wî<Ðz|æ¢Eç/]Jg¤¿ùù·¿ýmíÚµ~¿æÌ™…Áñc­»víjmm]¼xñÅ_l¨¶ÝKV‹ö?í±­k·Ñ°b…´¥%aMÓ:;»Ö¬Y3ØstÁÜÉÛv7÷…„SfϘX··qÞ‚s½^/õ–ÐËÓkøñ÷ýk%- K¹÷…ë®üeÿË[ߨtOCÔ¦¡³&‹5A†v<ú«WÐ?M%aó² ¤µ«ªlÀ ̬´[ç´ºdoU.6=yÊó¤æ@ME.î.غf}SYíœë©©©iíÚµãªÆåÎÊE0 ¶E#Ýü8Èž•SÔW¸zõê²²² &Ш Û©&f¬|ƒ†ۦܬbèšbÛž³®ªÛÛÛß{oµSÒ–Ì.ÿè…ÿ»åÅ·6¼ðlIoóøùÿÏÓ+&M,6é¹aÀ¢ öuõ GZ†O¦ÚY¬·sÜ™.ž$Ö0€¦i ÅÂ}0v¦ë ã“›h± ñK`Êa4vÛÔ¢Úà‡fË‚™õGL=+ÜÒï²ùÉʦiÚo¼100pþùçO™2ÅëõŠ¢¨ @ “H$@CCÃúõ뻺º.¹äƒUc Ixx|•òmlh( Gg¤ñ®º]ÁŽ£ñt«+ýWg_æúÃÑÆ††‰'ÒyI¥ihçÏÊäwó± ˆ¬©(”Èbã`°5~¸¡aòäɈ'ohhHxc­˱«±¿¡5ìà8!PT%‹ô8âü¬ôÄ***FmºÖl"Y§È4]]†°J@†mìù˜4MëèèØ°aƒ‘+.½¼·n§„y5ÍdO<¨\zñŽ+O©hóz½Ô€¨À!l¯ýÝÝW>¹ àë÷½x÷²ãXÛÖû–Þ´ nxäõž[†1y¢(JRì“?Ü¿üñ¿nxøÏß_2›“›bÀPiV’A™Æ`I§„!zÍÛ¦ZÓâ˜j%C2º©¯_¿¾··÷›ßüæÜ¹sÓÓÓ%IâyÞTrâb—••eeeýéOÚºu+±Þ´äªªŠ¢I»­ª*ñhˆ1nÞ±³ÆéÎëëtpƒÚ D€ÃýAW_ WCSœ®íµ;çGç%Ù;k·Ï+uåçôù„~!¸y^Â`%ÎÇBšþüô)\«kk\ ·xÃöݵ®iN!§G•ƒýQ¤€DŒ1Ö”°RŠïqNunÛõÉ¥—^j°½VÐÔ²@²Â0Ì`ˆèƒŽ0$0µ'ì+X‰DCC£ -]8Ï!¹‹ªÏèín}ï·÷ðj¼ææÅãøë—^ôÖšë ün·{ˆI eCãs5ξþþ_—íåî—ïùNÉøußœØñÐÒ›V;î™ÛÏëXýë»zùä·6/ÊãjÒ$ÑÎNùÇ¿~¦Ú¯üýéï=}Ç+oºk?Œ%ZH<Ò†©2¥k†…‡† ÕhojŸÙ¿ìà3-€é[ ØŸ†°ÎœæFç=tèPuuµßïWU5“I+0k`D UUý~uuõž={æÏŸ©õ¯¿=‰mà8ŽÖ$øèxÿ¸lyÒ=ÎÌ GZš K@‹'¢¢Câ±Ð ªÜÎ#GÉÀ¬ž—¼,£ëèÔy¹y½¾4Evgñ® N’BX©¡§Ø' `fúîY–Y¼7O.*ÌÍdø×•áug‰%´„8¡Wâ"J ýà®C´^ ?[›ºltb=% Qƒ^3²·ÒViØHS-C€ˆ¢xÚi3ASgFVŽ_”ä’75îMKKŸqÖ’Ð@Ÿ#777÷À„©•dX…È=×<ºåÎó\_6'gÛ‚›7Ö·}ÝY÷:Àœ³jr}.ßÜð|í¶=핈@¢$I’xñ÷€ …ŠÝÐÔDA¶\¿Eئf¨U*¦CÓ¦€±ï°­GšjdºsN–jšòd3Šlo·Ùá=ºÝÝÝEEE‡#‰¨ªª7!†ŒŽ­_¿^Ç,ýFÂ<é“‹¢¨¯ +"ã3œž—ëK÷º23ž4Ñ)#„”D<æ;DÙ9ØÛÖ—ÌG/rrQv¤e;}é‚7>M”9@‹BÄéè—8)œÀ¹Ù@¯ÊЫOC9™àÏöø|®4w¶Ç& J¨‰`Ô%;D‡ gãÃ8‰XÙ{3N‡ ¶¬W¼°s׆ÁVר/S3ijò“Žã²²²Ò3Ò=Û“†xôêÓ&ön Iβ²I…Å“$Q$933“u›ç––¸H[qåNèÄBO>ùÍ-—þ`Ò$CªªjÀª¢(±Î·¿û¾Wj`âDÀ8‹q–ãÉ Ó-lÐPgŸZŘÖ?¶Þ3†• [“[ʦ¤VP7M ‚Ïçóù|G–e¢b"hH“HMÓb±˜(Š>ŸçùH$bx‹¢($2‘H‰D"‹I’DëÂ4«¢\Œôº²ÓÓ³2}¾ Ñ—Æ;eТñ„À‹¯pœØ×›]QN÷ŽtÛ(Oœ*J=Ž´L!Ó‡¼éàÊàE5A‘CH₲4àœ4mxç Õ¦ËK¦9äÎtoNfVšÏ™éq{’ €âjL ‚€8tÈSJ+qDlãÔábÓHú›‘šÑŸvÈ™Úm+ƒLÇXa› èUJ~:N! ³§– Ðž°ä•e‡ ÏKiŒ1ôÇÉÏì8–é€ï<þþOÏÉ¡eà9xAh[÷Ü}¯ÔÞñÂú«fd]yë%¿Ñ%ËÒgê—ê1lsg—y¬Ü Ùm:`*0›Ø¦\V® Ý`X5g#MÅÅÅÑh”㸴´´´´4‚D¢é–I"ãñøÀÀqÝKJJtYç¬( ™(APÅáp8Ò׫òæÎ¾÷¶Ãíó¤g:½^ðy‘Ë ¡H”ç@C(MD"‰Ü³güyRî©5¡øŸ±\€=™ÈçEnrº€G8^Ói1ˆwN¹PÇ6]æYe5Ãt Å^WºÏçõ¸½N‡aÓ✣XH‹†Ï*®a¿)¶Ùx+25à¬Í¥txì!ž Ð-O%Éá$ë¼<^WFyYoDÅJ0 I’Ìó<½cÎá…Æ‡¾ùß¾§/©ônøÍ-¿Y2É›ë»záGßË}ôÿ3ÑÛºçÀ¸s–ÎÈ&oN€p¼~×öÁ®±`RcCëÄéù‚5¤OÈš±iØz ëßfö?iþc΂ÙÛ¦Â[yD»m(ïŒ3Ö­[7qâD¿ßOì¶îA³¥ [3×­[WYYiª"5MSEQápX‰‡ÃDZØ.›5ëÀÇ•‡£N/NOç‚mºÙù|¾Æ»/þ~é¢sò¦N‘2Ò §F"ñÞþ¾ƒß^½–»êºi³«C¡]lRr¯×»gÇáØS Ï›á/Ÿ"eøx‡0R•p"0ÐsðàûooæÞ<ý´2#G!äv»·íܲ©ïÙs.®š2aJ†+ÝÇ;PHÄúµ\ó—ÚÙÞgUÎ ƒ4L{ï†OÂZQ` gj=LÓ³)YVl˜tÔ™þ$S »ví,ÌÏæxIUÔ¾ýxM}Q,M=7Ó_,ÉRCÃþiÓgfee‘mú¤°j<²,(±P”—}n€jÜÁxÉå’ùßXÓ4up0Ä»¼NGb‚ìuJtc¢tÛÒËešRgÈ¥7+uÀ¦4üðúººÁÁA›µ+éééÓ§OŸ?~uuõÑ£GÉ¡I4CEQ‹ŠŠÜn7zõÕW;::ÈŒ9Ë®¨¨h×Ö­ým˜ìuO’“—Ï‹|WkÇÑÆÆÆÁA÷¼³gÍ›×ÒÒÂúŸ¤š ·òq°}ÕäiP:eR®ß0êêîhi>P¿ ;³/œ]3Ÿ¬xE©#R$œŸŸÿáÇvw¯7],/›Ÿ'pB{WwCKc˾Øï… ÏZÜÙÙ ziÀ ®¦:Ø`ü­„ ØÖßn“W—ÊÐ.í-¹ªªdŠûرcŠ¢F£‘þ¾>¬©àöú|>/i>_YY±Ûlaé a‘ƒRwhÐb1`Èkj‘ öÖ4=¡QÎoëìY±ñV–“]ÈiÅ„Õ5¦üíSšj+„(Šééé^¯W’$²Û”E‰Çãñx< öööêû5Ávff¦ÛíFúÓŸzzz ئ[gQQQ"ßþÁÆPþø@peæJ&WŸ³P–åãÇz$Àï÷G¡Úm"áZMmg(VÏš½Èårµ··³o¤q•—— 6mÿ{k°6¬´#yþ<ù´3ª¹ÝCzdÝß6O]`Ö ~ŽTÓ<4Žé­´ivš¦rfÙàóùHo !Dœ:–]3V0ØÃ_l滲–Š&=A*gDKn³dÝô§apŽ5˜†ŸÈÌgchžt‹µ’ÇŠ›3BÈår!„HÚJE"Œ19òÌ”ÁöŽ.¼¬º½òÊ+ÛtÞ³ÌÌ̬¬,2ì±X,÷ôôôööÒéu•¯ŒÄddddgg{½^’=G"‘ÞÞÞ¾¾>ú¦x#Ù322<‰ŒÇã±X¬¯¯o``€†%m‘ôx«a0Sœ›j¶NFĶiÛbUÒˆXwÃ6$0mަ61ýI=@O°±ÂÞH[o¶ ?GƒvV`ÓŸÜšFÒZLg_aµ`Æ 3«íÙø˜–×@ôwÑÓ³_VQ”@ ðƒüàÆ_½,hš&I’$IìXšž'G"2 Nò“κ>oÚ”õÈh4ÚÖÖÖÕÕ%Š"B(“…¯dƒŠž¥ú:·`0‡yž—$ âñ8 4u;i—æl(û˜°Ö¢Z¡Ý´8,C¼Õ#̸'l.«x .ÛÁlŸ01vÛ¦¤VeÍ#íô»X%e/¶i`ÄrY9 Àøt¼½V2FcÕ ü¦¢²i`h¬gÀ³Ù,hš¦ï"DÛ@ ZŒªª±X Ì ›ÌÚ\<'‘¦Ç?€éÈ)!=»©$ØÂ'g%4-ÅhD‚TghˆÀ´<¶¥šòg+ÐP.û50ìº]=^†…„©)³j:úSƒò2m”†,´ØlëMŒi¶é³ñ†HÓ\ˆrsدfP(zAØŸ†hZCÝ"f­»HžV7†Ê§Ãä¥ùÕ°¯=,`ŒeYv86>9û1ld%é ƒÕº46@²Òèl5Y YÃè°mÅß>1 Zr¨¬òÒ ‘6•Cbèj -læ¤ÑÜèbÕÎlL‡­)“Ñ`Ûô§£3²R”4YˆŽu0ÝÅi“LOiZ{VcÈÚ³1x¨[d…mEQd@wLÀ‡B!„ñxõú¢[†ªª‘p$‘H°ÓK_$aŒyžwºœd8‘Å›Žíp8ÜÛÓ …Eùeþ<c,ŠbZzšßï÷ù|µHHìïï?~ìx__½áóO„ôôô|~ZZNuwõ4jB –Š=3ˆþKÿq ˜A”³a¡.­hKNXY;ý¦U­;Vô‹ì±mª‹YlÓ$ÂG릻„>£ÔÌêêêjoo‚ñDÜ4ÁL’(y}Þ &”••*]¯ÓC‡555§ˆÌŸI¢äKó•——WTTXÙüººº={ö  ~ñõ ‹W^^NŸ—fÀ§6´lÖNZ™MS­Aúûû»ººÈ´B45U…§ ‘e™cš‘‘‘––Œ7ü%¥ÛpñE3…Þ”x½dí Û>ÈØÌ™3§OŸž››ûÅíééÉÊÊúŒL:;;÷ìÙSWWÇóüøñã!Õz#„>ÜÜÜ\UUuRd&´à?Ž/??ï³óyú½Ž¿(üì|`¨vïÞíñxÊËËI$Ýj÷íÛ·oß¾ÊÊÊ“XŸBßìÙ³óòò¼^¯Óéž^9• c¬ª*Ù×:::š››[ZZÒÒÒˆ4õXÉ^‘oŸYÊ@vèci´ÓÒÙÙyÉ%Ë €dn € Œ14ô“䌑Ô#uBˆú5Rb=Æ€HZ„1—””ìܹ“Ø+ƒGÚÚÚzá…Jfêu)Â0bp ^Wöx²6d® 8 áùH¯M=è›CèîYKKËùç_0B= Lʇ$µêIÍ €“cD l'9ªæfo·Ùþ'0èe;¢ôÏÎÎÎîîî’’’É“'çää¸ÝnQÉ{`Ÿ:˜×9Œ!+++'''//¯±±±©© cœžžnª ¶Éä´fµ£2 ååw.…†þ ¡¯M·SzøÙLf”’b„Ä)ÉèÞ‚¦a§Ó …èËôÀhdÆIh§“¬³Ì )“ßú_«A Hi(à y3ã‡ÿ3ã7ä’P¼RëA_ˆv¢õ ×®]^xá…Í›7Ï›7ïšk®1-VŞ̌7ϰƒ0›"y”>9;] …z{{ËÊÊÊË˳³³].±Õ§tG$Ý %‡¦ˆ¢H&³œN§$IÔ4>…Æv<ž[@–e²v…n¦c3Ÿ‘NŠOƒƒƒÑ¨q¦Ý øM3’£÷åÐÐ¥‡¦‘4Iäx>…§¦ik€­ëBBÇqtK”Äá ÂÒOéÒ™¶N½ v›í‰ÙSOOOCCÃgœÝ]]ã®®.’÷ã?ž2eʧ%ÑÅÓ%±Q…†§öýmC¹èŸ¡®®®ÜÜÜòòòÜÜ\§ÓÉqÜÖ­[wíÚuàÀ˜NÄc- BˆãxQ’Ž8J)HRÊJòÞµk×"„E9÷Üs׬YCt ‚€1^¼x1‰>‹®ý ƒ·5šo×Ðаyóf·Û½oßþººÝät­þþþGy„\¹yóæŠŠŠœœœYÑõL‹gÕ7Ö#YÜ”LƒA¿ßŸŸŸO€½eË–×^{ÍívÏ;wÆŒ°{÷î­[·¾öÚk¹¹¹óæÍ}¡¾tÒ៟_TTtäÈrâ¥UzA ÏKƒTw”ØÆoÙ²eÓ¦M›7o¶Êe…í¶¶¶¶¶¶ÖÖV{„c ÷õõAž \FÓ¦i §—Žø~†S{زKOa› L‰š,Œð›g IDATs²À6'ˆ²(‰¼ÀÑ=uIJ¤% d’d¤sD–“0ñMteŒbëA_èf@Ñh¾ÝYgU[[»oß>Ò‹Åb‘H”ãy{49sæYg5"XñxžqŒM`µ—Ó¾£®iZ~~¾Ûí&jqÛ¶mŠ¢Ì›7ï’K.!«³ÊÊÊ8Ž{ï½÷¶mÛ6wîܯÄ|˜N!AÜnw~~>¹BK×z‰’cilU²Ù¬HGõ¦M›ìsÙûä­­­äð‚‚á‰Db```pp†O¥`×$ÙÛ+=~Ö¬YzŒ =Òbj!„5ôï߬¶16Ácoîå h>¬r:Äw@¹\.Ó°.’¢(l=è·)[•מ.ºè¢;v`Œã±Ø ÿú]ŒúÝsÏÉ’Ç.ºè¢Ñûn¦Ÿ‰½gn4#g£Kcû Ç‘S¾Bš¦Õ××cŒkjj<ñzDQ¬©©y÷ÝwëëëGY¨SŠB’$¥§§Ë²Ì¶²æ”D H$ôôü£±Ûcâ³¶ú„°MG¢qãÆ„kš …B¡P,xah€î£Ž¦¿4§‚°wï^b‰·B¦ ‘ô‰¶ 9.i„UCªöð_¶&â1MÕ†g¹PrzŒãxQrp<¢‘«bEQ”$‰àSC˜|?²Ìl‰cëAO3ʾ‰NO=õTgggOOD"‘o¼qáÂsÈ#Œñ³Ï>Ëóü“O>™•••››{óÍ7“GW^y%Ëêü£ÕgbÃ̺ô±m³áœŽE‘Œ9!j9П¼‘Ô*˜ ×}%ˆ4·ÛM:$R÷zˆa'‘8N—ËeºvlÛÇ–-[{ì1Ó4ŸÛ„ÚÚÚZZZª««³³³£Ñ(ÖgÏ‘2˜k @n*0Ïó²,»Ýn¢ÛAe™Ô…!Ò°ÂA8žê6â1EYFŒOŽB€Q’Q”9ž‡T>Qa蓜“*¢ÃÄ'ú…^l=8N TÀh}ò 6$ R^ý1ýi"‘ày¾««ëرc¢(ÞrË-$þg?ûÙ< ¿c|ë­·vww[}&›±444~>šC^ƒ´º¢¤· –••mß¾}çÎ%%%zÊ;w€aEÍWˆ€EQÔ/çÔ bK£o‡Ó3ÓŒXîñx<‹O™2ùúëoغuËöÚZMÇÐï¥éðáC¥%e€õaúB~¿ßï÷»]®H8‚‘~%8ƘØfk‰G”9‰CÉ“§è‘u»wó<¯)êŒÊÑuu’$ÑövÕNµ({x/ V5U‰?sM•ýäM/îáyqÃm}ÕÎ@ûéaŽ%Á@@Ó´ÎŽNY–£±(92‰øátc‰Dx²^ÒXìXš}=Äcñx"–H¨§6«»»‹œ“¡©jooo[[ÉÓÛÛÇUUÍÈÈ(,(ÈÉÉiok„C~¾ÿ¦åËÿû‰'Ãþà“&NÒ°fõ™lH×ÎC=J»Íþä8Ž›¨¬¬Ü¾}û† A˜={6|òÉ'6l€)S¦Œ¾¯qªQ—öºiøîQÚíx<‰F£‘á[]=köìêÚÚÚmÛ¶mÛ¶ ãäB%žÌ¸¦¾L’¼Ùù¬YzC¨¦g¤’þ®¾¸ 뮯‰³g/3Y  ëׯ€… jª* BBSI$X´p!9¨(…¹D‘šã0`ü¯ÏmRã1MK9®}ºýgBfkÝ Hf¥5Ä.ý =C2zܼyóÚÚÚV¯^ýꫯ¾ñÆ@rÀ¦M›,X@6`|µcF5MÓ›(}‚eÊX=nd3V ‘H$‰¨Š:40  ˜3wΜ9sjkk?ùä‚ð¡É”/ÊóbrBHFâB~^AAAÊŒ<J¢àÖìø0ë“ë2£ä¢Ä · ý=ˆC(¹üq" X’l=’( CkÀs"YÖ4Ý:ar¯9ŽeA”xGT‘‘GÒ­øðŽ?Àֆʅ0Ö’B"À˜tæ!ÄsÇ''Ë õ »]ìØÒ߬ZµŠãxŒ5ôì³ÏRž?Æ¿ýöªÿøÇCÉ -S­¬ª¼í¶Û++§ø™$IbÍ2Pds³Ÿ! «& P!†ǹ\®K.¹$++kïÞ½ÍÍÍPZZZVVöñÇ=zô׿þõO~òŸÏ_"ƒ@@¿Ö¥ž#ªi†ös[Ùj=G"‘`,ðd­B€1•rîܹ555Û¶mÛºu«0Ô;Å”.v8$ž2†”º-Wè/((ðû±Aqc„Œ0"î®+’Þ}ï0•YÃcàçœ}IÀâx:’ãÇóÝåv`šwÃ0ÿØÕÕv>'B·¿¾ã0Ça¤W½c¨ßŽ11Äã%‹— U ^²d±^LÅ‹¢ ðÏó¶hwbßã 6ìØ¾CÃɹ@²ècÇIÏyËæÍlÜxÎÂ… ÙbŒ1Bè´™Uü™NhíŠMŒi¼a~G„îîîqãÆ‘íäFž Ì;—ˆA–Ïš5ë·¿ýmssó¯~õ«Ÿþô§^¯×â+žr„1‡Ã(õrB ýmbÏ“óÛ„LBþF£±X<Êñ\²Y`òhØë°¬©©©©©1mò‚ O'&ÿÏ©©±*Jþ#?&- [;{V2s I–·}²M6“dYEyh"'ÇQeAH™ðw:‘0d·€ª( i·þy["ÅNl7/;‰%Q³gN§$ -BÕD,Éòûï¿O«¢(ª¦㬩* ó‚€1^e^äEž7­2ðke·m¾˜1£rnÍÜ þ~Æ™§_~éeþæ±îîΜìÜÛn¿íõ×_ÿøãMçœsveU%/p# ˜t ÈÃßö3]Óþöhæ·õŸzÓ•$‰ÔI†‡‰âñxô%\ÄÊ9Îïÿû¿ýío9ÒØØX]]Ͷ¢S0ÆŠ¢ôôôôõõ‘nÓå`èäPÃYÍz  ¨ª¢$ŽÇChF#â„2[ªôïLGJ’ƒîZÛ'€!Û¤÷†ÐP'ôÿ,¦»,dFHpr<¯**ð<ïežçE^à9^UUŒ0KµJ’èÝn#Ä <'#DY¬býþ?âyA%‰xŽ’PtqºO.#„\ p< ×@#Uä¬[bQàe§S%ŽC¦õ@î~0œ—6ºo‡ üþk¯½nVuõ’%KC^nNkëñÜÜœŠiÓ*¦M{ÿý÷g̘‘——´CnüÐÃ$vÄÏdo· Ÿ™9fÕˆY!ŠbKK‹Çã!wt æ2Fp8………ßÿþ÷:T^^ξî$Œ±ªª}}}MMMªªÒ'U›VŽCôšJ~+Ç ¼þiu<ã‘w;“(‹)3I#‘5뤀¦ƒ62#„Þ!I2ÆÉe‚ ¢$ÒzÝàÜ:œ KÎh$[íA€G:Þtç‡t(‡¼C/¤jU:Ì!Ä™ïdJF%e& Fñí@A¿  9Ìá/(Ø·¿¿ €háóÎ?è|ú Uª?[Ÿìx˜áSÚ˜zÌt2²A*‰>|˜ã¸ÔëŠS$“e¹¨¨¨  €\m^‚S†t`8p ¿¿__êgC)ãÌlj·ÛÝÚÚ–žž>46Ü4HˆÀˆÊ'™Póa:Ig°Û6‰!éêC@,ÁpÇ®£³C÷²NTf¨I8CÁS'ç†%—y~Ûñ€øa qÙµI8^¥2PCý¯†¤y´«2ëcX>àñxFþv€€|?bÌo¿ý¶Ûo¿•F8¹.' ][ñ ×¼BjKC£»{Àà“›ºèz.‡Ã188ØØØXRR’™™i¸à]gKùNq`“BÅb±ÞÞÞ†††îînlCÁéÄ$œÜãIßãI—6//oÇŽí‹/F˜Kzy¤b’÷nëö IÜp H5Òœ p©ŠÀ&1pÉÿÉÑk2cÊa8¼g÷n¿ßoŠm¿ß?J™‡%áôª~7M’„8j=9Õ`(Yyè8IBÜÐuå£|5ŒTÅÅÅô¹z®¢¢¢‘ëJÎ nhÊ ›µuN×ÃIk?ÔuàFoܸqtƒÃÌш,° dútÄ)12•xðàÁôôôœœ—ËE9ëŸh¿,"Ç*E£Ñp8L–cëWÍÛ@clzÎiVVÖþýû5M«ªª".ˆNl‰I1D‚ PGxHú::;víÚÕÐØHýëëñãÇ:™m$á%ãþíOG¼dØÐ=ò«é§l=48PYYI„Ѧ !4eÊ”;wŽ\C=s mE‰0üblÄ›6mÚh £Õ9§j¨YÏÜ´•Ó‘Çïèèp8d}>ÝÎOq"7ðè+yɰ¿®M±M¯•F+V¬ˆD"ä>0ºeèa²¼­­Ü¶ù¥“Çã!›I²²²LûQãžžž–––ÖÖÖSDæÏƒ<OaaáÔ©SKJJô ¼›ššöíÛwüøñ/¾ˆx'N$›X'œNl˜ß6Ø%˜MÇÏÙÄVfÍÅÝé?é0£}Vegû#Ê6Ju‘ éõ:!÷I’äñxЊ+¢Ñ¨Õ=ž0Ô-ñz½†Û!¿xÂ/%‹v>±C¬ä<2±wŠw¨N”ôvf8ÌÐß&a² ‹Lí~1õ@Ã@Çë9ÓrZªUœÍKÿ4ä2… »qJÐpµJfú^Cél$×—òÚØaÓâÛ¤'Øv:GH˜±04‚D"ÑÓÓc>ŠNÅÐÖ[ ùZ5/6­ÐJ}š*]r5ÝôÊNöÂ@:lCÇ@ÅÆ²˜V[Vg± M½-=ž>å’æ0Ê{‹!;ýêO—×T¤ÓÛ»Á6l°mš3þ¤½lö›"Çê̼«UJ°më6i€ql ®“½9ªI›‚‡}×è±m#çˆ"±XµIÿIÇ,^œÜ>¨Ÿ»‹†FµÇ!”——wÿ®%6ï£1£ÏNÏ_ÞŒRU•øÞúöX2ýlðàAV/¤çæoþáá³/]nŠmÚÙÓ4íÞ+½—.˜c4FcôùÐ_76\{õµ'šëÞ{ï¥÷„%s?[xcÃþó¿å ×“ë¾(í“‹¢Ø‡æžØI,ÉÑÑÔý6¯Ñ“~Jéo“Ã-2„gž[ÁaÕþ¼4„ }a¥c0aÂ{ŒÆhŒNõ…rTža\€U¼ "@˜ã8‚m=3ÍÂáp«…ã#s£1#{j?Ò_lÒ½M¨½òo”Ø–e™,Dƒ¡±36žOnèiÃÝv¹\ᄆ³ÿð”¢xÝË.‰ýÛ;×_V ßÿò…g—>{èôRÏ—-ÙQ’Ú4ìúû[Uçp,¼Ã‰äŽúeê04U–Ûucî‚?Ü}÷Ýúå3äŽ^õä™J†{E ó:Ç)Š’P‘ªZ_I{*ªa€=ÿ³ô£ç•z ëˆÇ§ºØcôOCÑp°µiŸ†qkÓ>o¦ßáJ±: aêÚfÃýÍÆx €[þðwîÐ:ÓÙuÈ݆þ6=CKÆÒÇ+È|¯Ø©BHˆÀë7ÿªøÿÊ   hÇ6Þí»ï€™ßýóåW.âVýà_ ÎëþÃoAÅÅÿƽý¾?ýñC¨ºí‡¿øy¾b­kW^wåN€üÅ~û‡7å8¿Ü‚Ñ?õtw´màÛŽ6e—NÏ+ΠŸ’k*>‘Ùbµ”x€A€âI"‡Öé€%‡£‘ôͽ¦ç®Àg  h\ŸÉ9û§ŵ~øúEÿ±dÕ/nþ¿~ãŠ,  / íãøù^{Ëlµññï¼ô¬sò¸àñ潇›Ó/z䣉Ýñöçe]úÜ·Y¾æ§×lÝwËÌ⮵×] ·¼ã¾×Ìý¿¢9ßXZùe—mŒ¾òîÝ7Mz‘{÷î ˆ~É9lº ¢x<®ß@zÛ‹kJ8¬iš¾èÈxgÓét»Ýöãä.—ËÍ9½ó{í;äîèÙ_ï¼lÖ©]ò1ú Pwg{¸ý€xòž™+V`Œ}>Ÿ¡¿mpêêêZf^7»¬à -ëI$5‚Ãí€X N‡<šî³ E@eâ1£ÏJÛµþöü<ÓÍjô š!þþûïŸ6má@Î]q8G€p8LîÓßa؈ŠD"/xSFì¿ZäºÍmô׺ ÞŒ¯Ìpcô@o²Ã¬ŠëHÔ²ñz[wÚÉ#$I’$‰>/M'=µ N/dfA…£1ú'$gÛð²1Ó3'Lã1s¾ §ô·mŽFP¥ë£ƒùðÀI/ÏÑB½ÕR{¢¹xž§7q¦Ü¿MæÇhkΞû‡Úu$÷³KÿÅP¸c/Ü4·ö íê)PyÖÔÀ‘H‚ ï–ì¿LÇhŒÌˆ ìÎ3ê‰Dfff$!ãd‡ƒ8׺—Í󼪪‘H¨sÈ:2畼{Ä=ž†ø©LÑùé¥S§–WT\ …¼^ïï~÷»ß­éŽ‹yëžXêKOj¨>üø¯I/ÿ Ç—+ð‘A(...--å8.ƒÁp8Læ·ÈµG’$l‡B!r¶)ÉHöŠ,›œ—f lö†´S–~|EéW\AÂdÇÌ 7Üpà ۷o8¶±>  ^)mÊ>kíúîãG¿LqÇhŒ"7Æè`öx<Á`ÜUHî“ §,Ñ‹Scœ²žœ$ý‡±Ûííä–œÞîÎCÍ-=]j8ŸGt¦¹ÊªÕ?i6Œƒá¶á®^ó“dÒ½B©¬îè\”…Ëg¹³E«-]‰Ú#ñþ/®@fâeË‹&ÉÙ"?ZuÄl‘¿ÌŸ–ÅýК[3ùËO÷d‹xïŽÁÆgã¦eÿïΗky%õQšW˜ ©Û{ÆvòŽD\nz¤SM .Ë2¹ªª²,“»DHFãšS`Îå4ô½¿Zv»'p»Ý›7oÚ¹§±¼¢bÑœ9¢Ó©ŠboŽÕý¡` ‹kÀ¡‡Ó“d„_Þ1±J^×±z€Å‹ü?¿ vÜß=tíº>ûædÑ¸Ó ÿrÙðäÜÕ[Z®z-dHsÝwJ—OžÔ|õáú_w’8~woIYC|qîÊg›~y ®žlG¶×™-¤bû›ÿ2ñúâпÝѲý³—dŒ¬IŸÄÖ4- ê§§º\.ïÐÄ.±j$,3›­³¶Z¤~Š“¦áP(¸{ÿ¡¹þYB ýaèíÅ‘¸¢¨œ "cÓ8MÕL’„ Ò§åýçiñ•/{jW"¿@^0Ç'·™ûá‚Ý¡ÿøîÀÉõ»¯¨ÉûöÛ‡^1ñ?b¯þ¹³ x3c–¯»¶°Âÿ|ðÎÝòŸï-Zö/½ûx=" ưݶ¥3³.2ìÏ•t'‰îîîP(¤ÏR³x¤÷ƒlKœÒߦ»Ü†Ã\èÍ$Ÿ…:-ŸÄ³d_¡·hŽ;w”§¬¢àÀ€/;‹…v 2 ¨*VÄ8ÓD‡V¬.œ@?¾8¿ÿì¡_èoÖ¯$›ÄáÛß)½©HmŠIùÜË7®)Ìzàë9~ps]û÷^è7;ÿ•+œO>ÐüJÆÍÎ{á ×h~>î~ù®Bo_8‘íñË „ÃÏ=sôùV€l÷ïnðWd п·uéÌúôßIæ"cáµÍ‰Š .Û `!±ÈsÛB}ya…í]wnS”ׄž¼À{å$tßc»™µlÜ• »©ë–§{Ž”Lð-.ä_ø ‚ªò^ø–këæè¬3Ó<Ý ·<×{ÌþËŒÑèˆt¶E9vìØþýû#‘H$q¹\™™™ÅÅÅUUUn·›$¦ç·é[Ÿ8òÑ âuÒ†ÈûÏܹSo¾ºñ¨  F£<à0Ä5ˆ¨WA帄ÊEâNã…XÇU4R©ø/‚Xðu³y}™ç®Š|mW]ï>1ý¿¯Îñ ±wÞn{ÿˆRZéÿýnðˆ¼À‹Ù2€G¼è•¼B†‹Ë.ôäô65D—kù¿æ¸èÌìŠlaïßÛ^þ¨¿®e>¿×wU…±È£¿ ’ {ߺÂË×fŸÉ.…Ïv’³HD€ñ~ÃîB €«ªô´ìíßÛ­eOÈyt™ ¼É“#çx\‚ƒ—œ™hØÛ­eOɾlliÓI"½ýû÷oÞ¼¹§§G’¤ÜÜÜââ✜EQvïÞ½zõêcÇ’Š”ã82œ¦±‘øä¹+ôÆ`6–&ID?÷"eL\Ô¹û55šb¯x‡/câ¢à‚ èq8Ý! Q xQ â„c8’PãĈÇ5ø˜‚‘Ñ Î9Šûi“dhî öôýÍÏàÌ‹Æ{6¾pø¾}6†rîŸX53ã´×B$ãMÅÉp €è‘Î…ÿÓ 7-ŸxýÏeÙÐíÀåE¾Ñ¾#0ušçÜl~Ø$'ÞNšb¯ûå; ²A{çÿÚS|iø¤¶{€”5ÞSQ‘ý«{?¿«e-ý8¦F<#,“ç`ãKîÜ¥^¸È_â“‚Éc€æŽ^µ2œ>›{çJ÷Ø×'‹B½½½GŽq»Ý.—Ëív“ÑnŒ1YiFwìØQ]]ív»£Ñh8L¾ (ЇI÷Û8Æîñ„¡3¾€"Iž\WÖÄÀñ”Þœ+k¢ä9e3 »wÜ ("ã@DÅ…Œ¨ñ¨˜‹D0âFì6s" pYzD¶ïÑýBSÛYO¿—te'e‹JS ñð@À…#©¼‚©Ý%š|»ìàÔÀ++Û/˜X4yVþ“³òØ‘ IDAT›·´\õZhÞéùWQCb ?ÙÞioÝêφø«Ï6ÿšq¤`ǶÞÛ.ÿVéOg¹¿½@X»Q~,óEIf ‚ÕÇÕš!£‡ÃàÏvžfL ¬^€üSû¬­¯q×ÖÖæv»=Ëå"À&6™@5‹ƒÁÇ———K’äp$‡zÉü6 ¡PH¡—ªc·9Ž‹±ƒ*Ÿ¥•œîjPãÉ_^r§•œy¢L‚Áh$ÓŽ¢‡`¢„¢(UƒQ-V"QÕ›å닎¨°‡ûpU¾ûšÒGã1¬ £dw]AÀ‘™™UvÉ€œ@ ¾œ5U†%¡ª1€@èÚ{ëO›–~×5ù¥5ù×­nzþ¹ƒÏ›H%ÿî6D¹ãÈë#`ÝÁèOgÉ ¤NstGšbP•åH‡Á~€)¹ÞЦ sAôHh@êáRœÄ ¤Ñg'EQœN'™î"£h„ˆû­i9›x````` ‘Hèð$Ø&{<…ıOœ¹g¸\.}K7¤N‰r:ð…Ü;ÀK®´’3{W“Ÿi%gòÒ öŽ„Rz÷ " ú¡g@ëéWAyÓò‹øÌ4ÜÛÒ¿³v@Ž-'üË·z.º1»êâÒ?ä´?³)ä(všz²«¶ÿ½Æ¹ìªÂÆ—»³OÏ›í‚`C`ÀT ÏKÏ÷d,«`¨€ô‰Ù÷œ®î½×N »ûϸè"ÿBø¯;½1(riƒX»èŠ‚  ¦M>?ë/Büõ•ý;®½¶ìæ XñСgzà´*ßÔptG\úÉÒ4hkI@áìüW¯LßõÆÁ›7ÅßÚ«ªÉüÕùÁ_rþû™Nôý©Àëyýgãrûo|°¸oßâ¼Ñ—}F^@óÁˆ¹@ct²IQlAÈbb·É_2‡EV§ÅãqÃùäú°· hŠB¹fýmúéçMeàøŽD¨KtçxüŸæÂh,8uñÝDã :@ÎärÓ¤\Uì:ØQÿöšºíûR¯ôl΢Ï)òN8Ð}ãKø¯çL®ñÿª&ù´-D y{×ë®’σd÷²Åd¤TiY׿#´qž3ß}1ùˆxïGÇî>€À-"«^k™W\¶dñø‹ÔÈÿÛq €ôAyøë;2Ò~xcô7u~ï½X²ÔÃgB'Á„–Žý0‘ÓÈ ˜i»M°MàIFΉD4 …’~.éoû|>@ÿ{ÿµJúl¯×kï“ïÙ³gMôª/¬l‘Þæ®Ý¯ç̸ܙYz¢yÏä_+/Ë>PvMi‘+;T½‘ú­{ö}ðñá–.c’3·ÂÞxëÞ)Ù.Ô½½åk^rZ±ìJà°å/ù©^ ÖwÓ}`4µ@ öÄéXõ¦­¹Û¯ìm]ú‡Ði(…¡Ì“Ä•cŸ±$ gæó€†ö˜ «©ÅrÀ‘#1Ó¹«tõÇð¸lɉÔÑçKwŸsˆô¢eŠΉ%'æ–¬Hûýï_QQA2*Š200 IRòlV˜~)«Me_#³Tö}šc^Ömo]rÆôhhi]¿b}]ívÍ9Î]X-ú–øfð#ç€ptÛŽž»W¦,öÚqdh‹©õ&©p}kª{-ƒàð uG«‘ÃgEu’òÑ‘‘]­zÛBõÇ0ëþÒVàýÓYpJf¹ÉäYsJlµa›¦¢(æýmǸ™‘osî º>iöÅ'ȹ3.ÿtyƒBá/{±Ûæ(œãȘ–6³úD9|íá#ŸîÕ£¥îÁÿü³ ­Æ…¢c4F:ãL6ð©D°M˜‘Uå†=ž$,¤c¦—¶€ÙÙ ú"ÕSŸ²*.¸èˆ 4®\ÿØ®¿®~é©»ê^¼óéõGàÈGÏý豕¯­ÝüÎKËÊG±ÌÇ\ìè«7–ÜüXÏã+7o¼ûŒWÖ­ßÕ£@`ËõåË^„[ß©ÝüÝ¢]/®¯Û×€H÷¦7ßnŽ€•TÜR(¼yý‹­ ô=2ï©•µµkïZ¸þæÛVö ±}æž;7Ï{ü/-ü´_û«GdMÑh4I›Û¢ÓÜqúÚ^H½sˆÌª}祚„êîîÎËËûø£Çʼn@ ÐÞÞÖØÐ˜™•)Š¢,[:­]Ÿ¬^ ðÐÆ×–Ÿ•tE»ÖýßJ€¿¼ùÈÅÌzò©w_¼ù±U÷\òC€À­u‘ßÌp(ñe7¿Ø|×ÁÚ&cWßù.Åð©ÍËkrºòw=¸²î¥ýƒWMõniºúžfY€Àê'î…¯xઠ€Y¯m\•¹à™wêϺ•·¾ôáo®ò*»ß¹ùÁ]‡{Š#½-0Л˜|éU5@àHý¾žðp¯L̪œQl(ˆQì…ó_|>¾â‡—̨©{êÝÊ'AhMI¼2°Ê÷§áÈ"ˆfRzL¹YÑŒë˜J Ð7¯ `—,èlïz§ö þ©V»ôµ+Ñhôûßÿ>ã¯Ò 5¹(ðu(x4ǧOŸž‘‘qèСP(‰Dº»»Ë&L˜9sfZZšÍ©íG,<}zÓ°¬,´=1kÀrð ,œZâ€äiYŹ@nŠùY6§*¼…SN-ñ€+wÙðóõíICçL¯8ÞÞ»ë‡:ÔÍÅ.¨InÔe ômX6e(±œ=f•c”Ê‚›)Û_¾§úê“¿¨Z*-.ü§¶NúUžç/ºìèãfûÉqiƒY'Ýlþ9ûÛ¤?·xñâ`Œ§M›6cÆŒêêêË/¿ü‚ .Áår±jQ§¬ü,€õ;š‡»¾Ì"€æþ¡ˆ`Ä’€4C@ªÚ$';$=¨ÄP$I9°°89Ê”ðÉIlÆŸË1ᎿâÞÛ彩{–Ý»; 3–ÿ5¥¶ë‡ô —¹Ø €w·4@ßöM+!‹$ÎXÙСtíZ»L~£TÜÌ©oãõW?xÓŠZŒñÁËèª3)ì?:éS³úVÐË—.ºè²o'ç·ã©‹Åb±ýóŸÓ''íÜëõ^zé¥sçÎMKK#*O„ŒŒŒ³Ï>{þüù6‹ö j–-øQõ÷ÞÜÞÔ´{ÝËon/<}I%Ô-þùÓM]]Û_}äú•°ì›ó CÇÏ#NK¤~ï’ïÝëo¾ïå-]]MÏßsÓz€.(OID…¬{þùw·G\ÙÅ¥ðÿÙ{û°(®4ïÿ[ÕUýBÓ ò"1Š&43ã[Èd$³8Ž&YÁùI²#Lv~×LxÌ&ì,îÇd3w7?`gw‚f3:»ÁM“aF³Q“ÄÆQF$J"Ê‹P ]ÝUÝõüqº‹¢»A|‰ ]ŸëòºŠâœÓÕ-wŸsîsßß{fØõfºÄ mºmšJ–íØQ”•_˜ qåº,`ÃÜõ;^y!-fi`ö›‚>Õ8£oÛ–F¥íSúÕÄ!» €óZGëÁŸÖÃöágÝüÑš†8·ã™-?øÞ÷žaètº°°0å˜_ü)E4•BŠ¢ÎŸ?ßÚÚÞÞÞ¾råÊêêêuëÖ%''# ò,™¿nÞûÝŒü¼Œ:È«ý_k67©Ê]U<¿¦@^ùþÚ¢t€3¼`2&17÷¼mF1“ÿ.yíš¼þväæ/Ý ÖÊÃçÖ$2àY`2“¦a±¾kÇåc…ù…¤cùþ¢ä‰lÛ ÑÙA{ó\@ù‹¿

ª¶¶ÖétÝ• åþȧNúÉO~r‡zÊáÝwßÍËËëììüõ¯-IRo_ï [[[yä‘ÜÜ\N÷öÛo?üðÃsæÌžãÁ‚Ñë}6$òœC >¾mYƒIÝEž0“hé7ºâ±¹îN1&ÑÂ|Ç †ù; ö;Þ\¯‡xµsÀ’Í­û¢¬ùÛº^^}}™qFó=-£G냵°Ê6T”jÀ À`1éÁ Àd¹72íyÿý÷ÉNÙh4’ý²,ŠH4•Hú‰(­¨¨X¼x1åÓi"å™O¿äS"¥ rÆÛL†4MŸ>}:"""''‡a˜‹/._¾|xx˜ø &±[Ñ›üª3z“ÿ­Ûà ÌèõÌ >CÀèíus3J埬ûÿö[z¸òúÜ„òí¬]ENJ?gœÑ€³í6{·eÙý^¯‚ÉâÝÍè-·s·˜”ýûãÇé+-L¯+H¥ÎÀ<°Ð´pš¦cbb^}õU—ËEQÔ[o½¥üÞÿ}±±±!»a±þ ËöHÇÕáa,Ö%™ñ^£‹ÿq×™¼Žîáasÿ’ôäI~…Œ3˜6yèŠÀ.Zòp¢Z.=yÅM‚Sþ½ºHd¾™¨çy>ж©ã)xä©F£Ñ,_¾|ùòHS|jf9ÙŸ’ÃJãúèô•7R[&Ä iZ®BÓôë»ÿyFL,Ÿ~š_q?åuˆÇ“«¨LgH®| j’$EšŒM@ým?ˆÁ‡lŽg{{»Ýnçy^’$Q¯]»Æq\xx¸Á`0™LIIIqqqSýŒ*¡Ž2öÌ_/D§°,+[¸R$•poæ9]žžž˜˜R9M£Ñ8<<ÜÕÕ9kÖ¬ãǧ§§Oè!WQùÊ!srMÖàä<›@TüÖäÊó0Š¢x>SâeqII’´Z-¹ž5kVWWÙ„üñÇ’$%&&^w(•¯²k&ײX"HÑKS5•‚¢T´ÐjµF£Ñd2]¼xÀìÙ³zè¡–––ÞÞ€ú×·‰Î£{6e§¥¥¥½p •ç9^uz¨Lš15z•»nå7ò‹!ÔPîdˆÔ»Ç㉋‹{íµ×†‡‡cccÍfs__ßÌ™_A]y±íÅU…u»ö?ãÂGƒµüxÿK™!}–«ef×Ý?ùr¨g` ÈÖåòåËááá===Ë–-ëëëëéé‘$©»»»§§gáÂ…·ÿµ6 ¨üÛÿo} ð) ‰a &%9·U½ÝìhLÿ(x2mTî ÈÊš\+kŒ×fåÊ¡Q›:uêÔ±cÇFFFþøÇ?644|úé§_~ùerrrlll||üíÒ]9P½¢(ŠZ»§å*ø¶Òæ×%‹2²³³Ó²Ÿz½ê¿NõI ±eß dеk³©´µ;Þ¾­dQ1ÐT˜I¥í㸖¢4jÓŽC¤E뾨´Ú®móÕ}|*·ƒ@Ååo‰þ!ùC%A©ä>ßùØÄúä!kÛdMž››ÛÕÕe0V¯^Ͳ¬ËåÒëõæÒ¥K®]§°'w⩹«ê‘UÕpd1.^4zuWê­Ï54orýîõ¼üŒþðs¯¯I¸Þ¦šŠ&äUí­ú0¿¸ðGk¿Û˜—Wºkg~iÁ®Ò|k”«ÿ“¼ü²%e?Ð}¨,#g^ùÞÚÇ«—æ¸0è|ó¯>öÛåy5eçóK_J‰{Ð`J)z¶ £8÷¾%]åÖ?ZówÔÚR"Ƕ¹3¥ÊCTñ)ŠEQ¶^RÍSn£\t+}iÞ30¿<°@ÛÖëCtéF>©#GŽœ={vîܹ]]]z½þÒ¥Kééé .ôxc ÞFezáñxxžï¸0pèØµ?vÒ—D3ôÔ}æþ”¨Î‡ñõEsæÌ™ɲ,9ç"½Èµ·F/‚yËüæ¡ìK#gúÓŸNž<922288—””töìÙ¾¾¾þð‡$ŠÌˆ›Ô|zž[iñ9ê>à½kœw}]Ý• 8‡€¬òõp¡åØ8­ú†GÌöüÄ›­ýÆëïl¬^o ÒFeA–â-­Co59[û-ˆÔi#t&Öiˆn×h®v´_êýÓ_|[¸paTT”2[EyîÕf R,ä–ò0Œ\Sw¼þöôA«ÕJ’”””¤Õjãââ<Ùo§§§“Ïd‚rñ™yYØY’ñìÜæŸ<Àvž8ùÄÃZ±3çÅêsÿ°nðU…õÈ«Za.ÝÀ™V?÷òJþbÓåõ3O—UÔ0³ ø¶m™‹—îý°z#Ëê[ݯ“ŸXoÑ·îÙZX‡½ç†2?-›¿aöUoNõksËŸ“ÊíÄãñt}éxë·#­ýÌÐj#t³ÖlÒšÚ0­e¬í½”áäŸIR·\`[Ž''ƒ0FFFˆ\ƒ<´Ÿ_¦é]““ïµ#GŽ´··Ï;÷Ò¥KÊýö²eË&ê|Sº+€Y– 3¾Z«¬¬º’¸æÕæ½Qå¿jnâï­]œ_xÚ:פÔ'ѧ­ðx<¿ÿØÑzÙLÍÔ³s¸Öl2DEè#Œº°0­žÕP3Vœ?Õ5çÂ…ØØXe}?¢Ö@¦qª¶¶V¢»"¸>omm-++»³opê9vìØC=DÓ´ËåúòË/•ûí¡¡!ŽãÜ¢x¸±qÍš5ãs»uWø«Ýœ)>ZˆGw¯_U‚fÇ;ÇÔ"lj¬ÑOTEx2mT¦€ÿú¯ÿzñ_ìâÌåÙ³##µ½œgÄEGE#Ì“Qo45´Ô{êw÷ 5åää?~üÁ$IF“F£ ÷úÒÈŽ %TÒTÞŠ‡fާÇã!®H–eIN‘F”$)222""B’$Z£¹ÞámÖ]áÛßJ°–È?TÂLâ‹c2mT¦I’¾ [úh\ö×£44=ÀKŸœÃM:sDxd„.< K1)×>ø/‡Ã!‚,¤MâX¼»E(ξƒ¾L(Ç¥y<žG}ôóÏ?OÂÙétΛ7ïÅ_¼“O¥OÝráÌŠ/º¯ »°èë©·VcKeâñxì’vQ’ÉlЈ˜¡Ñk=F=®gÃõ0…CËB7+ê´Ãá·YVÚ)Ÿß|¼Ør?4k­\¹²©©iªŸ"}bJzâ 뚨ÜUH”)ŒÑдG‚†¡4š¦(š¦) ¬¬¢¢(*#Õ‚øÒH¤ËÄ~òМ·UT¦ ·I‚ä¡Ý4Ü"x<hɆ£²a”וJ§ø¨m+ë)wÝ¡—¦ê®¨LPÉCS´H”ÆJ¢(PðH%H¾Si¿ý¶Ëå"1£kòÀ8Såuhæo«º+*SFCó‚SÐ:ÜÞ…kâ†4¬@鵂Žáë à—æç'ÇX;5TÝ•)D¢iДŠÕ€ÑP,C³:-<¾•´sJæ`yfàéóxLrJwP YÛ@Ê8ÿ ¿õx<²î €––£Ñ8±6ÃÕζ‹}#,fž“mbÀsrªÈ,òy˜ÈpŽÑ—6˜LzFä9ÎÛÔ{Gn ò'°”½äÞ&‹‰:ÛÎ÷°aq ‰Ñê‰×݆‚G‚†MIZ Xšf)°€F‚ÖçûÖjµò–Y£ÑÈ{oæ÷ÿü£Ìï¿1AŽ'ù1dãÒnUwe uG¡µLQýÖÊþ+ Íò=ë–Ú†ÙÏ€³ÕDe”(šVöK…õO™ Çt/Ø_û³õéÑW÷¸¹°¯ò‹Z~Ž¢‡Ì®ãÝÖ~;úÚ»ú¥çÕã²»Š¡)š¢ÀVËjX†¡h4 †…Û'à·ß&á*èÿzõ B¹F/’ì®Äh4.[¶ì‘GÉÈÈHHHà8®§§gœÞݯü…µ¬»êmAr õŸ³Þ»cIÕ°n¯ïºrnÿ®[Má/Ž]HRWVý™®+]]].t])ôš¢µÜÖuåÂ…sÇ뫲lu2btòœf`†.rñ÷».\èºråL}9€íûmW®t]¸Ðõƒ¾|¥¬>«òˆCú»Î>²FÍøºË @A¢)Э¤DCb4вÐPÐøæ_9¯[ÎîöÞàr¹xž——éò˜Rê44uN¡X“+!Ñ>áááÇOw…oýïR jÏ<¿&L–äÔìäT€o”˜¼þûù(­ëïéH†§9i~|ôèÚ™€1sã£MˆNLLþÍ“aQþ†—?ª³¼_·zK|¢@TR €Ä…s££MÀ]0 c‰OÉžTM.•éEi(J‚†¡¡¡ ÑÐ Mk4 FÁç'WæxŠ¢(Ç“{õÒüâÒ×äÊhó‚¬É%HhÞd#3žîJûGY[ò‚G™ ^ãùîwÿ¹ @Úƒs}·Ï7ý¦ñZ8†]ÆŒG3£6Èú”ܪ,·_B"»&§à‹6Å­ÍBIɪºÆò#¯=·2Y¶ï2$J¢54톆†† ‰¦$†¢X Ö·‡&z‡ÈžqŒî ±õñ^€ØyÈî·É'ES´ßÔMQ”N§#éuãé®`6Ëï6õ¥« ¥°}ÿ™Í)rø€­$/w|èè öÈèÌÀnn±ø­tÍ,{6gÙªú²çö6¿¾1ýúT¦4MQÐh@{(†Ñ0Ša(  4ç_Aä•£Rnœþv€¢ŦŒe eW9ù.ÔÀ0LXXXDDÄxY 8¥?Hš X·ï=\»€!\iýYÍCäƒ'3øDË_9QE‡‰aâ7¾üNÿ™†-VTägìëÑÕ]Šx$H‰¢ a(V£ÑР%h(0 ßJZ§Ó)dò~›^·û%ò—JûPþùÊ¢+!kÛ$}|AÍ{Ý•¹Ks[é?ÿ.¨&%¦goþieÊr×êV4 ¶û‘ï]=Z[jCÁ÷²&ïñ¶¤¬þ—·kôö•©L_X $$Pn$ŠQ‚G‚Q„àsò( „Œ9ߦMó»=hOeÌiÈêœ] sÑkà IDATЦ!I€èW›@wEŸ’[•‡â¹¡¶²è±jûŸý»î{ó_­†œ#€åÙÚú_ÌÍËÍýY×ÉçMÐgû¨‘lÄâô'€¦¶ZZØáážOë6”ÔÛÿö©T€sNøä|ÇÁí¿øò©ï­ž7Øÿs„ªhzWá–(xD7$Iã=¢èEˆ\"ôà³W¿z`‚ ŒÆœ­= ¼ñy[vTÀ·b#3aoKÑ.SÙ³ù; ­;½· vfÁš“™À$®y{ïs‹òKÿ~ßšLնœ _÷¼æ¡ÿ˜k*r2*Öç*ëüìšxk2=›d¡ÑW"ý;‹+|¯»}¿M±«W¹ E4(H€‡¢E$zà$@FweÊØON®½ú䲬9¹KÔú MmŒ|jޘؼ™ø/¿óÄö^½ÅBbÃÞTôJÙøº´ñur)I›ýH¯>)UZ¿ñMi£òçÔÍÊîú”Í’ðÔlj"ô¦è[ƒP™$Éí‘מ_œSS (¨ºP@æa?Ñ•¥OÁ–UY“<:Zô†½[ÊòË>êøñúd5ŽåÞg’ùÛÊX4bÛ$LAÀNÛ/;Ĺ%Ý}jµD4eãëëÊ®r"}´O´Á4V5%()«¥ÁEXTîA&—¿=QÌ©N§#±+~q¦ÊëÐŒ]¹]º+è-ÑêÌ«21“Ì߯X4€Ãá`FU¸&ÍšAªîŠÊ2ÉümYç”ò¯Ñ `ü°GÕ]Q™B&™¿àÂg¾:€~Õ*ýDöÇËUQQùª ÀP ®—¿ E*'9²ýíç×$i†¤n🢨®=0222pm€œ’„9žçÃÂÂTI•¯šIæoK’DÖá’$‘˜Ó}ï~¶ÚúÍŸ·‘ó1AœÝ˜8jô÷÷Ã&UËGFFìvû‘#GN:5Õ¦r¯3¹úÛríQÁâ^‡0í¶ã)Iß ›8v%45• ©‘|eµZ­ÉdèííO’AEå60¹úÛÊE·Ï—& öÞEåtí‡,Õ²kr»B0táÉh0›MýýýÇ?yòäÄÝyRPyn€Sž5ˆÜçótò­­--­mÝœˆü@8q̨cÆò|ÌKAì¬Þ¶©úhç$>€‰8wð•¢WCñåŽàÍߦüò·á—¿„ð0–è®°>˜T_‰ÉcXV£ÕÚå:tŽ?ôgîêÀHOOߟÿ|öèÑ£ã[ß’•û/-W÷”9Êl­’ËÙVD™ßháÀµnK3̵Z32¬‹æ'˜v‚³íŽ ‚ùg'H×¶=ߊŠúþ¾6ßËpu›£VÔ Œ}éÇ£¢£%øƒ9úÞ®¨{ûÓË·öÙÀq¾¡¦´U-úU!çoÓåoûù¼iš~웩€V:Ùäaå˜J¹ZØÔ¼½©†¢(F«ÓŽô²ï†C¿n½6tÂeþ¬eovró·¾™ýð²eƒƒƒt7Dg…¤å¯ÛÝM~Á" Äîÿy«Â†Úæ+‚à¸`;rdÍÓâïw]¸ÐuåÊ™úrÛ÷Û®\éºp¡ë$ÿ«{a€º]åÌR§˜¡ó‹ –_:,Ì€YçŸÿ÷USTvËä'bª Ũ¦;Â$ëo“O™¤Ÿüú tmí–ñárÀP>ý¦(J«78.]ÐòÓÿ÷ÂŽ¨£o¶·uý×åo5zÌ‚O¢fÄ]×1@.·i+-ÝÓ ‚÷~ßå‹´,Ã0úÄÔ•+S,Ð[â㣣ç&ÅH\87::>11žˆ$r-ÿ]æê½6¯ŠKðÀßKÇù–†WŠÒ(ŠJÛôJðm%‹Š¦ÂL*­h'vìX›½í•ê›Ò(ŠJÛ¶¯³ãhEQ•½mŸªÝv˜dþv Ø! ïù¶Òèe´ B3æEQ¬VÏwœ€àì¸füãPôùZÚߨaiÖqR¯›dsy•õU¨+,8Ø øCem°ùÖ¨¢Wv0IÖ§SPnšÅ¦7‹a­ìºr$ xý—ŸÜôû2¶šÒRäï­Ún«+­nê„>öÛåy€5¿tÿŽ'Ó ŽÁ³õM¥Åf>SU¾ÅV‘?wþªžòÚÚò¼¦Šüßv¨»ì¯·DÁãv»% ~ùÛ’"{}ýKÍ•y°ú|Ü©¸{~ ò¶×Ö~oÅŠµµ•@EE½ìüö5Oèçr Án÷ ‹AÚ î„‰·ñ*·‹IæoÚ,¹?ªsªâVµüÐHž•¥/}–úPWÓï©Ø«g~K¯§”6“&~ëî-ŸÀ txå}Öï­Î0‹­oþ¢Ø5ϼ×úße@eÙöÍéÞ֡ƺ’’ÚÚ>ji‰ @`#îKŸ ŒJ¾f.È©ÿk!š¹êtåµÂÀYVØê~Õ˜üIJøþŸL.[iÏþ¶Íqœ$I뜆fO(tW´^ó—ž5) º\$»{ÂÞ¬0G°kJ½«OÞØ\ÕQ\gfÁÞº³¸t§÷7 ç~ K™³Ð0³^#;Ýð6P¾>}ÔôÓ×[Kêë~×^k*r2*¼¿ÈªúÍ2É—ãý¹¼ï@›eM€ÉÌë»Ö§´WjSË6üL_úôÂè£Ã ޅߟfD£,fË ­Ù„0Îý󦼼¼Ó§O/^¼˜tEqhhH«Õ†‡‡¯õç7ËÙ\š¦Ïž=ëwS©©D*øÞ ŒÞ4Êø1¦ åU¦€Iæo+õ‚h3ŒçˈPÞô³übæÌ™f³yW¢r/£¢r;™\ým’µI®IbÑÕöj!^Wµ3dýä4M‡lœÊÔrõ·†‘íœoÿM¶à”¢¾Ÿ÷T?¹ŠÊ”p#õ·ÉìK´ÈZ’àt:Y–Uî¨m;4ëy<žO>ùäÔ©S‚ (¥‹ÃÃÃ322RRRÔ¯<•¯ÉåokµZY«ÓÞÖëõƒabm†ÐŒKóx<===999¢(z<9ÑÝfkííí=qâÄ’%KÔý¶ÊWÄ$ëoËþ3YS‰œÚzK„M‘Êqi’$ ‚@¶$Z­Ö`0 £Ña^±b…Ãá8vìXh®hTî“Ëß&kre§·7|5{']Qv5$I"¡ö†!EÌu:MÓE/^¼xddäôéÓ$·‹öû¸ê'Ó ñ‹ªm›ªŽ~qUDn¬Þ‹ÊWÊ$ó·Çó…OÖ¶CY›èYH@‰‚è=))‹½ÿÛÇŒŒØl¶ñ“äì?3E™^ùã}⛆ï}»¢îí–^Œ'¨Â5›£¢^=1¬³Êíg’ùÛ²èŠ,”Dî{÷ÛÊxrÙ‘&Ÿu‡ì~›À0Lo_ßñ#4¾÷¦G°SÅjÖ=º~Aê²Ë_~90pmxxØh4몪ùðÃaóîôß ̲œCPAü# ‡ãßj¬ŽÊd™\ým²&‡o¿-q­ÖTMó»9uoqŠ¡iºëÒ¥û¤<ðà×,X<ÿþ”ĹóÑsöÏí‹E£ÑŒ¿å?{·¶áT?ðÕÛ²)Š¢ÒÖnZ›–½í@ìØ±6»hÇ+EÙ•¶{à:I+ŠJÛ±¯EþÐŽµÙ/$‹á¶}Û²·à¾ã@vZѾ}»Ó(Š¢¨¢ÝÞ%ô@ëMiÞ@Ã=­ãç”5V{_‡Ê®nœ\¢¨Ø×ðJí§WÁ»¼•[äó·###4M“™90.\‡lýmgΜùì³Ï–-_ñ—ù?¼6008È}ñEçG‡>ñäŠÉ‰ÝêÞ}fÛËà|nqµ²þø×]Xµ¡Y}"ÇàÙú¦ºú&ëöÊý«²L܉õósê­Ï54orýîõ¼üŒþðs¯¯‰élªoB¶Ðw²©1É0#}M¶š¦|”×î}æäÏKJrf,¸ðòêØ†—6ÔaûqÛúžæ¶Ùq†q }_t.­ªÿÇ%á~”S¼­þ‰“[-×ÿ$FŽ7ÕÅ îÞÝï½ÜÔG­¢d’õ·u: DƒoM®ƒAÖ'—cW06Ž%,Ì_7/D (ª··766ö£ÿùpÎ}÷¹ã¸Ë—¿<ûç³Q3¢X–Õé&<›¸ÓuuȪ¬Ýº&È´U²¾€¬mòòj¸Ú¸£ØðÕÕ‰ Òߨ:TW\ñ^ÙšBRJŽ^ï?çXŸ¬2ÏWÌï“/^^Å ¶¾Ä­Ùœà;ZO*9ÃâîO‰7¥n~9"Ç ,MNúë(^— ÝǾ•[åÊ0#‚wI¼¼Î)±#n­QÒ8)Ú  —Ï«It“ˆµ’½ŒÁYSI†ˆ+)¯ÉÊ*Ÿ¡†ËåzðÁSRR.÷ô\¼xñË/¿¼zµw^ròã?9©o=Vg&yÒ •µM.qÈ›K~bgÜ¥ûFRöÍš—@‚ –äÁÖtz¦'*÷g¡&Ïš@QE<øöŸX3”<{ [ö½@Q¬Ù“WgÜîò{Q¹UœnÑé†Ç#º=.ÑítŠ.7<ïàäáàðe]+Eˆ·œœiÑðÅ®(ñ»£ô´…dÍ’““ÓÞÞ.IÒâÅ‹SSS322Ö­[·zõj†aÂÂÂ4Íõi8t‚ä“ ´«ÇŒÑ_ÊÚ&æ¨û€ó×|{d{§8‡ÏÖÕ?4Œéë£çT=²VÍXR×7JŽ3‡«¬¨ÙööIèS߶и5G ówn©m–$é\múnðs¿û8Z.*7ކ–”ââ(ÉCð@’ Oµòâš„?9ù¾§Øíö!ƒƒƒƒƒƒ× Øíö)z‹S‰œÅ¹víÚo|ãaaaƒa‹ÅòÈ#¬X±‚ VÖÜÓýkóÐT²´hÇŽ¢ì¨üŸtøXCH|øQ+l9/Vw\½ÚràÕÂzä=¹ÂCâR+šŽý¾åÄîM1Åõ€¾ÿÙ¦mßÓÖÝ}bOÅN ëáyßX]ÝØÚ›¼x)Lg Ä!» €óZGëÁŸÖÃöágÝ<‚®Ìø¶miTZÑ>îºÝU£¾ÍH¼Óãñ€’<´äñ.Éíö¸œn7Ü.xœp;Á0 MÓÄã£<±&ÿ4k%€Éd Ùý6Š¢.\¸ðá‡ö÷÷Ÿ>}:66öW¿úÕåË—‰crzi¦Íÿq¡v{Aû#YµµÛ³pÞ)«’`öY`tvÑ*kMñü˜˜Œ eyåûk‹Ò&#ïY &7ci vUmÏR.Ô›* %$,-¬(¨<²}e4 \þ°8Ç:7jîªlyýÉÅAŸ†I\Y»ÅZSœ3ßšö\P·*¡f€aÍÀìò0² Š0bƒ­‡°æ ºû½•[€¦éð0©í »Û# "7Ü\¢àEÞáàp81p¹Ÿ„ŠËqi2.p0›ÿ‰qôÒ” B »Â²ìùóç%IêíëmnnÖétv»}V=âìCŸÓ€ëG^ù››_øŽ …(xÖ³²CüÊ¢“ÂÿÃ9À k$DgIŽ˜,&Ï8ȳ9ÞY$ 8`ò56m|SZ÷¯<0&ýø[_Ëæê“ßýé “•e»a²èÈ“Z\/ô©Õ’T-ò<À·7×¹,˜&î®r‹P5'V8öIa†Î`Òö ‹‚Kp9à`ÀÒ$k„8ßœi0Ð4€Ðu‚"®㜑Sñ§4MŸ>}:"""''‡a˜‹/._¾|xx˜DLð­GQ±v=<@{ÝÜŒRù7Öýû­q“½ÉðË@ma¨À˜,~Nz½“H97Y¼Ç^zËuο8Ûn³÷é·,»ßt£ÝUn†a¾¾9ý‘ó7‡¿dfLá¬Å¤åu­ÃIÃã‡âåSñóãÍf3'išÖjµÀ~µ—ÏO>Þ¦‘ØyhÎÛ4MÇÄļúê«.—‹¢¨·ÞzKù­÷þûOý8ÜÐ9?3}¾€õ]¶G:®»`±.ÉŒ¿åó_ÓâÂ3g¾›pGΑM‹ ›A£Õºx;€ì±ýrgΜ©~L•{r@£Ó鈫Œ¬¬å}d®%É×äGîŲ,Iòô¶EQéI-M Rh4îCê®ëY³fuuu‘MøÇ,IRbbâT?©Ê½ YœE9)“,•Š)ò¼MâXÈ–›"ÇS1ð ,4mÃ0²g‘îz<ž‹/˜={6€––£Ñ8sæÌ ÝEûϘÂ'8i¾“ˆÕ?~ëþ¡h¥úet7!ŸC“¿@?c$Ú¦äšäŠ[£O.ä7h(ŸoËáâä#"!~qqq¯½öÚððpll¬ÙlîëëǶí?3E•þý1©lé~ìà8úÞ®¨CÒUÛ¾—Pj­È~rÈó6™ºåÖê¼-£,oJ0Ë–-ëëëëéé‘$©»»»§§gáÂ…ÁzO3Ý’D¦›Ôqf[uö¢7žtœ,Rϳ§9D{—\+ó·Çì«1v¢Vf–„ì¼M¾õ,ËÆØØØäääØØØøøø»HwÅÔøÖ¶µäAv´€Ø}tÛZoŸM¯¸¶=‹Š›`+Τ¨mûÚ¾‚ÏUå¶fR ‡¢ŒÖñC VÑ7ñÉÌiüÌ;,,,""""""66vü =±û@Ý»]# º+}•õǼ´¬®ÞÖtrTw¥¦¬ôøÒÊý»²L܉Mós*úžkhn®ße-ËÏøñÁ@èlªo:~ÜɦÆn Œô5ÙjòóKÖ×î­|.«¦$§üP'ÀûtWšëk÷Zƒê®Ôíl\\µ·j»­¾Ìºu8†.öÆ>s¤¹¹¾² ®ô¥WaˆÍØ• `[}ýºôدè³U¹-X4™1µþ5PýòC9W„,XhŠöo§(J§Ó‘£Å«W¯ŽÛêuWÆÀY»Ž4>¿@ŠótFɯN¾±13eã›Õày®ëÊ îìç«3S׬Í+=¿â©5kÔ5ù4Gy¾­,%0FÃØO´X®:âS·ro¢„h3DDD\ˆ)Ô] Àá•dÏ“GãÚm¢(ƒÁÔÚÑѲ¯¤¸Ï[ÉÄoæû:Zñ \¹r€à4ÃÖðî‰6µ’È4‡Ôè•­û`xx˜[]Àï Œ¦i§Ó‰„„þÁç\”·'7¸U1mþ (ñ—$¬¯­]úËÂ÷FuWLcuWrWϯ)àÓ]AFÞ³ØYœ›Qƒ‚]UÛŸ«»R>Ýîò‡ÅùDV[Ýþ²¯9k0£=×:²ÊÏ”¯f€GžÝŽ ¥õ¥Ö‚-Y¨)Ë]÷ØÐÉ´o?U’·ai½uWóÉçƒlÝU¦ ‚ ÈæIbWFkô cåª;ðHlR+Ï{¹29Æ1oLdÞÓBweTD©µ‚ð&È<ÇyG€”õ/ ŽíœÀZLzÔþã€Ã`1¦5‚ƒaЫ޴iŸ>ù˜½Ä‹¦Ì Œ] Í\ÇC‚Éå;5­ÑŒ·a™†º+dÁŒ6zG9£7M`Y• ày~xx˜\‹¢822b6›Al›lª•Ñ)kòÐÜo{<žG}ôóÏ?—÷0~8Îyóæ½øâ‹A{Ï讨Lgt:¯â?oûáa²Ñ¦V®\ÙÔÔtÓÝïÝ•iŒ(Šãî·åã¹uàš\>4SQQ™V(±”ÕéFãÒ”«îÀy[d )xž¿víš$A’<ä#EQ«Õ&$$Lõ£©¨x™(ŒT ¬¿-CQ”>$¥}}}dy#Šq7ò<ßÚÚj·ÛÇIüRQ¹Óâå%~qr?È~[>à!?ÊÙËwèI§’$Ï„|š­ÑhL&“ÝnïííOAEåNBô®¶h2 IDATɵF£‘÷Þ4|šJî±øE§…¦mC!«Ä0 «eu:^¯¿víZkkëéÓ§¯Û]´Øùi}|Øqð•¢WÕØ³»—ÀdrŸ†oÿ-NHhî·‰Qö5jY-˲‹¥··÷üùó---—.]špûÏLQ¦Wþx‡÷¦¹p¨¦ô”ªP}÷Bþ8å?Tù0›ÁXÝì·e‘°;üÄÓbÛÊðRš¦#""–.]*‚(ŠCCC0ÍtW‚¢¬º:¼' ¶-o¥å/QQÔú`Èß¡©»B„Ùe¼³·VK¢t#""Æ‹iñ1ÝtWø£{^ð ¬ìñýÎ tþgõ6Š¢(jíž–ñsÑU¦%$ölÌ™·NçÄzi4M‡fÌ©ä@¾Ga M³¢(Ò4í+u<b÷ºwŸÙö2Ñ]©³VÖÿºë«6Ô#kTw¥®¾Éº½rÿª,wbýüœzës Í›\¿{=/?£?üÜëkb:›ê›íô^Ý•$ÀŒô5ÙjšòQ^»÷™“?/)É™±àÂË«c}º+ë{šÛfûé®ðí¯îLªl¨[¡mýÜèýÿÖ™`Û™_œWµ·êÃüâ­ýnãf5Úí.‚ä Ë6Ë0ÌÛŸö}¥Æ›+b4m[òÕô$#SñØSŒÛC9Fh½,ë·Ê x^üìÓžkN‹%,&îz.Æé¥»" }ç/³õÔÆtß¡¦“ Ž\yse4–qo×½¢ w/.—‹ç½ÎPQÇ¿m}r^¸þ¶2‹Ûïæ”>ÿÔàvÃÎÑÃvÊÉÓ¥ÑÐ.—gÏ/l'?½:3Ú84(¾÷ß]ÝÝo¹LÝý⿯Úb«(\cÈ~áÀ€÷î²V|M^½k!y`J`·@ Š # xN§Óét†¦^š[”ì5l§ívjd˜ÖÐ̇G/~Þq-ï; ,ŒJ¾?|þü¸ß¼ûçë4]tWô+‹ª¥¡®†]M;7¼Ùâ³î¾I~*ÓR{ÀéƒçùíϤë‰/M^¯+¥È•X¦ú-Lnìe¢ìm·Sv;Ý~öš†¥®^þÍ{íGŽ\ÔéÐÝ5$×; œº+bgãîÝÛ86!e«&pÞ;ÈÆ«Ñhb¢Œ´Ûsª×ëÕýv 7eç(·n7ñ|×Öíû¶=aDÂbM ÊlùëvÿÅÉçã°H|û+eõY•G~óìÃŽ+ç>í€ p°: «þLÝÃf‚€°(Õ°CQ}rI’ü¤”ä åC¨!¯É•–ððpŽã¸\® FðKõ…n‹¿©ØX몳ãÀd‰Ï\½xHZð`|´||Íy{ÙJK÷ä¾¹9‚oX†a,ñ)Ù£'èæ¤ùñÑêJ<4gb¥kl4LöùÊg`ð »kR\‰eY¢©{SÁöŽ/OÚõìÊ`ñ,& þðÁÆ£Gjéä8‡WY_U€ºÂ‚ƒ€PcŠ[›…º’UìÚG;” Jç›~Óx´±ñСWCñˆ#„ðÓ,cÛÚ ÊÔNYiJŸÊ ßnÚÿ‹O£Ñètºððp“Éts‰4D aÜ™µ®4gÕªœÜÜòCÈ!Ý‚¢Ÿ5XaË+¬Ã|«ýø­tíÝž‡ú²UóÍÛöµøúÛJòrVåääæîü\1½§!«H2åCrß«aìž=#ÈÆìÃ0dö¾©QEçÐ7ÔEÉY•ÍäKö¢Tï]ç¢W×Õ ©¸¼æ£­™ø/¿Ó¦a‹ùû:ÈYÍCÞ2Õ˜ó{¥z±Ò—F ªš5ƒàsB’.¨y_7CάcƒÝ¶ÜŸ•[é['‚ýv¼^Hݼk—%Åõ£[w°¤¬þ—·kôöú¦éà¨Ü㌊"p:Z­6èÌ,o¶C3€V«•$‰¢iH úÕ”õOƒRÿáG-Ëa€0sÁB9Ÿ.»øEkYSÉÒ(~ÿñ‚Gîs\¾xô­Çì¨~JÙK·@é„ßúŸ{Kççp|ÇÁí¿øò©ï­ž7Øÿs„€>ÛG-ˆ„ `#§§¨“÷½ 9çR^Öè8WDv§ÝÙž.P¾úÄ’OÛ]yq=‰H6@]qF÷ç]Ç»g›àq£³?ÿ^á&Ë œ„k#èï—.Ñ ¸iÆM‰’‡’hMXÐãfŸ6CÒš#‡MT [å¶£Õjå-3I= høâZÊuýÊ¢ji¨«aWAÓÎ o¶ @#À$IRQº%H3•{Ù`‰ñúŽ«5A´I!n¥öÀÝx f4¿ùÍoÊ?~ó›ß¼‰Ò(#ŽaAÙ;»Î‰!t÷yºû…>»›2EÄ-ŒÉX7ÓìšqåtðþAòkLÿ«x;PñýmÕ­'8ÚÉÇ=/ Èÿ~Yc[GgG롃GÇS&;wï>ØÆ± ) èX¦ûŸ(@SIáîC-ÝÝ'j¹*i¦rï¢Óé”29Í›+B&t¹õ½sš™™ùÑG]¾|9...33ó&FàöA>LÓ Ðà]`õÐEÑ1Ú7{õ\Ï—ÃŽ/¾þàýykþ2x†5æ²1c†@üê²#U}«Š‹­PeZ™˜ùkÛþB놜ED?¥ÀæX9—t°¬Ž\‹ŽË%%ù(€¬çö®K5Øø¯çzÃÖ•äz+‰TÙ†³Aš©Ü«m6Æžo jkkív{xxx`þ¶2æôOúSyyùT<ù-qöìÙû·{úé§,Xp£}wýô•™3"ÚçmJº/lf4Ü"¸~ǹæ?]µ}’éZú´¥™KnºLšÈs¬AY¨Kä8R¼ë:]yQãWâ‹ç80˜LÌ„ÍTî Þÿ}2 FrV¥ÓéˆÛ‹à fL᯾újJJ é(Š"Çqf³9<<œb/.—v7ÎÛæÌ™³pá›+£k箥=°¿OºôågûÐým§/ÜšýHÌ“_¿u…)FhÂŒÉ2™ØF¯g­?`¼àÍTî=”±+$žœ\‰K“5À¤±õÀîRm½^_XXxs}“’çÿò?ö§¥]È]=Ûÿ {òö>›ŠÊmoàìë·ÉÔ-ß Ô]Qf‰…O?ý´œø¥¢2mQÊu*ñæx*KHc‹þÝ¥«q•Áét:²ÐEÑáp(Òàµül›¦éÌñ¼´··Ûívžç%IEñÚµkÇ…‡‡ “É”””7ÕϨr·â·ß–CQÄ#§êœ~uôôôÄÄÄXAŒFãððpWWWddä¬Y³Ž?žžž~s?¥`¾²t_p}òÀ30YØAå&ã%IÒjµäzÖ¬Y]]]Ë—/ðñÇK’”˜˜8ÕOªr÷!×€ÂÎAl›ÌéĶó·Éš<5•n/äX’|î¤ “Çã¹xñ"€Ù³ghii13gμÃw*yѶí_ŸO¹µÿU‘»ÚyÉÉã×NùîÎSr¢=óÕ¡œ«•×þ5zý2· N§S>4S¹9亟äB§ÓƸ¸¸×^{­¼¼ü½÷Þ³Ûí}}}wæa„¾³5u‡o!E€ï8¸–¢XsÌüuoV;^Ù”F±†„ùkN«ä_%dnVBîÓPtÉ-7=¥Ï×CN"z{{N§Á`à8Îh4.[¶ì‘GÉÈÈHHHà8®§§çN=À-¹P˜ð쪽•ÀŒñT ™˜Ôg«Ê󀤠¯Ã·USTvË„©ÃmÕÙTZõÝ”]<øåxÊ¡(Þy[9uNà‚ Ü¥±+Ó’<{êÔ©cÇŽŒŒüñlhhøôÓO¿üòËäääØØØøøøqGb÷ž6‘¯Ý´µ/´\´íÛ–]ôJõr­Wq%XKSzÐöŸ¨KP —àè³·m|,'ã-5˜ÄÍÏ=³a­OTj,|[É¢b ©0“J+Úw¶ñ•4*»úÄU@wõ¦´µ¯½Ö¶gQqlÅ™µm_Û„ŸnHCìT6lùHË»ßöËÐÌQýä·ˆF£¡(*77·««Ë`0¬^½š”nÒëõæÒ¥K®]»¼³ØwötØÞÃÇ“]'–æ–”¯ý«w6§ ®Þ¦šŠ&lÙ»¿ö; —-êoÌ4iÙݸcQnòÊoÌ~æŒe‹>MùyMÏUÖVžÿeIñß=½±1ÓÄø›¹Å5[ê›ÿÑ~àGùKŸ]4$g›Gp³[a¬>öÛåy5eçóK_J‰{p^öò|kiñÒ'}ýëÒâ:[ý…%áæÈ]y(­/ØV¿!yAìõ?âPE»B\9䚯öŸÜTŽWúÉC³®Èm„|úGŽ9{öìܹs»ººôzý¥K—ÒÓÓ#"".\èñx®^½¼³>õåwª!råV4 àä€ò ÒK‰Àº…Îkñ‰³™™-G•^R 3×å ^Z(¶9K*Þf1FÂ¥/= ¨8Ñ>¹ØÑrò2ä/v½/-5ú6øU-oX‹²_æ>±>•€çw䣘U«Ì,€-{ϬIÔ©kÖæ•ž_ñÔš5ª#w”µDQ“ãIò?•õÀçmu¿}ëÐ4½zõê´´4K¤E‚Ôßß?{öìÁÁA»Ý6QüŸØYý7…Å5Þªƒ>]¨!Xc¢ú÷[°ãµôWz!·üE]d —ï ÀµÿwÆÒbe·*ÛPÑíH%SºàÈ`Ñ+Z[P_Xlÿ»Þ¬&².Õ¶'@Ö0†/ß“¬²ÃÃÃ~ãÊÄ/âZSýä·ùÿô§?imëèæ´î)¢¨¢»·EgGÛ'-m@_ÛÉÖ¶¶nß¶-J+ÚÇy'lu¿jìà«»ŸÌiBy—Ð_•‡²Ü5;y‚Ó [û'ÚT_ùøÈ‘Q~éØÌÿeïýãš<ïýÿ×ÀüqdVu`GhQÔVW°§“õŒÐªµÜ׬›Éq="ç³ÉÁ­´Ã³2í9Ø9ØzÂ&ÐuÚãNt«¤´?=°Z¡*SZe* š¹Cn’ïWrs“DÃ/Íý|<|<îÜ\÷›È•ë×ûý¼0 £T*¥R)åk³R:$$dêú~‚ŒkΟ?¿hѢ˗/ ÇÛ«W¯ãÚ%oV£B·VUö®lUqñ¦ q¦ `SJ¨ôM¿HƒóU²^ëizy𖺌·Â%i¶ïî·åì‘xW)ÓZU±«µg¯—`Ïæ,Yad¥¨ *¹qfgì&˜bÈãž,Q#G·®BWÒXýYŽQe¸”?Ÿ¦µúÆ“‘)jÍoÌõÚåO}75G½)Å Ú×tfw’çÀ×pòr,ï LQÔgŸ}¶wïÞ©~Øû…S§N­\¹’ mþú׿ ÇÛf³Ùb±DGGqÜñúúŒŒŒQîÁööÚ™†×Ûke"˜Öò4Õ[ß1×oCo¯”‰èUB___mmíîÝ»ÿçþÇb±xìò)rÜ_ÞyrÆúØ9@/Oˆ!WŽ,ÐU«©Bþñq èØUìƒQ÷a§ Œè]™8„ⳞŒô¥‘³§Njkk{`ámmm~ø¡Pž(rw>===11‘xW6lذfÍšÇüŸ;w®Ð1·w¥±¶Äd(*|·€}°ÛX‘§+©>¬ß®2hR²O÷ú.ÙY¿7>=Ǩ.<ÞØhÐëc£H{mÌRkbJô%»R+t/Ÿ±`ü`‘®x©¡©©:š”§oï0ä>8zÀ¢y#SJ,_~¨Î"¯èpç® n'ŸûT¡PeåÞ»9iqÚæ,•Q—²ù„…k«ÌÓU™¾»yUØÜûÔ²s †g’Dïʨ$Bi¤Éyî|ná›ã¸íÛ·?°è/.}ÑÐÐ ®~ûÏýè]áN”jÒ Œ»·fxŒ¥¥!sóc)íEïÊ„Ad‡ä˜Œ Éz¶k}›ÀWï§žz*###88xppP©TzØÔDîŽûË»ÂÛ“¬.2í«½°{ýHéûÀ5@)“ºoì#Š^ô®L64b nM P(>ÿüóeË–?^"‘ X­Öi}Î{žûÌ»ÒV“£.2é›ÌÛ’|%x31é©Ðµ]ä@7®œTÇx'¶ˆÞ•IĵG/ òMÇÐÐÐçŸþù矓ŸŠí¶ÿ;Θ˜˜àààyóæ9â9MJJ"‹“ãò®|zTc„*ñó^$¸½+½°:äï]ióQòÛº|¡èû¹q%»Ö´~<ÿ÷ù|fé³ÙÈÊÑ”.ÓoTE}ij•®x"i¶¯§b[~–U¨‚o~\W788ˆekžˆ‹ [*µ* š,åIaëwí‚Z}ð™KšØöéEÈ>¼*`Ûr“ãëSªO–oá½+±Ï®žaq{W^üÃÆH]zFâ¥Ó ånïÊ7Ö%ÆEˆm÷2âÎ縚lÛ-¹ýäþò®oSÖºuäuI“9.‚zWfòϨ×.Ê Êo)ôãz IDAT=¸Q@ô®LBå™ð˜ÒëõN§“8•ø{Ç®œ9sæÇ?þñ?ô}CÀzW,]],ÀÌ-,Mô®øæ.¼+äBâ]Q(.ïŠÅb!qÎü­=ò·%‰¸§?¬w…™=ŠoÍ…è]™<öàiR©t 7 ~Jþ zWD&o“ 9¦!ˆ]áK{ôàˆ¹"þ zWD&ÞÇàqìÙóé_“5_DDdæ@ü‡ä˜ŒÀÉzêpþöØ ˆóäþ zWD&¡ ‘¦i¾ž»Ö·ù!8%Øß”à“B§á©ïDïŠÈäAö#­/q1yq€Íf“J¥ÂµwÝ÷óÑ»"2yós´Âv[€, y ‰—æ'4Mó‰xÁÁÁ¡¡¡ ÃðÞ••+W677wwwû¾˜ë(ÏÝZ~¢ã¶ïÂ~Q§Ýº¿MÌx$xɯ] çGŒ·=®j•Äñ¶Ÿøå]±ö¼U\…˜µknÓ°sÝŸWTÕj^ß=¿‚ÈÌD¸G¯pž\wŽçÒá"w‡_Þ)”€R6Ží¥2Á#ÏÇp2ž2"3“ÑæÂÇ[·E7ƒŸøå]ÀpòÍÜL"TÙÛÒ à:ìÕº£´->kæHÉÅÒ¬M¤¶î­#e[jöP‰{ÚnŽ,3¿¿È¤Áõøq9OðQóiÂ>¹8ÞöÒ'OOO¿rå ñ®<0¹\tùòe7oÞëUEõùeÕéYºÕÎXë¡gþûÑ›*½ïð‹ßˆlý¤/ÊçR†|îS…ꊂ‹Yy/ÅÍûº‚‰ÓîÈ^¡K_°êJ¡êUYEÙzSܬ‘e&å™,ø>9YaµÛídjmxï±=§â˜Ÿøå],@꾆úÝkÄÙήÈùý™ýqUP?´; .Ÿ ú|\êi8IÒÔŸ¬Ò¤GP—•nKàQFäžÆbéW\‘‰DBê¶w\9˜¦ç¼¸{ï @Jì·.1R `²Þ¾?åi8³í½¦J ìç/Dø.#rÏ “Éx±Mï{ßêlO?9»‚‘q,!!ã˜È¼+n\Õ8dN P°©³Æùþà z+_Ñ£ƒŽn)ßÈø(#rÏ@¼I¤¶’=z‰Ç©«\N%}E„ÆbòG)ÜåSä.ðÇ»B0ü÷ºÕ»—ÚOçè ØU«úÊÒg³‘•·vÏW´Î:óáÅÄg3¢øÒ£Nä-•;5U¨¾`NþsÁ’M›~øˆ©|[‚G™Éý,D&’©IŽÉlùö¾ùÂ×\šB¡ðo{Ä¥‰si~âw2¢Î§«–@jakázØr°õ2âó²Ö@vÓ·IÝ&߆“ëW–ekª¶ëM[bÄÞU¿I“ýô“Mëen8wŠ©£÷|…uÍ“K"­  ¯¯ÀÞ¾ŒÈ]CâL½ÇÛÄ»„1¢ƒä z»ýMs¬Åb¯R·ûsçA ¤Rš˜ItN'Ñ’&”;å®ëçï&lÍùYqo5¢ÈÝ15Þ•™ŒõbmEžìç»ÓÄØ˜ ‡o·I rK Èñä7âæ[^*Öm?™"ïÊŒ‚kɤ¨Ìò×K Oèr[y•X>v ?ž2q› !ç]CðÑ´,n§‡¸¾í/“ä]©Ù³•üeæVv°À¶Õ$&æ¶¹Ž¤%æK[Mnš¶ôHy.)^ÙìÊo«+Msÿ/—ûÏ4*­¦Íe^i?¶'1s'ÀvœØ“™HÊl­lÐY_î¾*­¼ž Ø•{øB°G~ 2ŠJÔ6”¹XÚê´¤(ÿ» ÿ¨2^g„I—LQ¹5mõû©´òÓäi;Ë·&fî?qsdÿþgîy‚¼ ç]í6ßt [oa.¶Û~B>ÆôôôÄÄDâ]Ù°aÚ5küñx <<|îܹ· "¨*ª_^V]–o2¨vÖ°`ü :«ÈTbhlª-3k=Wiìv‹Étæ–ìöN£éÌ9¶t+r6é.–U—eàùá» ³~o|zŽQ]x¼±Ñ ×?¯„1ë·:kÔE¦ÕËéç­-2D•Õ64ÔV§Ç*ô|Ñ‘Rfhj:žŸjÔåz°çÿMSSRkj:^­Ž—BþðÓ… .<¼wó¼ðãÜë0tDBF¦:ïâcÏedÈ b÷û ÍY»V)°½º5c¡ð(Є„„09$Ç®½þ¼/à£ÊE{ñD1áÞë“cÈÉè¤G€Š¶+–¥‚KF&æš‘šþÐl¯ûªY*Xß\½)š£§^y´VÕ¾§â˜«—ÔG¾.Ìûäšk Vd¹^¥äËZ¶}­N_¬IÍ?üö«#8»0Û<;#W¿¸ /]•ç:qñÖHß‹Ýf`\õvöšŸë³ š* ÿå-q¾Ë0Â|R·IC2b¼íp8„sÒÄó«bÓùø÷>¼wÅh4VUWøðÃßyç7ß|“¦é»ð®øè×ôÛµsg€ÔØhRE”¤V_jnv§s4B g®^ÉãžÚ§BÁÚ%ëŠ°ë¥ ³¨yQ€ñÏ#æÞMVÑv}“Óé¼ W»o+_£-wš¯ÔîË6m:ÔÜ ;(½¦Ï¢ˆP}Áîþ»;šì+“mø²®úš*@Ñu¾Ë0¤æòŒXßMZÌ{@‘ñ@BVbbbV­Zµyóæï}ï{O?ýôòåËŸ{î¹;wJ$’qyWZÚÛ›krtìÒ©¾“ž ƒîgum]mõ¯ª‹¡Z¿˜ì6ÀPûÇÓõ•¹ñYc Иoëòâïç–·ttœ®;r¢ƒfoxi@½õ›±æ'«Sœ;Ž5···Ô×kfÍ}&¶›í-Ç^Ñ`:ùI'ËuÔ—–k³H£ã–Ii0 V«`hþ¨­³W8¢žÿ°:Èú~A}[{G{Kݱ]ضŠ¢ÊZúØmJ˜jÿût[/  «tó:# ¯Øo”©Qžq¬ƒõ*ÐH$ÞaLæÈÈycøÊãMDîŽÉð®hßnêØ°"=¾ Rw5VýÓlËŸÌW!O¨÷íÛž—ǯ«)%Àüõ e=ku:U1”™Ìk ýÆ7bUþÎ$òµ‘üvSõ†YêU Ö›ªõÛUݺ `û®lW­^ÕÙú•œœ,äg©~&˜'r·C£‹¯8Øh>%¢ä®škT›ÖÅ“^}¶ÉºFa`³Û,껩9êM)Õ¾¦·¾ZžcT.åϧi­¾ñddŠZós½VXæÌî¤ øºgaY–W•r700@†ß”^¯ç8Ža˜Ñ<§äeKKKAAÁ”?ö}©S§V®\)‘Hÿú׿z{W¢££‡8îx}}FF†Ï;pG{{W‹3ÂÐÀYz-R&B>¾@a޵Xí*R¾÷tidJŽ¾Õº-N8˜e-,¤pù]Xz{¡ˆ`ä`{{áz/Že9;hFðÆká ûxÎÒk­`_CæÑ/»³2÷&ï½÷iqCCC‰òL&“‘Àpš¦Is;44DFÍ%%%>ø ¹ã8³ÙFÆýýýF4v["‘Øl¶)þõî'&Õ»Âøð®ÐLÄ(Ïh¹°~Y~ŸŸì{*Îãäu ùð{Ñr¹§’‰–&só!G¿ìÎÊv»¯žÄs:¼G/q {Ý"DŠ¢Æ1Ó#2*÷”wEñí_µ>¡\ä=¡.23ñð“óÓÞ.§’P¹_ãm1WÄî)ï =?6nºŸAä`Y¶¿¿Ÿ“ñ¶R©©ÛdéËÃI.<&‰ SûÀ"""ãB&“ñêÏvÛ¬/1ÚÔDïŠÈäÁqܨãm~qŒ/íÝ'çÍDîÑ»"2y·ÐºÊi¸ãÒ„½nïv›R¹ DïŠÈä!Ìýòô¥yìämb (J.ãvý‚,K’ð’~ãp8xï €æææÐÐPßn†qÃ~Q—ócSî¯v{.`„³tu\Glìhsál{ËÙ[vé‚å ³Åÿö $%ZÂ\Ù]|3.ÆœN üÀ‡Èd²ÐÐPâ]),,|÷Ýwûúúzz||ß\÷çUµ7G_Ó`ÛeR”T9gÉ3oöú.ÒYš©X¢Z±b…jŽ"³¾S\™éß¡ÌMpp0–Æ#¿”ÀíT‰c$bÝö¿¼+wð62 cFƒK+«.É¢d>ã>Z*ór 8ÜjvÚ/ä« ë²ßiUÛVNQiÍcFq§ŒÈDá rò×;ÿí­ ÜãonLÄñ¶Ÿøé]ñ4Ÿpí{3Ó´{KsÓFšXn‡|aÚNí–'ש}ç„¡«VS…üããб?ªØ£îCaÓͶåÄë£&™JÔÖX,ÍÚDjëÞ:R‹[jöP‰{ÚnŽ,s’ÈÝ ì’™7NaáSÿ‡†ÛÛÀõoóƒÃixêûâ®IOO¿rå ñ®ý·åryPPÐåË—ܼyÓ÷Å–ÓÏ-Zk@jYmÃr|ùe¨Öžsc•Á¨Ê/«ÞÜ‘¥+P팵Ú2·!ÉϾÞèËõÂYäàÜUËúùîàPùܧ Õ³ò^Š›÷u§Ý‘½B—¾`Õ•BÕÿª²Š²õ¦¸Y#ËŒÿ3¹+HÝ&=k§ÓI¼Ò¾‘H ÝtŸk`¼Ÿ\ô®ø‰?Þ•®ÿèi>a{|˜Xny¿†íl>su8¿Ù.]8މ1iÈ\àüX©ßÚ”‰‚ߦ?»1€$íAýÉ*MztuYé¶eD&{Æ“fX€Íf#=F¾´w\šsê?wí]¹ú…·ùð6±~d9ÿ‡):aá2“Y›p»Ý|í×6)|D¿[í6öaS ³í½¦J ìç/Dø.#2‰ümþï‡4Òül¬ý·ù|OŠ¢FKr'¼wåÌ™3·nÝš7o^LL̹sçzzz^|ñÅ1²q¢æE¾hYáQWFšX•›IÐ:ÚÑn(…>¥%LLz*tm9$ÐÀ+çÕÃ1ÞÙZ=ýÃ_õ½•¯hÈÑÁG·”od|”™DYÖ5oÉqœÕj}zþë¿ö Ç®gÂ}n4eÏz_ìt:cbb‚ƒƒçÍ›çp8Èx;))‰Œá]™Ÿ¬NEQΊ‹š^yPÚqúâ¬gŸTü÷ºÕ»—ÚOçè ØU«’ãö3ÓlgÛ¥žO;.ÂÔöqKÛü¨èØù ÛV£ˆÏzÝdÑ%D¬ßµ jõÁg.ibÛw¤!ûðª€mËMޝO©>Y¾E*•¦ªß×Ç>»z~„¼¥r§¦ ÕÌÉ.X²iÓ1•oKð(3‘Ÿ£ˆÞy`/í›_û®k¯?÷¶½.„£k2—&úÒüÄ/ùäÛOªÀT‘®ª†M,FÒ¼°œ=¿"`Z«*N-iªß™Ä œ' 3 ùgÔkåPå·Ü(û€ ¦˜Aò¸'KÔÈÑ­«Ð•\¿²,[Sµ]oÚË ¶èð®úMšì§ŸlZ/(sùóRÉEî"5äcWX– v(’7ÿ#¥×ëûúú†ñ˜KóØ£÷³Ï>û—ù—éyö{ÿ½+F˜OØæLÅ Fo:´mio¯ÝÃÄâ?–®.`f6ûÆY,œTAËÇr¼§ŒˆoîÈ»ràÀåË—“ 9޳X,×ê_}åð—cùÒÄñöDá¿wÀó‰fàâu; ˜„N/3{ö˜³`43+Ê8ʈLR©”¯žAAA,ˆ/yò‰µç]ëÛ|›î ©çbŸÜ&޻¨ªZ[1wÉ„=¢È=‹Ífº­Vë¬ei“Ö»Ö·CBB„í¶‡§At*ùÉ$xWèùq¢Ehšæ'Ë$Ɉ=z‰KM4î&ºEDf&$³Ë{ —SI˜Òíè]ñÑ»"2yx'ehþg£y”¼*r§ˆÞ‘I…o®…’•á}<½ë³ðX¬Ûþ zWD&ò§wø£ë$Üþ4lÅxr?fᇆ†2 Ã{WV®\ÙÜÜÜÝÝíûbܭe'¾¸Ó7½pl¿v½˜F}CrE¼5*žûo{@Ê1'|4?ùô‰šxWúûûçΫT*{zz|;•Øî·Š«°(W·æÎúíÖ‹µy²ŸïNÃ>ïc„¹"B_š€B¡ [‹„º s£t:ïã—w…3Žt d@‚ç¹ã‘¢´•§Q‰åbƒO@Ô„nr^À»β¬ÕjµZ­nøD‘»ÃOïÊ0lG¹[¶²531-÷ˆ`Û¤%jkjJ‰™E[:z?|¤8å\ýþD*­ü4Éï,ßš˜¹ÿÄͶÊx&]2EåÖ´MØG 291¶rž†Û—æ1—æO.:•üÄ/ïÊ0ì‘ï/ÒU©J þßµ› Híá ôMÆ,ê«_8ó뜜uQË.½ºÞ×ÌÜHqÊâ´G³Tyº”ÍËÍœývž®Êd¸´*L9kŸy†ì\æØes'ü£™Fäxz¨”øÑ»â?þxW†±œ­ªBj‰~gFl*«SÞæðëÆX9|±xÉ»á»n{‰Sv¿ßðÑœµk•RÛ«[3Ê„ŒLuÞÅÇžËÈê÷|Kì¹÷‹ w×À&ƒ»ö® #€õÉ1®W2á0Û ©ò_ÞâŠc%B5; Öí™±•’cá#¾Ý |¿9qžÜOüñ® c€ºÓçv''½Í§ ˆJ÷*tíSR÷=\õF)JWýM è:íK‚ÖÞ‡˜EdæÁoøEÚ`>Y{\ûxŠžSÿñÇ»¾2K3ÕÐä¤hoÂXPaÔ|!ãó+þèÉ[,. Y,¼À¬6RŠb)ݼΈÂ+öÿ°1R—ž‘xétÆB¹Ý¦„©ö¿Oc]bœèM™á;Ýžsidl "%îä7~yWh©P†KfÛï.¡ð'¿=r$z£^Ÿò[Í»6Î¥`,ÖÄ@vICþ²QrB³ŠPœÒXýYŽQe¸”?Ÿ¦µúÆ“‘)jÍoÌõÚåO}75G½)Å Ú×tfwÒ¤~2"˰€Íf öÙ2óƒm1ÛOȪ÷x›xW‚‚‚0†‘NžpÈýSË ¨ m{`Û÷(4ÈÞ¡puÕÕ&ëÑx{¯ /EHМDvš°ÍéÜæ¾ÝüGí·ÅÉKÍ#’9‡ÈqlF½Ýjá ¿ÓgR[Mnš¶ôÈÈß–öÒÜLRr«ëM}\뫘›¾f­ŠÚº·Ž”l©ÙC©ö´±ä“É=rÌõ9ì­kk«ÛOQE%NËpæ¶¶Y8e¶`- ©xCïqm¡i:\ÎZª~ý:% š.¥é?x_BQ+W®ãÚ‡Ÿ.uáá½›ç)SQ–îbYuY6 š¾k8ëÅcœºÉ€}°ÛXQS4·¬z÷‚¡æ­ñꪨüãM ›CÎÝ[øÚzŒ¦Š¬¬œúê’]©9ë ë:ô|Ñ‘Rfhj:žŸjÔåzXo3+ òSJ¿ö-u¡PeåÞ»9iÉ_…!ÏÐÔ €k¯ÕT™bão·‰kþ°êXíçf€=òƒèMÙû76×—=E£³¾hEVAáá†FÃ¾Šœu…õì–ncEΦ‘¿oLJoäJŽ75ÖV«ã£ß×z&,I»#»ª ½°®“ë<¦Ê*Êþ§çãää“)Þ¤Ö¥êõùjUAz||zÞ¾êêBµ)/ïÝÞ»ùÏŸ\‚ƒƒÉ šVK£þñðaZ*uhhŒOòRŒKó›Íæp:ε]¼,.44¤ýüç]×®>°p±Óá`Y+EQcDÅ>‘™KzæÆŒ=6 ÝpýКÙXmy«ê YÛ”pEÛ,À.“õ@‚½'öƒ¾0m!–d°Õ+ îì­jIØöj8‹¥7%8#£HaTùµM¯®§nÑùam ·i; rþóÄÎäŒS5û€üÍɳ-mg{†i”*A`€áWZ-g«* *<~hw€äd–ÊÒ ûñ‡Xl?½Š´ù€ß×zãK·nØ—enI¦XþèëZ¯b#HÒ½®ÿ°J“]@]Vº-ÿQYãumòì®ygŠ ¦êVó–8æt{VÁEÙ ±÷Xß&á* óädÚLˆ\9#îÑë'ƒƒ¶þ¾þ«W.ox>ÛøÞ» cb¯]¹k£Tžo=køô“OØÁÁ ç?¢ƒ:¯\v´Ò>°€R(#î¹Ýû(`îwÍÓY¾ôÕ~íSR×?ÑÛ É*Ú®or:ôjáMß,j[V¨wU)Ñ+*ºó© Ú£N!gvúŽ¿³Ã¤ÎqÎ…×Ý—ÕºSǽ_yìî£Î—óU(P¿ÒÂŽr­W±‘ôV¾¢!G,2˜Égnw?ªë䌄oø.¶€D"®i ¿xÄxr? e˜˜¥qo¼^šÎìò IDAT”òØæìïä½ü/ ,ºuË$ ´ÙƪÛÌ‚Õ*š?jëì½Ó…á%Éߌ/þsùéæ{Ó¢ †CUK[gçéÊâ" õ‘Åœ¹ÏÀv³½åØ+L'?éd1r4Æk[:{YôÂue¤=Û¾cõxÒU™g³aÌ[»§æDGG˱šcóm]>Pðý½GÚ»ºÚ›ëëš;G»MG}ee]³5ä+ c|%„ö}­W1°m5E•µôh©Ü©©Bõó…ûL›þÏ(žÃѶ8Ïû}8Œ…uÝ2öÇÛþc·†„…múÎ Ÿü¿Æ?ýÏ@QH¾•ùŒB¡¸]¸þì'r·C£‹¯8Øh>%¢ø ªÄp7[)8é*@Ç>ÛPö×êt)Pm/ÜÞSpMàAóPµÐX£ß®ÒèÖUÛwe£¸jmôªÖT`”®«„ږΈø¶._g(*ùîº1×\¡CfËÁÖˈÏËZ[ÙMßÎHZ_بGŠf“¡Ôe¦õIóáë÷µ^=¥Ér5¹…‡µ±4àëZïb}n ×Y—¥©Ú®7m‰e[txWý&MÖÓO6?:â“ $íÚ°yrøW˜È1™-'Ç”^¯`Ægþ6Ün†O?ýô§?ýé?ôýÄÉ“') ‘‘QDqsþü¹   ¹óþæ+QQ‡ƒ¦i–e/^<Æ8–xPîf6‡³X,"âKK¹RUë­j`éí…"‚‘ƒííáë 9‹[Ûpõ{V¬+zê’óÕ»Û©µX •ÒÂ_ŒµôZ¡PÜî—åX–³ƒfF”ò¾Ög±™Í{ï½Gf»BCCÉœ—;äIJÓ4YÒ#Û‰ØíöÒÒR>1ã8³ÙFÃíRó9OλÄ%n?´‡„„H(‰m ‘H$6–%’Jj»;øãA¡ÆsÐ;ºª…q'¨ÉGÍTh[ºÞ_WdR—UÝõ¤rÆË)!gÆ%`¤årÚ«œ÷µ>‹ÝGðcòWÄÿ!Ñp·éÂula¦'©Ø¢çÔOFÙÑqÉbé044de­J"‘Hz{{pœ=..~JŸg¢T-ÉL­Ê¥qðL"wiÀÉ1_“tîQ÷‹í¶ÿ¬ZµjÕªUÓý&JÕBGÄ&ˆ‰èÓ ‰]!Çd¿mrìò“óhä,嵘èf™™ÊëÝúºÚmÒtóg½½+ü‚x@áp8>þøãO?ýÔn· SdÃÂÂV¬X7Ö†""S‚pÝK €OÛ&§<æÉ¹7îp8®]»¶nÝ:’žÉgÅ™L-ÝÝݧOŸ^µjU`~ë‰Ìl6›Õj% ÇqV«•D‘JÈÏ<ò?=.šN§Ón·SEÐ$ü6444<\ùØcY­ÖS§NkÓl‘IƒŒ·…ãä¼+ž|l9.ÍétÑtMó‚ ‰DBQ’åË— œ={vÔE¿ÜG géíõú˱>O‹|t/?qFÎK ôûbÀÕjåM Š¢Èê¿Óá(ÎÎ qޏ¸åuïýéøñã&“iT­‚Ÿ‚ŽÞÓ‘‘ßzÕkaiRFF¾vÚ3˰ù`rddòiß "÷?|­öHʦánÓIËì±² ÷ÞìT¢iº»§§±áõïrØû(Š’‡¬~b㲄ÕWÿú×ÞÞ›ýýý¡¡¡¾®„òî3 zË5)‹jÏ_î¹$½|cUí×áuZ$P¶Õží6Ù£—À§’€‚K¨H$’+—//]÷à×Z¶lù’¥q -±sŽsŸŸˆˆ ׇÃuÖìÙJ2s+;\h®¹f1Ÿdf¦Q‰™ÇÚ]?h.×è ªìTÁw×S»_ÿçëž}„k¦ÚýúÚë,ÀuVºß"1sOs/‹Èýïfà!ç%,tñðÑ,B¦õù§“ÖÖÖO>ùD"Sþ}Öÿî¹¼¿ý¦ökÉ-6ÙÿáXWW—ð›rtØ#?ˆÎ*2•›jËÌÅšEÏUZ€Îº‚YE1…ÕM†¹#L†K·¬,-•+t†üãÇ~ü½lôðyGƪÎ[ž=«êºí×sîlHõñÆÆÚ“¡¨ðÝVx8X<k"÷9ž|(ÊpìŠp&Ü;ž<`cW(Šêîîž;wîGž|`Á‚A»Ýb±\½ú×sŸŸ‹ŒŠ”J¥2Ù8ÖÀ,g«*Z¢ß™‘$ÿªºyIÖoÏ[6˜ÊŠ *Ñ¿´%(1•U¨tÀ¶üP¥Qå׾𶰭ü ªŸH@) Oxõh98Koox¡ F³á`¹/!mRg…KZ®ñ¶ÇL¸wÝØyrƒƒƒ_ÿú×#""þò—¿ô÷÷[­Öîîîű±=ôPxxø¸f¥°>9†¼ŠNz¨h»Ò&wC,u¥16WäT(ÊKkÌ3M€Q¹Uf>´íöj®£ü]…K¿à¶„ ;XDîK„±+‚­¬®\az6=.Ì L#"ùÝ×­[wüøq…B±|ùr–e‡††BCCe2YwwwHHHPPÐíÃ{ìðÑ'×àÚ¹3@jltØ'f 5œ4Ë—šO‘²1©?©®þ^0€`¤Fï+¨Š*Ù¤Ï7kûï_ÕU«[Í[â•iÒßòyÚ·•´ˆÜË¥YrÌqLE[úÙ¹L.— Ã'½ÛíÀo“†a233{{{¿øâ ‰DÂqMÓ>ø`ddä›o¾9êß°c$&=:ÝÏê¾±oÚ^UCµo1ÃÌÛµ êœÇ·^Ýø•³ÅJ)‘¶Åíäkëù})}{F’G»ÛR©UiÐd)O >I¾ž-]—NzTc„*ñó^$@ä~‡Œ·É1o»\ˆ;_ü‡þþ~‹Åâsq›(ÓX– äyrŠ¢.]ºtòäÉ7nœ={vîܹ¿ÿýï¯^½J2é|†òº ¥Jà«áR BûvS~jUz|ôœøu©»kÿi6°0㵦êÂËYãÅåÕú|@­Z4¢ëm#s÷©¤Ã6ö*pUPÐl–<¾Y èÖªRöžÛ•­2oÚPÞ2RZ"râáDãiú‘®£5>¤C˜}r¸cW¤RéÅ‹NgwOwSS“L&ëëë«Vä ‡øÏ-"éÕzg¾Å€áml_ûÑÑ-r€;QºFv¾´GZ€cYZŽóM ]JHМ:p, Èlf ušI;ê´ööÚ™†WPje"GÚ‰ü@Df¼X…ïe“óôöÂN'£T*¥R©0ý‹¿’” ™ú‡ž!H$’³gφ‡‡¯[·Ž¦é/¿üòÑGíïï'ƒœ;2É1##ìù7£U9üËì²FÏÎ7Àb*U®Èl_½”¿ƒåd¥ÎصkŽë¤<Â¥¡#<}*"÷9ÛrÒÀ¬þþ>™pïº=000ÕO:H$sæÌyíµ×)ŠzóÍ7…ŸÌ{ï½`îܹ¤s~ȶ_j}ì‹Î›ýƒˆŽ8a¡oɳ\ÓÔ°òº]¿ê‘…ÕŸùÿÞo]Þrɺhurœ˜k°03~ Ìn·“Ù1—/MØM÷°¬—™¨ôè£>ú裓öò…qI oë#’ÏNZ“æëtÜš4Qfè]+¤n“Ð4ŽëNg˜°¨Gþ¶ˆˆÈLfT§Òo^ÉË~ù×$vÅC·"<ØØ•öööîîî   ™L1ªýSDdzðBð%6X­4M £J½ûä»gÐõë×á„Õjµ ÚúûûO:õðÃó:h‘i‡÷œR#Æ’×+ô}LD"‘Ø9;7ÄqvÎáp,^¼ø—¿üeCCÃt?—ˆÈ0sd…D.uíè±üí‘vûµÜû…B¡+HüÐ#11±²²òwÞéï÷d{{û¦%;ç±ýÚýõÚI»gñHâ$']KÞB! É sx1­?m8‡ÃáÞ´…V*•kÖ¬yûí·=:IC•¾æÒÈHæTßíKN8Ö‹µy-£xdDf"N§S(_‘ãés_ò’ÿ2L"‡ÃÁqv‡ÃbcY‹ùf×õkv;·zõjÓ“R©|â‰'îz}{4–=s¼víצ%yK$ÈtâôÞ„{,WK(Š/BCCCCCù—ëT’Éd!¡¡r¹¼»ëúÿžþ¨ùtcßͶ~óâ˜EÏoyžã¸Ë—/z1×¾73-wùÞ­‰E%æÖt´ŸÐREQi¹5]îÚ½¥¹iEQ‰™{[z€½z¶dß±œû¥•ûµ‰E%nÝßær§Œ*l°GrÓ3÷´X€ë¨Ë¤+[,·‘{¾ÓÍÛ“Èy Ü9žv»ó‚/°}rš¦Ã•á¬u êׯS’ YQQáááRšþðƒ÷%µråʱ.¶Þ:g0çéŽ|å…²Âí¦â¬EKÖ^+Ôë ÕÆâ¬?µ³¤@EAN}JYuY¾ÉP ÚYÜõâ±NÝäÜwÈÑä!«º,ßT•WnìÀèÂ7ò¿q‡ÉP¤ÚQâëéÕ O%0·‘ûˆá½ÆŽ9 ع4›Íæp:ε]¼,.44¤ýüç]×®>°p±Óá`YëmVþ¥°ªüÚ¦W×ÓlóÁ‚ UYÓ!mºæi Ý·¬ˆ†HÝ×P¿{ €8ÛÙ9¿?sp˃æï°«úä- ×R«+:s©ˆü£Oa‹yìÆ †ü%ê,E€ÔÚ+;fçy ½³{¡òÌápŒ˜KN²ñŒ&=4mý}ýW¯\Þð|¶ñ½wÆÄ^»ryμyCÜPdd€Û~21 £É(xlõR`¢Õ‚Êp—#Õ~ë"<3Á &.ÀÙG¸ ½„-ÄfäÔ%ÿº~þpÈðmŸGäÞB˜ã)l¤%pçs{wÂ…Òãi}øéD&“‡ÏšÅ(•ç[ÏZ>ýävpðÂùσè Î+—Nçm­f›+QÌ Øì¤>ÚGŠ]ÿ!sb¯|ÜÇæKØâAû±CΡÔ|Ï#r/!i{î¿ÍWzO¡S)`cNCCCCBBc–ƽñKý»o¤®³ Ú>;sæ³–3AÒàA›m¬YÆqd2@ÕÁ¯[½{©ýtŽÎ€]µ*9†¾Â; 3ë} [À¶å&Ç×§TŸ,ß"í8¶D]¤.iúÝó7“ç¬KÙ°êzýÎÙw*rsÝŽ0R·‰œï[zïõGQÔ¨[gvû`HXئï¼ðÉÿküÓÿü‰ä[™Ï(ŠÛè_¥R`”äk‘XS\}c% “ºûɦŠtU¤¶®wŸUzÞA2×}¼0㵦êÈÂß7-Ë«õ˳4gU‹À>`‚)f°ü&G uɯv&ÉÚãû¢×åüSÍ“‡žÏó(%ŽºïmþÝÐúÚ¶U”^¯gY–a˜ÑÜ $Û»¥¥¥  `šu:9yò$E!22*88Øápœ?.((hùJT”Ãá išeÙÅ‹ßåÝÙæLÅ Fo:´m©Û—2Î »:-ÌüÙ.aËÚ4Yúô:ˆÜ‹¼÷Þ{dÐÐP²Ÿ;tJJú×$ä„,o•””ð Ç™Íf]ICeáÓ#þ–|ޫɶÝ;ä´‡„„H(‰m ‘H$6–%IïþFõØa.^· |)ãbœÂ‘@@8#æ>–JH—L&“ñûa”>y`º0Œ²£ã’ÅÒ`hhÈÊZ%”D"‘ôööà8{\\¼wWUµ¶bî’;½nœÂ‘@ÀWÝHݶX,N§S¸G¼Gþ¶D" Ø}¦æo“ðRçèLëóO¢wEd†#Ü[¡PŒØ[↗ó©<<ÞžXï ×Õ¼_›™––˜–{dRöºç¾(ËÝZvâ‹É¸·È „XH‹=888ì9`³Ù¤R©ÇþÛ£ó€ÝLè] r:¼wÅjµnذáN3Û›ôš¼Š¨²Ãy苜”8^¶û­â*,ÊÕ­y`2n/2Óæ÷ñ¤išïŸÓHS>vŽgÀÆ“O´w…ý¤Î„]µÚë'ë‰i(1÷#pàçÒœN'Éø"ùK#ÆÛei;ÞöË»öD¥ËŽBm­´m•9:#PŸ—H¥Õ´YÀuÙ«u/Sh[„rK³6‘Úº·Žœk©ÙC%îicÁ¶IKÌ=r¬<¢(ŠÚ[×ÖV·Ÿ¢(ŠJÜ_×>¹Ÿ…ÈŒ„7¥yç±7ÄqœG[-<d_MÓ …âÊå/«~ýúß>öø¬¨¨A–èïÿðƒ÷ŸÍÚ¶råʱtˆìùÓÅ”ÔV=Üò—P)0wÅSjT\LÉÚ»Wµl.uäÑ›*½ïð‹ßˆlý¤/J8§Á$iwd¯Ð¥/Xu¥Põ¿ª¬¢l½)NË@ÑTlT£P¯O9z  =¾ØW]mû}V^Þ»ß_¿S\  4¼§ºY¸óÀH¥÷®ÛÂÏ€u3øå]Ý ô\¼*}þ¹-Irò„'3SñÛ¤ôŒŒXšÿ¹ªÂã‡v§HNö¼8I{P²J“]@]Vº-ÿQYãumòì®ygŠ ¦êVó–8æt{VÁEYàNx0¼w…TØ   RÑ%ä#Q¸‘ Øñ¶_ÞùòŸ–m7kâç(Òöé» ±#HÁ&ë,³í=9*ûù ‚Y½*q6&:H[Ä™#ªS¾ON&Éù¹4 /ŸË`»æŸwE¾F[î4_©Ý—m,Út¨¹wÄí°©³ÆxóÞÊW4äèࣂ33ñ4¸ãÜ'EDÐ$ i™½ãÒÈq`î¿ ÿ¼+\GýACß“‰Ž[¡ŒÀ,}6Yyk÷|µAûè¬3^L|6cî…E|Öë&‹.!¬¥r§¦ ÕÌÉ.X²iÓ1• ºå<ž2•iÙŽDdúÉdüJ iÀí`¥áö““ºí1Ò†{¼2MO>ýܵw…³^ÍÉÉB¤îª~&,2%ÿsfËÁÖˈÏËZ[ÙMßΈ²°Ùí\g]¶¦j»Þ´%–AlÑá]õ›4ÙO?Ùô(àR²¸P’Ñ’”¿/-UÊðC Ä›Dj+é™S …ýÊiJ¯}`ÀA¼+|i¡±˜äx~úé§/¿üò4<øtã·w…cYΚ‘:¨a-H¥´|ô"ÆyWŠ‹‹ãã]»Ýn6›%!!¡7N»â—ÈsiþyWh¹œ;rMÎ0ùÄ"_aÉœ9™'§ôõõû— @&×»""â7ÂíHìŠÀ\ãíÐÐбcNv¼=ÉÞ!k`äØs (¾ú5Wì ¿&†Q|iÂ"""3oZé4  äC[|®Q¼˜è]™á'9–H$Á<ößBêyÀ¶Û¢wEd†CúÝü˜Ýn'µ›æ1Þæ«z€·‰ã•â(;EI)â]yþùç×®];Ý&"dɆ;’®¸4ááË—È{)Š   »Ýîp8ŠxWº»»¿ùÍo†††Nï³qlŸ…“G„MâÒ8ûE]ÎM¹¿Ú'no0#á‡Ìd¦Œ¯¼ÁÁÁDÝìFFàñäBïJpp°TJóÞ•£GŽ•à9%˜J™Hæ½·/x÷pÝŸWTÕÞCYg*¼jÁCm8b¼-ì‡óWòýø)æ_Þ®%SªB™é¨ÖGø„“Ñpü‰IÞUD*|Ûb"ÓDPP? †™I ÈóØsÛcÈ=-Ï=íøã]9ò•0èTT¢¶ÙoËJ[MnšvùÞ­EQTfåéÎQŸƒkß›™–[Z¹_›HQTâÖýmèù´¶äÈ'¤ó`i¯Ï%6*qoM³Ç—qgýþD*­ütyU¾51sÿ äJ+KɃiO´·Ù“FQ•¨=Ñ _è÷Ž‘“#r<ùìÎ 7¼5`ë6MÓáÊpÖ:Põë×)IЬ¨¨ððp)MøÁûŠZ¹rå×>üt!¨ ïÝ\–еK–lz7±ºzŸÊTñòÓÄ|ˆ"“ ¯7å!çi|iä”÷âv ¯oûã]‰}"3–ôÌ °4WyYVzl ð’ó¥…À3_³U¨t§Ïõ&'Z›Ï\W»tAbÂl),€jWõÉ[®¥VWtæR° “]ÿÑ>öÚú…4’–ÕUéŠß-È*–î~¿á£9k×*¥¶W·f,$“c`—Éz AÎ ªuUó/4½K[.gåÕMÆ*2ÑðÁ)9Oîj·…&Z߀l»í—w…³[óX–3Ts"ò¨¥*RXÎÿaEÊŠaRTïœwybâ÷m=¸úÅ9@½x.™ò”F-à¥Xš½æçúl@þË[øÍH[$`€¨…sh˜“:®ÏGdÚ±„ŸsµÆY_¤¡æ7$ óéyðéÆ/2)9ͲBª;k>OzÀL‚ÖC8«Mp%й¾&|¡Œ\\¼é®ô}ר<‡Ë]õ;4U€¢7ê:†Ï÷ø¸a€îÛzÂàCQ$ðZÍ&øÜÑ7 e˜˜¥qo¼^š”òØæìïä½ü/ ,ºuËL¼+cÕmfÁj ͵uörÌÒg³aÌ[»§æDGG˱šc%Lº×Žœîìl©ø™ÈN]>Êœ·}”c7 yBÓºŸ”·wu5yMc€zóc@K¥–¢´Í}ºJ7¯3¢ðŠýF™éÇ:<ðH}'Ðî-øMEB‡ñ?99+ŒK#/6æ~xW€ÙOän‡F_q°Ñ|ÆÛ²"•1 6¥€Jßô‹„ÑâC¤R`”ı2—?†Qr˜VÛP–¾V·¤B@]xX¯Mö*¹A[ÍOrŒ*Ã¥üù4­Õ7žŒLQk~c®×JD¹v¥`€aHO^æú×»ˆÌTÈú69n%@éõúþþ~ïŠ÷tÚ§Ÿ~ZXX8•OøàƒpÇY™Íæ   °°0—wE.—]·ùD“@Ãoï hùˆz&´¬Øoa\Ï–[FËF ,X–åU¥Ç 0 ï50ïOò2`÷˜TïÊÒì ­›#Å”Q {ô’câB$Ç4€þþ~uZv›äBMáÓÎ &Õ»ÂÌ»})‘±°Ûí|õ$u{8Ç“8Œ…½nï%±;ÙªRDDdêðð“Ø£—ì(œòõol®ˆè]™á°,ÛßßOŽÉx[©T‚Ôm‡Ã144äá$K$’€o‹Þ‘ŽL&ããU„í¶h3Ê‹©{Ì™‡D"±svnˆãìœÃá Þ•†††é~.ÇÙðsi¸cͽcÈ=˜ÖçŸN …B® CCâ]yçwøŽÐ ä±ýÚýõÓ,Ž™†††FÍñ$ `ŽÑ!Bói}þic†{WFÃz±¶"¯%p=XÄhͰ=ƒ¼ûáE±[åýÍ ÷®ŒŠ Hè>OizId ™'ç}Œ·=šqPó@cZ½+ì‘Ü´ÄÌ=-à:ê2©ÄÊ DYŠˆÞwHv•ÃíT‰G·<`ëö´zWäÿâ“¡Hµ£†E×/2Ò ªžJ` ÊRD!š°ò’ó®50‡Ã1öúvÀŽ·§Á»’<¼~.ÝxÁ¿D¥¨Z{eÇl×ODYŠˆ ?9¿˜-ÛÛÀ¹­Ÿ¶gŸV¦Þ»âAlF.É¿S—üëúùôð…¢,E€»nói|=—à»érr’P&zW¦Ø»âAû±CÎ=vQ–r_ãsõŠŸüâù•,ßk`6›Íjµ²¾àWÃ6æt¼+l[n"•¨­±lDZ%ê"uI“õúqªR6”vy½‰(K¹_qzÁW]~ ŒtÂ…ñ)|»=ÖþÛ|¾'EQ2™Ìû½„©ö®°öL1ƒ€å79j¨K~µ3IÔß½.çŸjž<´%n²·EäÞ„¯ºÞ-3)ÀŸäã,8޳Z­aaa ÞŽã<¼+BHÝniiy饗¦ä—šYÜcÞ‘û‚÷Þ{¦i¢[!ê²ÊE†Éd€L¦Ãõ«_ï OïÊÀÀÙ¶—¿µptM6X_Ú=æ]¹/ â[lŽãÈn“¤CMª¤PÈï±Íq˲d Jšõ6ó Ú‡Cô®ˆÞ‘©Äét’HgÒë&Õ›ô Éfx_¹„œ¤(Ê3æÔ§/MoCô®ˆL¤“úLZir†Ÿ!']i’ò%•JùêĽi¸÷-òŒÔó€í“‹ˆL=4M½! Ž"•®ìŽTïÁÁÁ[·nEGGÛl6UÑÎ IDAT¡ ‘ŸK£áÎí¶Ûž†@v*‰Þ‘©‡¦i«ÕJª´pàDZo›ÍÖ××'—ËÃÃÃišŽ!—HøŠìj·m6›0hÜ;,`]ˆ¢wEdêq8ó7ÓÑÑAÖ·BCCÉŽ}¤¹&'ƒ‚‚âââ$ é¨ ÇÑ„áxòÑfËDïŠÍfã†8ÊNQRŠxWžþùµk×N÷£‰Ü·8ÎÈÈÈàààÏ>ûìÖ­[¡¡¡üˆšôÀçÌ™óðÃÏ™3‡HŠ}VR×~`ãaJ¹™Ä½è]ñ€ííe'9rM4½L d›Íøøø”””¨¨¨ÁÁÁëׯwtttuuÑ4ðä“O~õ«_åË{Ä®‘“®•WÚ+|àu[è]!£Þ»bµZ7lØpX+ØÆ¿|¤e_“swÒ佉õbmEžìç»ÓfüÇq@úÑ4M/Z´(,,¬¿¿Ÿl0@2>Ȱ<é™Ãm.%']cyrÊËs°ñä÷ªwEˆüëÕÇkíqË&÷]DÓËÄÁ·¦R©tÞ¼y}}}f³¾œHpŸÂ½rÆWdÏý·=ðˆP@¦Å»ÒÛvlk"EQTÚÖÜ­i‰{Ž´¸–cûÓÈÕiÚº6’Æž¨Ü“HNn­´ø<Ã]­Õ¿þ¾é*¶ýHZ¢¶¦¦”Жzö¢;ë÷'Riå§INJgùÖÄÌý'8Ñô2åcQ”Jexx8ÞcÚŒÏñð¥I@Æ“ EHHH¨›07J7$Š-™ïŠå´&^]…]µMß[p¦Êh:Û9 ³¾H¥ÎSæW7™Žç£"=~É^€=ÿoš¢˜’ZSÓñju¼>Ϙ?¬:Vû¹€} ÇhªÈÊÊÙ¨¯.Ù•Z‘³®°®CøÌóÓ6g©Œº”Í',\[ež®ÊôÝÍ«hÑô2å›h‰DBú†|ì í1 P@ÎÓÈk>¹p=Œ¢¨+ôœl¦Þ»² ÿàð±×Ö/¤‘d°¼«| ,,-@j‰þÕ-@ÒÛ ïF®­¨m[óŒÝ ô\¼*}þ¹-Id¨ëuÆ+åðëÆX9|±xÉ»ñêú…‚.Üý~ÃGsÖ®UJl¯nÍXHn+š^¦§ÓIô†|„©L&ûøã}ôQŒœ9ÃÈ16™„sÃíK›€u*M½w¥ç‹s€úksI”Ê¢ãUW÷W1K\é€|ùO˶›Š5ñsi{Žô>Îx’º8šT×èUj˜Œg=ËÌ^ós}6 ÿå-|P¬hz™øÆùܹs¯¿þú¹sçHÂöxf¸‡ýä1§7¢weн+Êȯ†Ï¯qÐuæ¸Q›H]èJ"³\qÝY¾F[î4_©Ý—m,Út¨¹×יѸö©©ëòŒ³ëªß¡©½!챋¦—)Ä£>þú׿PUUÅ늅¼å u[X½ ð5°©÷®,\óL*°iÑÆ½û÷$ÎI©”2Ì;¶Ã¨+¬9ÝÕÕ^Y°Ý|w}<×Q_Zz¬Í"Ž[@&¥½Ïx9YŒ?̯lëì<]Y\¤>²XhzºJ7¯3¢ðŠýF™éÇ:<‡c¢ée²v­?~üܹs.]ºÔÐÐà1Þöè_XßöÙn{ÄŸì<9¦Þ»"_óv«¡ðŸÿÓX}6K¯Wi4Wl»ñ_k÷ ¤g¥€ªäø…Œ…4Ûv5'' 9º«ú™†ó:Vª”áÃÃc±&¾²Kò×Ì{ÕmzA[ÍOrŒ*Ã¥üù4­Õ7žŒLQk~c®×Ц—©DXÞ|óMþåï~÷»5kÖ£*xÿ!iƒy§¥×ëÍfsXX˜wŽ'LQÔgŸ}ö³Ÿýl²¥ÈtxW¸®ŽÞˆ…³i ·¥&R••_{åÕõóù»Yí*¤…cYÎZpÊûŒ KK¹RUk²·÷ZÁ0Œ¨z™‰¼ÿþûsçÎ]´hQppðï~÷»Ã‡“üÒžë[ßÚ±cGPPÇqýýý%%%|‚ñ®Èd2—w…,€]·ïè«Éa:¼+×,Š.â_¥îÓ>1_x7Æó¿‚–ËiùmÎ ¿%`°ƒf"ŸDf ¤>ÿüó[¶lá8®««‹h•d2ÙcdþG4›ÍF%Ÿå›¿=Þ•ù?ºÒªnïìï”ÎYº*)v¿V™åšÖÖ Ñbµ¾ ë\Âc‘:ÜM÷h«ùc~:mjx¦0-Þ•ˆùqÉó'ÇÈ"Ÿ7ûöÅDî„ùÛÇÈß&{ð§øårþ˜H˜DDDf|î—G<ù°çt ïŠD" Ø>¹è]™á÷÷#vÄá>ùmçÒŒYy#zWD¦žëׯ÷÷÷÷ôô°,k·Ûɪõ­[·HŽghhhxx8ÉûÀÿßÞ“†WUdyêÞû–¼% IHÂvpaSZEAf†åE…f“Ží7­8ØH‹¬¶cÓ6v#.0B ˆíˆ"F@d5 $’@HàeOÞþî{ïÎCÊJÕ}a¤!çÇûêÖ«[uªîÙªêÔ)‹ÅBO"šL¦&þäÁ`Ðd2ÑcbÀð6Šß´g<›ã®4Ãÿ?¤¦¦¦¥¥uìØÑd2Qd×É­V+ž¼öz½ _DœAKи÷À9¨~ξþ¬pM㮜ÙùÊÌ—?¹¦qgwÎ|ì•ü›ô°ÏÍMVÁ¡qÝ\÷^^öêÀŸáŸwÅl6›L »òñÇ_ýñ¸ê“;×|Xâÿ?Á5„« Ölø¬î&5¼n|`9”_K3™Lx5/-*®“ß´zûªâ®\Øâã,×Ö;Ìdó%‹5Ã?'°w°‹â4ò¶¥¬€kl7­_ÚÕÄ]@þüÞ÷­Ëq„Oÿ˜œys&-ûü[/žó Ÿ~yì}3_~ý™‹ÿ¼œÃàr™Ù›<öòN4rÞŸOzÏÏ`(•g>üäM ÉòòÎüü¯BéýÊÎÓ×dPšázœoSh²–Fo¢¥YEé›öü6^§Xv®tÃÛ»ãî{““C€ÏëýzoÖCNíß¿¿‘Y®ú¾ËÞ“®†üõéÀ'OgÌZ3tõgßu­Ùuÿ£[!°ÀÉ­{6lÝÓë?Vÿ}Ò™Gg½Ø+³³ý#?ÊTgß™s¦ô›5²Ý€²E½õztÙ”µÙ=¬àöUïÉ^¹g ,Z»vàÇzqdÏVüýïÁ7oû¯GdÞ¤;ÿü€ÖõÆuýÉ/ù:ï—Æ­“CS>¿ áj⮀I/8þîúçWgŽè pg¶{O¯¿^,àºbßîç@àñ~OðÃ_¹“ñí;ó¯k÷oxbd›e0fõëS ±¸ú»Š™w¶¬LÿaÙÖ쿟hx¤‡óÀéG_,¾ÆÖ~3\3 Óæ1cÆìÞ½»ªª ǤI“X–¤>§¨ƒ©–€¹{€ƒ›| áªâ®4S<ó0öîŽI6â.Æ¥Së‹¡©pçÔ?¬ÅÔêåÓš1z·g›C{d8À–:æòk†ëX…j³Ù&OžL~øa‡ÃÁr%F5¶Z­tZù?^ÒÀžó¦WûƒÁ›Ö/íªâ®\®âb|Rf»ðÉWßдðEv¶¥vdž(Ô®ûØúëŸ>vÿ˜ß€ÕªL\ÍÍðO\dáûï¿¿[·n‘‘1dÈÒ4Î)7ߦ{ÝÒ‰j—ÍX°2€97í½WwLmž~óG|ø@Ƙl (àL¾`ÃK>îŠÕÙBÇ7@±¶à²Uh(®P„¿šá&œœêmUU¹nºæ‹bø":˜úðõï£tb!²~ipÓßõwMã®4m©×†' ­ËÿMmÍðÏ \|âh4êv»1‡EÁ!xú›aa¼ÛÏStô ü/ ˆÈ›}N)\Ó¸+MAiÝãÚÄZi†6 g:ðÎǃAHãÅ›xq'»·Ìþ¶bPM  Nׄ{<é/Nߦn6C3Üt‡ kjjü~¿ªª„¼——Á1!Ër$ñûýx}‘¦`î UëÚäü1ÅU7¼»„Þ< …>6`…®¢(ôüìÏQ3\pÞ‹NQ$o${ֶŽ›ƒŠŠŠÔÔT1ŸS™Øêá±cÇ^>’;vìàlrL+ùÑ/ÝôÒšÞõ‡ò C‡(è,?»\®£ÙgŸ2å1—V(Þ @$)7éíŸ?BTƒ(€¦9µ}‹`ŸÎpo_k»Ö6“É„Üþsã× <ÐÃ’Ñh×¢5Móù|~¿¿U«VIII”·Ù£ÈóÜZ‚×ëMKK[AÞædD4-,,¼"„‘yÅv•Îiv‰øci.wP„b6›“’’:vìÈRd0ãž|oäÎI2›ÍÍÎuìYhdœÍ†Ãá3gÎ˜Íæž={þxw£)plV]]ݱcG6GW½SÞ®©©¹"„c™J';¨ªÉd2QtÅurLøý~I’pà O¥eí=º3'ét°5IQ L¿÷ö›EV!‘hT&„H„€&"É!Õ@‹(€Õ¢ ´¨ŠhHD!p#•÷#„€h×Þ—lÝñÕùœz3ì«nÛ*У‹Ób±4ç×Ð}&\ЊD"TÃ…B¡¼¼<§Ó™‘‘ÑŽ@à€q¸A¬Îmýþ+ Õ ý~?¾‡/¾ž§÷ÇÛ’$Ñû»ñNƒh4êr¹Ž;ömtZmG’­ññæ”D“$IáD4Ѐ¤€Dˆ²Y-ªi@ÂMÓ4¢ª„£š ªÝPåÕ0Pƒ+Ñ’âMΤ¸zEΩMÞù}]׎Iè$xE²®5 o£ÆFöÆ€²,úúú'N8Žôôt”Ë,³ø|¾­[·Nž<™­ÃØœ7Ž3Æf³á#]´Ö4- ]©/ žÁ4ÎïÎ\;}WÐoü’qN !C+ºÝn·Û]QQQäk+%˜œ –‡©Òµ^Íf‘dDH!ˆ¤H K E !$Õ4MÖ J‚)„# iVˆ|c•÷«„HhQG Ú£%8 À:'…B! ‰Ñ<ë¾®€ò65FãjÙív»ÝÞÐÐàõz=O||_}}½,Ë „ººº*Sg¼¥E‚9Áaö†È"U‘Â’Lpú@ˆf2›dËdŠ"Ë ‡£‘p4J¤p8‰‚ŽF€„#`’Aº±Ê×"8‘‰j¡H8Ái"² ‘È9o|0İçͼ}]ò6nãZÝVÅb±`,ad}z·nÝ4MSUÕívû|¾+q¤5Þ=b¼4ÔéÈÛWC£MŽÛk’$y<œœ[,Çc‰ƒ„xS‚Ôo‹‹3)²¢˜$¢IÖ¢QYÙ$ˠɲ$¦ÈÄ$Ëa ÂáH8ªE£Z$ !5Y G™˜å«¼7,)ŠQ#PÕ`(àª@ˆUMî(A#\0ÿIDØ ×X½æ4=’<â÷û‘ý|>ŸÏç;xðàßþö7¶†ÊÊJö±¾¾žË¡Ç0³³³333gϞݯ_?ºÁF/º|„)oóz»‹Âè* ™ˆ“sUU­VkšRi²¦ÆÇ™ãíÖ8›Ýj1›ÌfYB Y’d…H )’$MQˆ,ATƒpÂQЄCaU“Âá°I‘L7VySˆD£5¤†‚RÀ$™¾pXSã”ÄPƒ¢$à|»™·¯+ –-áM÷ÃÉm6®(cáAƒµlÙrÆ %%%Xw g^lýâS¦LA½M·²®t †›o7‰—&z•²˜–eQ”$Éb± 7\rrr˪³Aëí6‹d³Yâf›Ý¦( I ˆ($™È2(„È&4ˆ„Uˆào"šŠ@D3›˜n¬ò’dY‰„•ˆj øM¾€äp ¨ÆYå Òàp´AÞn¶É¯+`ÙŒ>â.7Æ)IJJ¢Ç³PIÞzë­‹/Þ·oߦM›Ð°e+dƒ#H’äp8~øá!C†àylTÚèLz¥z›;ãI¯û»¨·QuÓÒì~:õ]Ag7´Æ±K]ºti}roYMŽ)y ÕDâì6›,0›AТ $0›€˜L È4€P45‘„à†! ˜À7TyÍD‚° áÈ& h$ð™§7çŽŽÑøøxö_3\?€~h!S½¼íóùn¿ývÔ“¤ñn=ôZ9räàÁƒ7oÞÌii»ÝÎåŒ5jÒ¤I‡ƒ ¬\)oãÚ8R.þaþÅù6'ZDÞ¦!pŠ’$Ýyçá’£2IºG0I`6ƒb“ "a0) ˜@& È  2( „Ã`Õ@UAE³V…P°˜AÖn¨ò*\äm5@ ˜d¤"»—³ü_zÜÒ¢E tA½BÂk†k ìÒ»; @jjjïÞ½Yöc÷·­Vëœ9s8aÀ-aöä%êí+µÉYßÖµìâYvKjv½Äé‰1ÌLKK6l˜úÙgg\ûkž µëéÔ/1=I‚jÐL L@•Á$! ˜@ h ˆD ‚ˆ¡0D­`–n¨ò?€š¡ Ô^¨©->8ŸÓÓá¿£W·öíÛãöæMúºz:šî¡g‡Çã‘e¹OŸ>v»–DÖ ¿ºÚíöXœ¾ËNé¯Ô[‘½{€Uûïß¶Z­Ô/ ôô6:Á:J޲,ßrË-’$9{ë—FMÚ6mÚ$$$H’ôÓ.x¼ä[,c_©Ï©$I¬b¦é³týסq²áóù>øàŽQ¹µtÝ4÷K6“{KD€C} ói¢ç»O;È–7pñgëÅ|óXe bìüˆÕÜèq%!Ƙ³ùÜ8ÐŒuŒz>qCGóu¹w ÄÊÑm‚û7Ö#— OcÊùÉÀ} Qó™lëÛ¶m3è5ãé¿ø‰9{Y§Ó‰±EÚ‚F¢™1cF,åûiŠÚà-ÎÑMh1κè–Õ¡uIN¾¦9Æ`\ží”.«ÄJ_=p¢Y¬ü’,j ²¯(?VsÜ׿dm—7ƒW¸ò"¯cÎÉBȉ' qQ­ o£MnàON9|ø°1G±Í‹Òèr^‰¥Ã9UÏÐMhšÆ¾ÂÕ,ÖÏÉ1_Tqœ_ŒU•8>\¾A"–Þæ†…ýä ðó%y[—tØŽsWLˆý…Kñ6§Ûٚɖ‹‰Ýaç;±þ¥· lâ`B£I/v-Vm"oëñPÓLÜÃÕ¸‹ñÒ0¬­KämˆÍÐô[byÝÃâ"*l÷¸JÄO+6'Ö&¢*>Šë&D†ŒU§AO±ÒmBFÐÓø!öEnLèÒ‹ÅEº„Ȳî§á˜„EOü|º"óÇz…±ŠéŠÎXÿ²ýÒer8ôDfÖeo¹sÉþ²ãÜø°iämÜrStGGWîê2<4Šôöš©›`™3o‹_1Ks/²­p¡[,f6.¦[R— 9ÄÑLÝñÕg¤è¦ÅšY:Ö¥’XôÇJ ]A­sµos»Ø…Xë‘"[Rü ÊdпØÝVÄ„î`ŠlýØñ¥K—Ο?Ÿåí&>§‹}WbQi4Ã8ý£Kš"ãé2a¬Þê‚î¿ã+æë2›A+"ƒË2îQ@ñQ‘K¹ŽpÓQ`éæëö‚#©XÚC±*±ŒHÙ,b±€ý—³l¡)©Ha]É« -‡›˜Ï!¬+—u©Wc\_h_bu–—¢@Ôm‹í×’%K¾øâ‹½{÷Нò¶ßïGg7ö56‹·)p[óâ`‰t,~]r×m7ñâââ8ÖeQ¥ù>Ÿ/++«¨¨èßþíߺtéÂÕY__Ÿ˜˜H3#‘Hee¥ÝÊår¡1>²cMôf(,Î;wîìܹs÷îݹéR¡A‚ãíO?ý´K—.=zô`¿ 1´G4Aoƒ‰ù¡P¨¬¬,--Íjµ’¦&+¦ëêêêëë[·nÍFïa±Záêÿ!77·¦¦fðàÁbùêêꬬ¬‡zˆÍÇòC† 1ÀJÓ›kˆ Q.ˆ`,•ÄwYÞ¿Žn%X~Ù²eYYYåå媪RôØíqþKƒAhÚX]ˆ:ŠæÇâg6}êÔ©wß}÷Ô©SóæÍðáÆ|BÙ±cGuuuff&=X‡ðæ›o~÷Ýw‹eõêÕ˜óÞ{ïíß¿¿U«VçÏŸŸ;wnïÞ½Àëõ.X°Àét–––NŸ>ýî»ïæ$‡¹˜óí·ßZ­V¼‡Ñè+蟤[†ÿ}ûö™ÍfôÓÒJ¾ýöÛÌÌLô êÛ·ï믿N[©««?~üܹsÇišöÔSOµhÑbñâŘùá‡nÚ´©{÷îùùùóçÏïÛ·/‹L^^ÞâÅ‹ãââòòòú÷ï¿dÉ’œœœ™3g&''Ûl¶áÇO›6MÄŸkô’øÂ /ÜsÏ=>ø`IIIqq1ò*gçΓ&Mb3Ïž=[\\Œ²À˜·a¬b¥- .GWo!déÒ¥ÈØÇ…¹÷r~i¬ø©Ù¸a"Ä3§¨³]b;Cѽüš’lÇŽ?øàƒiÓ¦Q¿?®¼¨ÿóóó{ì±äädÖµ–2wîÜ_ýêWóçϧÚ833ó7Þ7nÜ´iÊŽmÛ¶ :ô…^hhh:tèСC9ámÀÛØAöx<‹­î÷^¸páí·ß>qâDn¸ÄO€ž†¸®!òv\\Ü„ Ö¯_>ŸïÌ™3”æ,X––f³ÙÐRã õë××ÕÕµmÛÅaaá{ï½wøðaY–ãââΜ9ãõzisuuu™™™úÓŸ&Mš‰Dl6[aa¡×ëµÙliiiÅÅÅ?üðÃ]wÝõÀtèÐûúlÇuɃ#¡Í›7744ÔÕÕá=³øÕ8*²Z­lŒmü /¢¥™,z[,À},6‡[›•Ëd+çšÖmŽÃmùòå”±—/_þüóϳ„AùHùðÃùË_ŠCÉÖK.cÆÂŽGˆºrAäNš°Ñ„Ùl.**B²cÏœã»åååùË_\.—Ùlž1cFß¾}ÿñ9r¤¨¨è–[nùÝï~ÇVwîÜ9 kpàÀ¬Íívc~nnîܹs³³³ÍfsFFFqqqÛ¶mÙî/_¾¼¤¤Äëõ¾õÖ[N§óܹs‹/÷Þ{ïã?ÎÚ¨~¿éÒ¥§Nêի׬Y³RRR`Ó¦M7n4™L;w=zôþýû=ºk×®Õ«W—––¾ôÒK~¿èСO>ù$ÆëZ´hQ^^^›6mÐè ââ!Üœœ&—øjšöÁtîܹOŸ>ô?KF'NœØ³gÏ‚ þû¿ÿÿݱcÇĉ‹‹‹½^/^éÎ~©mÛ¶)Š2xð༼<¬JQUUñ>|Øçó™Íf¬´•ƒ®[·®¡¡!55uÑ¢E%%%ï¾ûî+¯¼>Ÿï™gžyã7>úè£Ý»w{½ÞÌ;fÏž}çwÞ}÷ÝôHVeeåþçÖÖÖ*Š’™™Ù½{wÄyÓ¦M[¶léÑ£ÇO<‘‘‘ÁRÈúõë¿üòËV­Z=þøãÝ»w瘙>²‹ÒÆ QˆŒmPèém‘Õa¢+Vdee•••åææþñÌÊÊÚµkEƒÞ1ÒöíÛÑcVeWÒ1ÁA¤)D… ½Û€% ¡ èÓŽ­ ŸWç–-[FvûªU«EÙÐ Ì‹IDAT™7o^ÿþý_}õÕÿú¯ÿ25*Ì€²CÓ´·Þzë©§žÂõÅÇS-‘šššŸŸÏV’]VVvèС‚‚‚ÄÄDBÈÚµkŸ{î¹#GŽœ:u*//z$ùì³ÏÚ·oŸ——wë­·~òÉ'ªªž={ö7ÞØ¼yó‰'Þÿý‘#GŽ1âùçŸß½{·ªªkÖ¬yöÙg>\PPðÃ?„Ãá;v˜Íæ¼¼¼U«VùýþhS`Çßb±8p 33sË–-¡Pǧ¨¨èóÏ?ÿÃþ …pYPUuÙ²ek×®EŸG̬¬¬vìØ}ûö;v,++ c…B!6LÉdJIIY³fÍáÇ'Ož¼k×.tÛp¹\íÛ·?uêTŸ>}Þ{ï=Êl‘H¤¤¤äàÁƒG]¸páºu먯8Â?ý#c——— c———Ó:qx©:Q¬z'αÙlƾ+ºº—•4\ynÛ`¾ÁµÂþ+J/`$%gP©ªúÍ7ßÌŸ?ÿþý‰‰‰‡#77?jyyynn.V T*ç!¡PháÂ…“'O2dHEEÖ qÝÍN«>}úTUU;ö7¿ùMzzz$Ù¿RRÒöíÛÝnwnnnÿþý !è„ÿÍ7ߨíöiÓ¦Õ×××ÕÕ͘1cÿþý÷Ýw_jjjAA"‡ÝnwII‰Édúúë¯SRR>ûì3Ç“——7pàÀ#GŽŒ7îܹs-Z´èܹ³¢(ˆ;†˜¾ë®»Š‹‹sss§OŸ^ZZ:þü@ ðûßÿ~éÒ¥'Nœp»Ý555>ŸÏétÒî¬X±bܸqUUU^¯·¦¦&)))„Ã᯿þZ’¤ý×Ý»wïÈ‘#é+²,£ˆÁœ¢¢¢Ó§O6,šÍæ¡C‡ªªúÚk¯=÷ÜsN§“šÓéÌÍÍýá‡jkkëêêÜn÷£>ºsçÎ_ÿú×Û·oÿãÿ‡[´h±k×®ÜÜ\EQª««“““ÑéŠýèV«µ®®îwÞ9vìXmm-žPLOO?~|nnî¸qãÖ®]‹|E±X,‡ReÖ¬YТ俿RaŒòäÛØ&¥‡9ÕÍiu±<ÕÛÆ–9.ž]¸p!??Ÿ¦?ξˆG x¢D‘eIUh2éZÎ"osË`š°$Μ\àÌxö-Ý uA×ò ‡Ãµµµ­Îb±°+ÆUaBUÕ… Ž=z„ ¥¥¥ˆÃàÁƒ+**0ºë… úõëÇš—&“éèÑ£K–,yüñÇW®\™’’âõzxà„„»ÝnµZƒÁ Ê—`0XUU5xðà;î¸Ãd29T§Š¢Ô××cmH»Zã-ÊgĈxú×n·G"‘²²2z T´éëÜz½Þ“'O:ŽÌÌÌ¥K—F£Q—Ë•žžþꫯF£Ñ3gÎ8Ζ-[6Œ¾öîÝ›••U]]ÝÐаiÓ¦Ù³g·iÓ&99ùäÉ“0dÈÂÂB¶ÅN:ýùÏ~öÙgq¨ ·mÛ6lØ0°X,÷Þ{ï¸qã|ðÁÏ?ÿ|„ tÐV®\YXXøä“OvèÐO5Ž5 å)¤¤¤¸Ýî3fÜvÛmcÆŒq:x»¦ixx%²ªªûöí{óÍ7çÌ™sÛm·íß¿? â<åìÙ³PQQát:ñ†¦iÁ`°¡¡¡}ûö“&M"„Ìš5Ëçó±4 Î4u‰Å#r²±M.–çlr±X¶lÙ_|qáÂ…ÜÜ\œoŸ?>77wÅŠóæÍã Ϙ=.š7T›‹f€hK\Òºà¬_sS°\ 0¦2—ÉÖ(tY<Lj#8 IReeeEEÅwÜa #ġܼy³Óé|衇Î;G­©|óÍ7’$ÕÕÕ?*Ð>ú|¾’’’çŸ×o[µjÕ§OŸœœœ=z´k×.11±Å@9Çÿþûï»téÒ¥K—´´4³Ù<~üø/¿üÒår¡s¢(‰‰‰UUUŠ¢¤¥¥õéÓ';;»gÏž;vLNN–eù¾ûîÛ³g!¤´´4''•R^^ÞñãÇuÇ_ӴÇ>Ül6÷èÑcÇŽYYY»wï5jÔ¿ÿû¿Oš4Éb±|ýõו••‹eË–-Ÿþù—_~ù / >|éÒ¥‹eÔ¨QhîšÍæŒ;§*'N$„|òÉ'H 4`&{mmmvvvyyyRR;ò_}õÕ‹/¾8`ÀŸÏ‡²Ïét¶mÛvÅŠS§Nõù|UUUEEEË–-kݺu(ˆø9EÁÊM&Ó¡C‡¦N:fÌ4ª1pˆÇã©««³X,GŽ1b„©ñn&³Ù§TäëZ#œ6¶ÉÁp[t§ù„Y=fU›>tèÐÚµkËËËÏ;—žž¾lÙ2¶ÀSO=õ«_ýêã?.//õÕWý~?‹…3,XPRRRQQñÔSO=þøãƒÞ·oß¹sçúõëÉÉÉ6l€Ñ£GÏš5ëÅ_,--]¾|9'¥h}Î;WUUŵ~Ë-·` +ŸÏwöìÙêêê~ýúY­V´ B¡ÐÑ£G@–åž={wèÐÁårÕÔÔ°¸%''·k×®²²2111???âVvmmm\\œÏç+//ïÚµkmmmuuµ$I:u"„ø|>›Ívúôih<\WWçp8N:e·ÛÛµkWQQ“‹Ž;bU@ÀårY­Ö¶mÛ⪒$I8_­¨¨Ø³gÏÃ?L'P×ÐÐвeËøøx—Ë…óúQÒÓÓ1Ô<tíÚµ¢¢¢®®éO˜ã&´nÝ: z½ÞÔÔÔââbn^j·ÛÛ·o_QQQYYÙ½{÷`0XRRb·Û1 çìÙ³l¼ôôt¤7”5.—Ëï÷§¦¦¶oßþÈ‘#X²k×®ªªÚl6ŒdTZZÚ½{÷êêêÊÊÊV­Záf[BBBFFR:  \À5°”””œœŸÏצM‡Ã‘ŸŸéééè‡säÈ:½Ò®|¾Í‚ñ<Ù¸6Ñ&g ,Z´Mñ‚‚‚Å‹‹i<ÆÎ+=Åb±Û%òÒêu­HÐîp°WÏéªÐXl)æPÞf{,±D ˆ¼­;3ç0áÊ h§;·±0'º1X,Öû‡CFQv#1°ÔÌ^.ƒVS´ñ"HÚw š‹åeY¦hšv‡» öþç D/ú—ÙlÆÛ0è`²*"éÎúP^㤗ãÙ¶@›­ûe ÇGnÜûÐ…reD„I㚨Òô]¹ñήQ¬VQî^Mo¾Môö¢¸2bšÕÛÜÐ|n?<o/Z´h×®]Èä.—+??Ûår8qbÉ’%ÔŸí8Ûg³9²Bñùý’,G"´6E•Kš®{ÇÒÛ²@üË@RˆoÅ_Ýu¯S2–»\Ub»8åô®GÜÂe[§пt‰ b¸ˆù4ݯ_¿@ MãàB¨žAl8 £µ!-꾋™\$ ±r.‡·nt]JëTÙȱÁuq¦q~¨üâ¶ih¿ð—Ši–Ùh¿tŸq„îó‰ªë’ ò6šßåååEEE¬YþòË/cµwVVÖÞ½{)oG£Q5± "‚—ø)Ì1OÚ gcs ––¡iª=Dæáûqo3št Åæ.™ÃÉ…XE”Aº¨r‹üfC_Ñ ŒOª³5sµé~±#ì`räÎâÏ&¸2¢ÞÖíÈ%G€I–²Yዉ‹Ò\µì‹ì¿"zìˆqõô]sÝ„.…S f”™].×É“'‘™q!mÉ’%˜f^è‰Ý!/¿µ>)XïõzSÓÒ¨Y.’‹´Qd¡Ëám¶3º"ƒ+y9µÃ¥]Þˆµ¼gÌóºd«/DO×ýä~ÅâaVoëVÂñ6&DIô’¼mÀíÆ–ŒhÐ]½z69èÑœÆÒEºúüŠôv,Vá@œ¡‰ÚbN,ô :«›)öšp9ãkÀuÇÍ ×8³ù?M¿!\f,Wñ̳®@ÑÕÀ"éfŠ ö‘szãÔ'A ´6-ê® —c<þ ÷ hFP¯ª¬²m´mˆìf ³§C$ ,=Ý‚È*ðÕ²ºDï'uÇõ ºLVku)X¹;3¿ zoÓrðLÀø_±ã‹pxž‹Ql3…DêMoy*€ˆ»°ç§ïoN@ì%l»¾¿õÐ÷·Ÿ@ô ¬_Õûîà^B¼ƒÝüàÎï½vâ@dx¼•PÖáÁ œ8‡n T²D¦¦Âh@<_P)#ËÓEu—'ÿ³:ýpÎ Þö*@ ØXv‹åíFéBÈf¯°4ÌiBv›‰ó+ ~JcŠvY}õw±0m„7ó€¥g[š}Œ)2ÿ+²C`×¥6Aó³¶›—ã8æEÛKn–ëØMÍ<lKü}*Ø }¢ |á½›Þ`ÙgxSwÓ~°RŽÝ7ÃCÞÅ,³¬+¼××gûÙöiH f¸%hÀVíX…v•´^Øñ7Øh³ë5,,t†™hxµxžW¯å©©7Sœ.5Ò·ôÞ™ÿ‡;®Î6ƶÓodºÜbI’žA³!0³ëºŽã[3ì4…%?ÛA"‚ˆÓÿ[ÇA‚–™AD@9`C«"f¼RRŒRJ$Mô "Óló=³eþþ2[ÁÁßsÒ¥x2õKÀ[X„EÈq4[ Qhf3tÁb"å8©QH_‚Aÿ1S)%é˜EÄÌ ò7°`eÙ¹!"ó«-ÍN^jM;dãàym¨Ô bhÅ03@)ñI)3fˆÇkø*—e¹ÊŽÁ6Æ.|¥”§µ2û‹_¬ØR»*‹˜¶)" ‡"3 fì^kÿµ4ª‚摳Ï,Á’°¢«O¦§Z3³˜íÖÒŒ¥[NèE„€ÿÙóÌFL"PÊ1eš‡‰Èó´RJ)‡ÙЇ°"»«Ìa!ÎÎlÃÝÇŸÊ ­5‚í3,lš,³ðÌ3F¦6hX‡¿Þ‰Šöõ”à”¸ÚìÈbwœpwÌ®.¼±Ú¾„ed»M‡ÇÊ–·ŒÀkù“m[°i¤Þr2ša¦8M¯5ì[õözÛµpó”QoZÍ&`» Òi¹^™ö›°IÀ<Öc,]P0’a¥¡ù9ˆCi[«ÖÚ,ÛÛ£”º¬R]ö§L§ÔwÿÝÐà«"d1íqcD±ß[Û‰Ñ<ÈðŒÝö;Á,)å à"D²äšZ=¶Ã†HJÔ¿fÇ1ŸIDlóVÀŠ‚gü!†0§[¥ ߬N¾XÍZ;J ³£|^Èï!†iF'ôŸy=U ~/ +0u™J}Â,_ó½íE°½±Öæó|««”œ.vÒLOSñÿ4?kfSŒ‹™Y§= !¿|HZö‡(LbÖfHM!"¬ÌšY“e"’jv R8F*ÑÚvÙŽƒ?Ñd˜<Ã0Í!@À|‘A4KÀæ&2ÖE_“0 Æÿ¢ӧ/øOXÀvƒ³Qyüe)BDÚK:ŽYHPŠX _T,¾ðaä›&%*X ïY–à‚C)Ebm K…@J”3_†5ƒðnb?ÛÉ´$¼…! 1¡p#ÛKˆ:RlÀ4ÀìkõŒÝ÷ÃÿÚ-ÆŠÞá·Â»„Ùnêq, ‰Éá^Û¦†GÏôÅšj-¼ )ÊS˜¯ØméF*ÛóXØDC!ÞS¯Íæ1«ùÙÝìÑH×츅‡Å~“*YR*i½–[öj dýz}÷ÔhH[äD!íÙo|Щ03³"‚1mAİßñþoFD`d4¶Ì#ØÉÖ²œ¦f:° P —µ!‚oEÌtÂטâ.ijÿŸÙR"rš¡f ľ`£ ¸±¿ñù< DÄšåOžøÍ1+ß&`¨”Ãj–™¾`!ŠÝàRd+À¬ËççÆjX–&ìó,Ê0'¿ƒfÌn«´§lÖ¬µˆˆö™…ír÷A¯L±ƒ…FÕd­Ì0…å 8•A°,Ù® À•,3t>×45¼’’- Ã3SÞg†¶~µì7K[–i”!¿ÙÆfW6RV‚`}Ö3cý3ÄdІˆR$¾þ'©é%ÿp" ÀÔöm÷ Y%Ã߬UZó-¹4ºËKªµi[g˜+HC6$n§mR¡WÂ¥…ßRÁ¹‹e a»^Ûìg¿´…Øí,lò ·-ü€m¤­Úî¶©VÞ·v;ÎáÝܾ+Ë×í¦ä8Žå4á&…¥uÛ—p׬v6|…‡ŽˆÌÖžMËZì¤0³oå4i¸ãV© ©ƒ;€¶ûöy„d¢” À>섃©H‹‚b›­Bµås¡EJ øœã8qú; !{G $d­vcÕðÅma&b!DØ ³Âì¶þÞáoLZ‰8Df§0† ÍZùÄïï×ö?»×˜]ÞrfŽ9 ˆÇž_Àì…w(-¾_¥GQ}eȯÅç æÍ®g´S‚ÖžÏ ‚­Ç×¢L{Ì—Hk°á>!ùݱ²¿\ ›ÊCÛ X³ëBD´—Ôž/˜&ªY›³u£Rå—rcGÀh¦ï†«°ÖÊž“›m8xQk*bÿõ;²ŒÛ2¼i†éþæ¯ ™Y‘Ç3Š,B$¤H3Ã_B¾¬@©Õl†ËoIÀR ÌŸú€ëûT€c’¥OjYjƒ ´ÇÔæSû©ãó o$EaÖBD`q”²Òn؈‘Ò÷Ó9A˜hÃR¶%ãz¬BY!ÙÙŠäá "ü€ÝéÂeÚïëm^¶á£c»«Úì!*|bL¹ˆ2JÐY; ˆT°[ª¥ÏóDD)'Ü‘0ÆÆí“vÚ·,é…$Ì¢(äAâB!¶‡€¯‘9 v¨t ÀžãÙ‘I1(Ö¬È1í2g`áö7dxvýsDc 8éþ;þò E ŒîßGÙCN4XT朜c{P$`À²TMÁ^&©”¸ȆíHXûb/%XÚ³60òn >#¬G‹sDjIpJN¹mHH¸_¾6kËs”CÂZ(5™P—©€] $L*¤Õ_F€Q¶ÌÍÌŽ£D 5КÅwÑñ7f d 0 I`í²b&ÀZ$(“”Qw€r”°‘ù"Šë¤ò;"ŽKâ»3‚AE0$$©#7VŠ<Ï S¦Ù“ï2Ö~½bö1£¾+EŠ”Né¶Ð’Z²©}Ħy£ŠÊQÚÓ†\µÇŽë°§]×M‚t’‚Fq\Ø3[„ $Ú×t]rYkœÛ#ÐÚ$dañÂäjU „¥lå/_m‚#fj@Á9Ž#lœÌÎeKó¢È÷Ðó7‘€S‡ð>Y‹fV¢‹ §”ͬá8žHP3n6*¨ÖžëºZ{:LïÇ?€1¾ )Ú÷Ô2z°Ï€5k2ÞF02œOÄKê@°¬EìÞGDzÝó|Ÿ@¤N/¬vîÙ±åÀBKêä\{I"âàôQXkÓM¤vÃ|}*ï㤠’LÄI)á`ÒEA{É`c‚¡e&@{žµÕ(¥ŒF{ž0-gÇq|1Q)³ì {4bÙ 6Mâ4Tj÷‡™9“rŒ(iZ61Xk¿Y'£fS6¢œp €3³¿õ9äÿͰ§ƒiƒ¯íp\×ÈAvµ›Ó94ö%Bò· ³&X‹këC%J)Çuá XBäû‰‘RÚó ¥´ÖŽã"W) úèSN ‘‘ˆNÖVëD­NÔOš½×ÿŒ®¿-ˆ¤œ¦³FèSlð,|¹Öª&»Xš]5$€Yâ ŽëØØ Ä0¡ˆþ6¿¢`N Žc5Sÿð3ܵ éÄÇ)°kÞÆÔ-i’\H ÈG‹ˆXÆ ” ÖhÖŽrÌÀ¸®"Rɤ—Lê°€c‹%ëã›î$jwºzìP‘2#tÇžsöÌá±"ãê"_óeëdm8a;©òTC»CCQ×gêÔÈQ‰‡Fޱ®„*ÝÓ4ÔeÀìòõZ$"nvnVnìüBfRÊ1²1–kzj°QéÌö†QCIk­!ŽR†=#ØpµÌþ(ޝ–z„8QŠ4{žéNúÒ.ÝEHê¤+Œ•m¿Y…dŽ»‚ùmÔXd–­™//1!µÉnWë´©¬7³¶ v™v6,Ö²@+·š÷Œç(ÇÓžo÷ð‚U¡‘â:é§^Ø<Èžé©—îì%MÎKø×p”ï8k9ŠÕÉJ)í4ÒàX›ˆ©¤—@±ß$ò‚Ç8Ena—RŠ M…äT \c¹(ÜŽãïQ"J&“°¦!ö8ÖÉð¼XB¨7Pv:Â&A""ÏX«”q“9ɸ§ü“ 2º‹krŒšüíÕe6©„uàyi6(%±­*Q³iÌ°Šƒö³÷°þƒÊ KJJ!B×ĦM›Vm¨ùhÅ×ó¯øpÅŠÜžÙùE¾²ì8HIcÆŒGy¤´´t̘1ý*úaõª5|ðÁ† N:é¤Ó¶÷úvQWS÷Üü¢|·þ÷5q ¿o¯Îssê?eŠÍÎÎÎIÿÉ««©‰{nvQ~ÎvÚ”_Ô ÔUÑ"4ÕmÔUUÅÑXûš¨&J ¡êóEëKöZä¢áÀzU‹>Ú4jÔ Vw¡50B–ã8O¾±ð‘7—æ”*å°€”ŠÅ²˜…µÇÂ8^[3¬wö§‘›kJ·øâ‹/-ZôÙgŸ­Y³@¿~ývß}÷Q£Fí¶Ûn-iLç¾!‚!‡º„wýÌW>Ù¨³r bÙ9 E*¤g·Zñ„3~L"Z{^¢¶r·2s¦¡’1cæÌ¿þéO‡0ñ°Ã6fLIÏRRjó† £÷öÒ¿ÿ}×­·²È´SOmªqΘñÂw~g¿ý'~wJÿ±c ŠKToÚ¸÷ðasž›ýŸï‚È)Ms3fü县Œ;pÜ”#§ì=rLii/oØ´iQÃ^~þ¥;n»SD¶Ë´Póùìg_þÏœ·î~âÅkž_væ¨|ÿûøç÷\ö¿W?ñìyÖ3ýzlOÀÚÿÜsÚÉWä?tøÍÏüþ¤±=–Zõùû/>ÿâœWÿü®YüÈ™Eöoí#ÿwþ%w¿eþ:þú—þtÚpÓŽÙwþòŒkŸ0ߟuý¿>m\Ã}võkwŽ?íZÀž×?3ã´±}éQ“U4‚¥L?ô’%yí£†f›—ßäÿ¦^r·©âš‡ï;sb…_õ;3~tüå¦ã{ÿ«;÷“¡f¨6,ºþ'GþÙ¯íð??󱣊šÀzˆþìžžó«ççoy¬‘M~~ÑáGNüËœéG jª mã|9¤_©W·óŠDÄq\@´—dã’/’LÆ“ñš½‡ìžå*/ð­WΜ9s/^\XX8räȉ'¨ªªZ³fͬY³FŒqà6ߌÎ}}»HÆ·%µ““—­êOj4ü¾!8É*Vÿ)Sl,‹¥ÿÄÉx]R;±¼ìØvÚËÉkPj‹ªhšê6’Û¶%ÑXûš|£ض~eu~EŸ<…†ËÛV®®0 ¬õ¥¶†gd¹jï!}?Z³Ìe‹#‡Ç0àúk?žˆ|)  _iNør£Å¢E‹f=ôðýÇ—õ©Ð:gõšÄÚµdyz€§Ç•õ©é?àÙ‡=jÔÈ‘#¶lÑ¢Eo<ðà„ÝÊ÷QVQ†B¬U[¿¤¸¢ ãGô–oúÌ{àÁ½FjÔÆµhÑ¢‡z¨ïȾ½÷éMýPûª®ö 7éôG¯}Êúm*ðÁG5ñºEÍ—/ŸqÉÕþ’ ú~bÏk˜}@쿜òË©{ýwåE}€Ÿ}€i×<4í€Â- ÿxÊ¥—LÍ»âCl†_¼ü»K®}kO‡g¥.h¡æ‘sö½ôELûÝgX¾êW?*ñ\=ûg\ûÄÏnzè„}òfßvÌu—ßxäÑ+J+3þù“ãO»\úܵ|xYYYnn.€ÚÚÚþýû/]ºtÞ¼yDd¶òFѹ¯ûˆ¯_ôþâåË–½òÎÂ/¿ù;²ýï“ë_ûçß}g5TL¾ìüã(UË^»ýO®öýƒËNÝPAÃR·­ÿ|á‚…Ë–¼øNüÃ…ßÉK{ÕÜÝ÷À+ËÍ_ã¾ÿ«ÓìgÚ±höCw=ùŽù~ò÷/9îÀ! ÷ßKf_yû“€Šï_öÓ5x¤™*Áš¹_ûÀª³®:olÃøó¹ÿúï˜*N¼àÜïT–úU:ç®›4¯wì™§Léc†ªzåÓ¿þE¿¶Ñ?ºüû È’ï?~ÇÝËj|ù+??¿fyïÉ¿úþþõ[’\ÿþeÓï>öò?Œ[ùN#«7çƒå›—­Ù–£”KO{[ãµrN¥ToØ®¸þŒoW•·ßôáäKozëãsš=ìÔ[^ôË©¼púc/Nÿ賯ë†Td×+äîÿòŒìO=kòßRcç­~íÒ±çOÿñ»iß‘!»U~Û^ss‹Œýî»çÉY?»éº'.ý|ÍÖý óm"²àÉþñÇ GöÁð¿Þu÷¤s˜ýñA§V†ëµU\wêAD®¢Þø«o9ø|óe–kÛë¿ øéôs­.¼ýÏýá}Ï}xäOö©ùì¿/7ýì¬1C\ô:¿¼{ñÇ_Ë¡ýUTL;÷{c*Ê__q÷wŸ¨NŠHÓüÕÏýíÏÀM§”%R9íÖý¯ÍÀ.1;äˆÓÃÌßöÊg¿›â6=­ý¾Ñg bí9NŒµ&R~ŒhÖìy:™€PEi~Ø…ßÂ…9ôŽ3Êìî›—>3ã©eýA4&;+(¨{û®_ÍX€ƒN»àáÅ>Y¼:ßÿqãÂýå©ùGþè’ýg-|þ†'znßqÍK+3¹~þUwë é÷\³çuwŒ.mÇS4£!T”æ13ë$˜Y{ åºA¨`R~p'€pÄèÜÆNµàµ×÷ÌÍï³i]κÕXõ¹‚¿Z-_­ÁªÏeÕykW÷Þ°®27ïÃW_oø.3úÚë{ Î+/ÛTè.uk?pk–d×.ÏŠ/Ϫý(7þAalißò•Cò>}õÕ† `æ7Þ~=o¯\·lƒÎþxs݇›Ë7{oL,¯J.¯ö–êÜånù†Üṯ¿õZ½zöE)N$ê`Ã)Gý%ÙæsŸ{ø`Ùšzål^ù6ðÑÆm‰FÊW1fNÔU‡K]ñÎÎ=éæ„ñ·±¨Z¹¾qézYòö›À„ÊÝrÓÛ¹uÙ[ocÂÑû–13«£Îþýêü­éõÚ*D’ÖÏ™™kŸ½fàÀ³¶ÏÏ»õ’Û1üÎYž ÔÙö@v–y¢ âàí×obŽå—xqÖ›µÌ‰- cG `fæêO—ï¾¾p3¯œuÝG^’Õ솛´à{€3÷–›þ½Ø¥f`9¶ûQg3ßZ™àFÆ95>­û¾©g”"1u!Â’HzšÙKz Íž—¬+**BcJÆgŸ}–——WQQ‹ÅâñxuuuU€x<‹Åúõë———÷é§Ÿ6J¥ûºENÅÁwÜq×mÓOOû6ùÕ;o¢òøi¨¨¨œtÖé“€g–P1éŒ_O;¸r`ÅÀÊ ß=¹X½ns²a±¿ý³»îºë¬ÓÇà›Ô—¼qÉý Ðÿˆó§MQ^V1bÂa‡.÷sòì>¶²¼|ð¡Gž,[¿©®^™_¼3¨<ÿÇG ,zÂ9g«Þ\øU½gš«¢>6>Úþne IDAT;ýóɲ´%¯>qÒa£ËˇuÖùý×ß_ nÝg €Óæ;ã;žÿ°]ïÇ™å]\\ÌÚÓ^2™ŒÃÄð¶éæ8Èoaáÿ˜=Ö^øºy=Ô~¸d ©^.•ôèYRÜ«Wi½{–ö*+*ëUÜ«Gy–µmñ’F_­X2|¬Þ}¶””xÅEE…Åe={•ö)êÙ»¨¤°W‰î[^Ý7ûbi£¯üÍâ’ªwïÚ^ETTTRl*îÙ»¤gYqqQYUô©-à|²aqSíOG0TI zkÒÿkÃj_~½%íº^ú€cÇÌka©‰­Û¼zÿUƒ :tèÿûû÷¿ö«¨<áâS±ô²ãötÄ £õäå3ÿ0"+½¨º/ç¿.­hð é5WEaÙ©S†›Í›ß¿÷Ä›ß>öÖ»ŽÑcEªÐd¢¾…ND‚àÉŽˆÓïÀûÎþï›O¯tÄЉgáÇwœ7¡Ô”:õçWcé=Gï=hР‰wàØg¦“Õ”گß.žÜ¯^ê¬3rÒyÀ’uMs‚™…YX³ÖÚKB8™H ‹—d­=ÏK&“^¬X±¢oß¾ùùùJ)ûŒ}Òqœ=z”——¯X±¢á»þzÌ^"sœéCk‘¬<ÇüQPQ!"Ÿ­ü¦Þ‹Õë–Š¬ÜR›h¬Pò½ ‹ òJKs{År³‰ÈK&âùUÛrbÙ¹[6~µ©Xs£%”öä^e(ê•[X\â”#§cÙJ\‡ÚÜœÍY*k[ÏjéY¢o@.+Eß^= óŠò{õÈ/Êr³JêäÖº¼ìœXN¬v[/ù¢¨‰×ÓDû šš[ñí)øõ5S_<ãÐâ5¿:ã?›ç½ ®w½~Ë¿3X5YƒF?Ný‰'ÿ^ôÐ+oõ^7çŠS~~ÜlyãÓÿWÄ×mXoÞY _¬XU»ß4;N"Y`xYÌ/ãÕŽíTÛÿ»?f®Y|õq×à{¿ÿÍÔ\»¸ búP0á´3ðö-ý¦çeÇ zó¶Óþ ‡©b˦MKSí[ùÅšj’ׯ Õ¿ô“/×ï5¢çvj¾|k)¦œVþ²ÑÍëݸcñgÕ£Gä£ÝA-¬„Š”(@´öØÓ:™Ô^Bš0v%‰âââ¼¼¼œœœ˜9l Â%“IcÎ*--]µjU£µvîëÛ@]iMWoðMUºàŸ\óá2ã—e£eÐñ€%o<|ß+sTŒ>üÔSŽT¤ô?õÀ¯}à—?P¬Æ±üv@½cæä†O—Ãìßù½GõU‘æªÈ+ëö‹fU«1ù¬Ã+Í)NÞ¾ß;ñÑ?þ€†g*ɪÀÑ#Kë]o`ÕÀ=Ç++j’hñHï8´—­M J TÆ• }ÄOS!´…¦yFÙ¨±­ëóz÷,-,,‰9¹Ù¸.‘t˜ëxJÅ6mì=jD£%äÛ+–µ.§¨Ô--¤‚bä•8±lADZ5¦ˆ²ÔÖ쬪¼a¿¾×à9Ù_”•ö,*Ì-í‘_“• PBdzk\×%…-9Ùë†ï>²E<#àŽÁÃ'ÿé?E÷Þöç‹O»sÿS.v좋÷ÎÛ8ó¶×d[W¯xü²^Å€³®™V‘ÕdùŽ£H¥^3Q–ç¿ZpÙÿý¾¨jÉŒï¿q‘ºî®cKä–Ú3¾kÂÆ[Äí¶ùzHÑÀÒ\×UàI‚Â…c;U¸#ŽüÊZ÷åã7>FûÿhÚ„¾nòË|"Ç1g…#>”>~öÿ*>v|ï%ÏÝò*å›*jãµkˆÈoߦMÕžküÏãÛ¶†ÆWßl®\˜r ضâ•ûçÓ1—Ñ·á(Õmþd =¸ ܃F6¿gÑ¿¿Úà ØŒK~Û@´'Â&ÀldLÀøÚj_Ó`ÖdÒ¥±nRNï?qÂÖ§ËÉ/ìQ\š[P€ÂÊËÕÖ9 LTS—¬­MV>¡ÑŠFN¬‰? Ùý¤G)P~Ê̓CRW‡Äq©®]]8ò¸F_7lâ’mÿÈuw+È+.,,è‘_›“KBqN¨D(^Ãu5Ÿ2±%M̉ˆ9Y©‡U¯Ãμæ°3¯°ø¡ËþŒSÎÌØ8÷œ£/YvÈ5óï8¥°i†jàH> VÉ/®P]åoûN~O Z˜¹vÝÒeÀË£|ôÅ}äýÑ'½¿t5Ø+U–*Ùëàéoª “ˆ6û%àôA¹é-h²ŠÐCùù݃ªscð\·jÉÃ\ûô!ÜtÖÄ~Ì ¨=þÁíÿ¾|±òÿpÈ‘c ¸vÁŠ•ÀÉc÷Èeä|æu¿{÷©_Vo¨eÎN$ì³_%3÷òïnZ|ð¥/ølóè…M`jÐ+&ÇÕ««ü/›ØM_­Æë—Û'\y^¯gž~ý†—qÄÉ'_ðð¼Z0ðõÛ3ïyg^yë~}“ _zìŽ'ž˜~_Émçîç¬oú/ŒŸö‹3& ^¿ìͷ̼纙½n=w°©Ž¿þ× O òäƒ*óïrêÑäÀj€Ûàùv‚ø’¯°ÖŽkbE‹ñµõÃú³ÉÏ©=“Ó‚µ×¡î3yò^{uÏmuýr ¤¸XõÈGvEY1&ÓR_ùeQéþ“'7®(ìð¢'žXëeåäg•PA>²²áe¹â'eSí× 7–íyÜw}ý q/zñ¹šºd®[P\XÒ#'/ Ù ”@Ü)R‰:©«]ÍëÊ&MiüõâëÞ}å–ßðß×_RÕgð˜½K¬[òæ²dŸÝÊrÖÏûçiW?]yö_'õR̵³®;}|óÚÓ×$‰DÉ!ÇÖ/Ëÿõóï]ñÌôÇÿ{ò^ùjÖ,Yôå–/>[…eŸÍyë­ÂÂcöê×ÿ ã§âþ«y}ßߟ³[í¼é—>ŒC®Û-—yåþtÓƒc>i`·OþepúàžÌ.ö€Ó§ãééÿï¦!×>|Þù2ð‡S¿¥˜k?ûט£®8øŠÇïøÁ^MVÁñw¾åé­¯8sRnùžƒƒ@]üðô¤CÚ+›™õÆ%/P;tH_w뇿?áBठ¾7”e}ûßù·oŸ~deݲ—ï{˜ZcfޏúÆ¿ ¹äؾޗ?ò0pÈ>»÷`æ&0<=î]‰¿<ûÉùãvwâM ,_øêýÀù=;‚e€µv×$ÅÒì™âçÔaö<ƒÈÒá{öìYWWÀì×fãFH…%¢ºººž={zž×°ÞÎ}½Lr* =œ?zòÉ£'Ÿ `åœûŸº[‘çy¨^vûoî^9úû8çÀ<°WÓN‡ÎÖË)‘š­µ^I6¸ù"5Z{^í¦•+EŽ®,d…Ž>÷ÿ}zÑ>ýr½W¶LæVì'òÎæÏs$¿zï}‘É=Ýôî5YE衞#Ž=ï¼mÙ<×ݶrÎݾ3úè2¬Ø<Ôg¯Igí5 Ö¿ÿS‘ÑcwËòj×}µNdân}\ݽ&O;íã¹ÔTÕz^,‘Á»—{žW2d´Ó?¿ò¾9Ÿ~µeÀ€<+_{ü ‘§î›ÛèPñð=äÁõÕþMì– ëDö(/v[2•; ?Y¤ò9j­]%ÆU„Ì&³›á,Â"¾žÑІ;h÷ÝW|ÚÜ{î,éWÞ§O¯¬œ\·GHi¥µ‰MÕ[æ|´¼üGgÞ}÷D"Ѱ5ƒvòùÞ?žýîŸîÙ³oÏò,ÊqœÒŽ“DrÖ­/¿·&{ôƒvÚèëCvrØg¾>÷öÞ½z÷,é­œÜ<' ­Äã[kª–Ì]}èŸiâu‹øª9§ñ+óù™/|ãš_I>Ö¿{÷Ù×ùîᇜûµ?9P‰Ÿ—o»ìeóiÜ裦ôÍ‚H.qÄÈ¡«Þ¸ãŒkÌËÎ;ã~Œ»zþ}'çgíqÕ¬Û×ýÓ³¿÷Œ;ÿÉë-ôýÉ ýꈳ§Ÿq‚)ô¤+î½è;}E$\lÞž'=ù‡5Ç^vá!€KÿúÒQÌA¹ 14YEò«wïdí Ë~,Y¡î'EÄ$iîgÔ®_xáy×ø¿ ûáC¿l,Á'\yí’ê_ÝxÞ#7ú#òäUGe‰`ð±OÞúÕ±ÝvÚË·™Ñ¸ö¡+÷Ì‘¦0 ß:õÒa=û®çxÃQÁÍÿlö…àü{¿WÖ”„ßbHƒ¼ eÖZ{ ‡”v”C¤„+jò]ýúõûä“OŒý'++åÐiÄ|Ïóêêêjkk‡𝧬úô³o¶}µÀ§‹?ì[[Ò{ð …ª•KVëâ²ÂØ–åoÜüà;‡ÿtD‘’óŸøÓr¨^<n\{ž—?züØÒVϽÿºÞ9åò›' È߸òóõÛ֯݀Õk—.[–—W6x@iÙÈ ãðʃåNÌY¿fí—Ë—\]]tà!GsÌ–-[š{ý¹§6­|rØ^\¹Gï¾}Ihý7kW®øxéRP~Ìáßý^ó¯?ûüSïýTÿ‘±=wRÞ««Ü¯×³låò•Kâ{Ozds¯ozÛ–ªšxÙ¥em;?zÛúuUpóKË ÓõK½qýF dç—æ5©yÆ·lÜXÏ.ê]Úô3MW±}Ä·m©©‰k8¥e¥õÞoÙ¸%®áä—•æÕ{gý–À),+ T‹påìß|÷’GïzeÁIJF[ºþ®Q“o?ñæÿ^5¥ChçÞø˜r³Ü¬œXVŽr³L†­µ—¨óê¶ÖnÙ8ëÏ¿p§¡ž¡”Z´hQuuõ°aÃJKKí‰3{žW]]½téÒ¼¼¼‘#?`ëÜ×-’_¿}þôûC_TþâÖ‹çàËWo¹îasø…1SÏûáQ£ó nþ½Ý3 Ÿ_ýöÌßÞÿæ©WÞzPE€ÕoÞõÛ™ BOM»õâI9@Ý× ïœ~‡_nåÔ+rTE$×/žqåm¶àI'_|ÒÁ•14(vþ“¿½çóÌñç_w؈Rɯß9úßGŸzÅy D“UÔÍ¿÷¢{6ûͨ×ýÓ¯¼u‚yhõ›ýv¦ÿ[ÿC~qá ƒ pÕÛþýþ7ýR1fê•?ò[¾ú½gû×gl?OÿÅ' .€m+îºä†oŽ8ÿŠcG4e¿ãªÅ×ýâ¶Š3§Ÿ±_qS›üzþùÓï™zñuGUÖ?,ouÁÍyÅe9…¥±Ünv®ãf9n ?óoæŸfuB´Ô^ÜKÔ±—h†g(**újÍšÿ¾þZõGÆ7C@~Ï>9{ ßoò!åååUUUÍ·©¨¨è«5«ÿ;ÿÕš­óµ·„ܼЬ¬o}kÜ¡-|}ÍšÕo½ûÚêêÿÖ$¿"E…¹ýÊsö™ø­Õ! ßûçí_O8뻃“¿â+þò§w¿ìÚ7ZBçÝô˜›«Ü,åf9nÌqbDð¼¤NÆÛªã[7žÑhhB×uß{ï½ÚÚÚÂÂÂ^½zõèуˆ¶nݺiÓ¦ªªª¬¬¬±cÇ÷öFѹ¯o\·­&žL"VP”×L()¸jó689EyéÊÕUÕ Ä² òrš<&In«®®KÆòŠ š~¦é*¶dݶºx’¡ Š êGLÙV½-ÉPÙE9õÞ©ÚT^QAkGjý{3¯üë›çßpGƒË‰UÏžû‹g&}Û´}ÚtšÄQçß”[\–[Ô3–ÛÃÉÊQN,–• €Æÿø¯",¬5'Y'Ášu‚½„NÔ±N<ô»3êTÏÝ0777???‹YûšˆlݺÕX›׾]Ð"RSSS[[ÛèóõÊ©W»±0Ø×›©7ú~Wþ¾©g~zÓc*–ífå¸Y9N,K)Gž—¨«Ûº%±móS¼¬)ž ‹­[·nÕªU"R]]MDÅÅÅ"Ò¯_¿Þ½{›<Í s_àO_›µqÏÃ÷ëÓ˜^\ûüÓp¡· ޹èÖÜ’²Üž9ùE*+Ûq³ÜX3Ó§˜·hs¤áç¹ÕÛ•M¶nÝZSSc]5´Ö­ÊBÓ¹¯Gˆ3+ˆhí‘cÜjýÜîÊQ6tSH&“½zõêÓ§ˆÄãq®ëy¨%[vç¾! †|ç˜!Mýësä …%m7‘p͗Ȧvwý¬¶æøÖä87'âþõ¿y«ì„ÚÛù¯Gˆ` A"E&8|ç‘àúŽ‘NÌÙrS…’1AŸ QK.FdÂë"„¡‘R)'`˜üþÉ8ˆ8àJ‘Öék»¤b°aâ€.ÐÑýVÍz÷ãu’‘ÖZk­êùJeJ0Û:ÂZk­}Š™¹óOø,"¶‘è®"”3kå8J)æ€(”= ·áØüÜds|‡=ÖÛ£1ºñÀ‘R)2ù¿R¤ˆ@)}å*?í €ŒT5‰P]]T„@ iÖ&–­q²u™Ù\X""“pIˆX˜ÉJÏhD'm…fÂ-v†BÌIkV®¹Ê'&fgÃx…™†H„jìò"”qª%fÖ¬cäÿéÚ[àlczB²ävÂÔÞæŠytØNÈœ!Ýù#1Qæ„/tªaao2e,"ª­‰P1¦ZG9æ<ƒ”CD®RʸNÁO¤¡Ô޶'Ž|¯åh¹÷H«è$âb‡7šn0’é¾¶!÷?w ûñžUÇJQ‘Õ‘9CÚV^E)åH)eD@ÆÝÖw!dÒ=ðí"sæ)BÇ£…þQ-™°çá”|Æ·P9NÛÊP­b-ï`«h¡û…HjD"T€Föq}ç IDAT™™Ìb\©˜µ§u d´½ªÖ0Àvò‰ì¹­ECe.pçùD£«ËäveçBã7•ñ†)‹ÈBµ+£m/-PD‘fQ¢”r­ŠEX3ŽRÚdÛh„ µ­zØZ:‰ØFk‘iõ“+§Qna)€ n½š˜¶&ÿñÎÔØT"ªËa¡”"“„Ï€›Ãp29—üS ¥HÌцO]Ã6e‰W-A®õÖ¢=¸…³°ò½"ÍlÜ aδ"ª+"Ó†«=ˆ‚Yˆ Ìæ Ül]¥”ö´ ˆM¤‘ ù3Ú»4Ÿ ¦™-¦ÛŒÒv…tqÄ\ÿVD†‚ÛK™J§yD"TK‰PÍ…ˆ€”òÙˆÇqý$KDøz,'UW"D÷›º;v˜&0ìƒY3wâ!x$Bíšèl Øœ4khíº1˜pç “‘‘Ÿ¼ogªË ´|Oìê"I—ÆÎPȉ#ßk†€NŒV.¾ÏT‡œ€·?ZžÚ¯Û„¢ŠD¨FñœXA@B­µ‰k›4ù÷3ü8ÐÝüN_7ÃŽQx×Ícц"¶9Ï3DâØÌ€‰RØVµt:vY#U×ÂNŠP;ߘ|àDd|£ ˜Ø!dœkA¤Å„,ä kÛZ’nL'­åÝ€O´„…Í•W­9æ¸D8 ›]‘Õ0–Ysç"r%$OYý›:[ÅhU² ´Æ¤%è6g…6çíaÄbvúÑD¬58£c‡D"”E$BµR·¿AXkRP ®RŠ%Í]Êq<¯»¦ºåµØ®E!;‰ö>é%RŽã˜xSŽ"“à3K§žîE"T÷C¡Ä?Ó6® ÊqL¬gW !‹©ÎU6Z)mí@’áV—`~mHç8Ü¡žS‡|Ý ‘….ÎÛ]„R©»ßÆfKqƒo!ðÕ2'ÃL½qÙù=¨µ¹`3pUu [m&+ÝMÁ·Jâ"¬aÂvªçT$B5.Áüº–Eþ¥= H!8ØðÏÀÅ·^I*:2Ú€¡…Èd:o!:LÃ0–Û4#®Q¸ÉÝ1Íh‘Õ‘ÕNà@sðýìëÆW)ÅÚ"s·SÈte:éÂÚ7¦;ÃW0|×t{œYwObØÅ9´¼ÃèPÊÜÏ`Vb¢w \ÌlœkE|j1.ê™ XµZeÌͶÑ%„©6A'_i6– Ì¢"G‰I£Ñ=¹F$Bµ7º®¥ex@ŒªM®e&’ò ®2eËØ®Iø×¶5æ¢knÁZ–4ÝZd׸Ö‰P‹Î¡ #pk câM‰øÊ…yŽü ÔÕs“m×ã°«¤MÎÌøm—;‚&™È(uëÛítGež ‰PÚþ=>ÍÚ×5”9³°öÏÇMxÂÎlj³Ø’®‹.Á-Â_¶vcÊ>‘‚QÃ]¥XØ5f\d‰D¨zˆD¨0ºŸ¥”ŸÕÕQŽ"%"J9 ñý¦‘جà)}<Ó9‡ÁÎDýÌX:éÒÚw{ä›ë8«>ˆ1%œ±‡‘Õ<"j‡á› ˆ4k%"ÌB 0wúD@¤”ò´ˆˆE#S‰dÐüüe,ÛÈLtã¸Üþâgñƒ%øŽ!ü”¹ˆD¨F‘!tÚuE("ã<()…âÛmEDkÎÃaî¼Ö¶=ÍtfÐòÑí)zÇä© ¡ƒFƒ“·IÄòöic’ŠèIÄA¼ÛÎn`Û B@ë÷Ù®â¼ÔNhùyR—ƒRJZ³£ø±C0ñ¦´ Ed SÝIÏh!ºhþËö`meœÍdÑ>‡¼ÑÍý>R¤”âîÂ0,šQ¾[V¤½Ä3Ó$Õ*4•²·“šÓ"ø&¨às)@¹F'¥\Ç1Çàbc'tG´¡nÞ-Tmhœír°~çøMÛ”QÌ»-I4H„²Ø5E(ƒTìZff†@³0³Éûê(e‚@gxþŒ0:q&ÚÜHÕª3ÍÕÕ!"6^‚9ã3†Ú U7äÍOZk°Ýéædµã5oÜëN0Z?3€«-lÌBOkKÝN÷a¦¼™³ÖØ&œ#¸EKŒ³Ísè®(I¥ÂIŒ0ÅÂŒ®sž‰PÚÖ4ed)¸Öq”kÓ0‰ït«´ÿBW:ÑØ1æ6Án&ºì"BÓv¡”"HÚ‘žÝSë®~?£)ØÙoêl£µ„Ð&Ç­ª´XEK<к®OT ž§O)"ò<Ï5‰] 5¤¸„ÜÑYmÝA´­¯z‡ùv"sÚ±±êŠz-A˜I(¥´çùAm»NÈÎH„ÚD"T?ë7`Üm ÐJÈÕZÄ^ß’»R×Ò3ÂØ¤fxu£³|Bvž0º“h0³w)k’"“¢¬K!¡Z‹H„ ÃI(¢üD ®ã8ˆR"¤=ˆR$~RË®É4:™Š' š‡R~Ö Àžx‹õérj·A$Bµ‘Õ”òCN)ÖJ)åË̬uøøBRŸ»&•´^ÂjžNÐú¥ÜBjé°T”mÂ-º+UX0 ‰(G‰ï›. …+ìªäЉˆD¨® .ĤSrHpG´vÍÙ†0™ð!¬a«2:ZB `÷¸6Ig†6Õšw©ÄÅ]þ>Ín,ˆ” †ÐUmµ‘Õ‘Õ¤N¿‰ˆÈq“ÂÒUJiöIƒ™ ¤Yl¢×]-q$í¬;VN×J>œ  b‡„V¿ÖZkÝ è!¡"´Öé\4k¥µã8,p /1-MŠ@~.¿Îkn[¢å&ÝŽ¹ðÜÞ§|mÅ-vV™KL1œƒ@Š¥+ÕU4v‘Õ vºP&É1‰cJHXàx~ìÃ6Ì®n¹m+ݼ!t!yj!Œ4˜»K†U("I!ˆÉ‰Pí‚H„ÊX(,ŠÅå(J)I(í™”RJ…“Yv½ M£µóÝV1Ú;nCD'{; EŠln2­aò!#rj×DóAp[E ¿iíݽΒ¢vA†@‹‰ (;ÛQ*PÆýr1Zy·bíp"lo%£­ÂàdxÄòö³@Š‚€Mh¶î…H„а] Q¥à*Qœ›¥ú—˜ó üìߎRZwnSÛ mè¨ÞZt·hÕóMaWfõ@€MYi.Âvjs2;pucªhÕóQì$ÈϸdþàÜ,ô.Ì.ÊŠ¹©/‰à®ïÒ†hÛ-D«h#"Œ6‡Ž ™ŒÀ$CîfT‰PÛÅ.NÌ¢@@Â9Ù±>E….>]»Å­G Onç´²½Ñ¶qxÐI9"’h/ÁXXàgsUŽòlIg·¯“‰P»H ‹Ry¹Y½{8ù¤Wn’Í[ã.ÌÅoøÙ+)&ê:ÑØvm‡§ ±]òˆ"´+D„Ðq7™€Ð 29û:³qí€H„ #"z &å@e¹%=b…Ù± Il©‰»ŽëZJ`fã+ÉS¢)ݼ­î7uÈÐRˆ0‹röØftWªˆD¨‚”йNif»_×x[5„ÈqáÇH0¡Ù”IÒ‡®–=c‡Ð¶Ë¥]]ÛÄ!j÷†jˆ”rT`§U bù9B€fR–µ -D"T§ÃuÑ3?Ö3;÷›ÚdMB b¢»ægÿâ+`B¢w¡¬d;ƒN< £òˆãt4̬  ‚®‚lÝš"ZK Í£]TQt þ{okÙu݇ýÖÚûÜ÷=_’"EQÉ‘dJT%+¦-©1Ô…¸rP»@[ÙŽƒÚq‘Œ¦V J¡Ø œ´Bœ° ´N´)نĒê*°ÅHr­Š?Ä%~ˆÒ£á¼™y3ïãž½VÿX{ïsî{oÞÜ÷uïyÑ}çÞ{îÞûìµ×o­½öZ³=7ß+.¯ë‡îlÐ’ðõ¤&²~vPcM+0P}Ïôj[é:ÀÃû¿É”Àö3ÌìŽG”¦É¶8ØC{Æ–Jkp …»¸V6npÉÊ!b9 "b‰ªb›je-FÁô2;¾P•x¬5žéKnÛ)‡–BµÈ¸:kƒÇ¥”””É{%溒ˆeü&ÝÖ1c·¼cëÜÝÿéÖM_osO9 ÖRNZe ²óM-FFK¡Ž:VB &çœ „s:Ó—R†`¢cz|'ì*z$ol¸`´dj‚ÈJADš«gX‘×)ñSí¿ØÆþ…bSrÏV(&†Š¨AÁ @D|¬GÆLÊì/5ñ8ö¡S[±Û”ŸÜO·‚Ѥø%mîXËršÐR¨L bQ&V"Q%&‹ÉhB§©SûÀg~ûÌ£¿tqÒ­ZÁ8ˆ‹³³Ô„VS³Ÿ± -…šj0ˆ‰˜B)Ä–ÚYUÕ±<@܇:ær*%Äp°A‡£à3¿}fÏßm¥â`aºA$8W0³ª· GEK¡Ž B`(‰úŽà™)”jy>µD„cPÌrÏØ•ÚQN¶Xß¶@+¸Îì1y§ tZí ´jŠ¡±æ7ÇðA&R¨‡PÕ`f™Á‘èZì‡Q(¦‰: 0GalÉ"¦8çÔκN%v5ÓF\ñ·•ˆÏüö™Va4 "A³¨@D>Õ­D¶3˜9·3»ÆA™uÙØ”™jâÑJÅ«UZÕ>• Ç:ßÔ˜qYvZ¹8 8gçöTKqEdQDä)&ì¬ÜP9³ÎÔú¦ n•Ç»í‰IµR1ˆ(©ˆ‰C e¦R•˜Ã±«Ö7:Z 5… K³&JŽ9ÇÌLªð!ˆÌJ2õGÁãTÞß~C–}êžV6ƉH•Œ/Y¾)È7uK´jÚ‚ˆ%@EBªJÄ>F+w2NÃNºÙ†Ñ¥Q}&Ó«IUºo±˜n°X[UUrÑO5å¡S-…šB03ÁH~ÙwóÚf‘¨Ÿé›N;cktÓ£¿tqo[sûçeQ ýû}Þ¦ÅHPUÎY;™À)HÚœr´jÊ 4 ‚ïZ®s‘|äS5 ÁÌBÓ{Ži[Œ¨6>óÛgª‚MÝ#¼)•B‹C…ªŠ¶4èPÑ”¤yÞŠ–BM-bRU²ã®æyböα€-'3C¨™Âó•ö -sts?J«ò:Ð Âþ‘7ó¬¤k¬¹Äµ=ðX'<–B>2®Ûƒi1ÕHÉŸ/Y.[b.EbÖç#‚Ã[1œBž‡ª="¾O¥T ù|;ï‰ ª¨•8Þgú&>‡ö 'hÕÇ&bBÔ ¦9ò!ð#ay·ªÅ!†F¶”v¾%æBOÖ•J–Óf{Ö…yÚú>6Dÿ,!W7cœŽráΘ¸’h)Tc }{‚s¬Lev>Uÿ¦ ²éx3±ŸsW³gWfxž÷íáÑ…F׬Z|!ñPlH“åbÚhDK¡Æ;¥‡ªHeQ«NDjâ'·„Q5c›£¯ûm˜ù±BJ¼†”i§±²ÑR¨‡„<çcúff'"Þ.…rŽBûø1³ÄÇfŸþô|ù–rbhõMaÅÀó†ªÚÆxËV¶ªÅ¡Âö)‚„Â꫊ˆs®ªÓÇÌ*¤µ«F©±E Ž(!;ˆÇ(jc?Ø9z¤õÞî±TFÌe‹:%ÉH?>h)T‹‘uA•àVU=`&Ȧ7}cã–8$…qP“{·»£c“FiµÈˆ°|S°óD¬*9½¥P; ¥PÇ ‚ªcgŽ(§¶Ï'Þtˆ*TÄâ¦ò‘׆ãGqkŸ ¢o‰m£GLx¶W¨>"˜c@º¨(;“ߢzÔ-–BZAD <‘sÎjVV?CÕNiPJdh¦ˆ4€Xó ë‘Û¯kgÿÃ"kí¸’ª@AŒFme´j+Z u¨@-6$%„ìÉm‚%¾[ñØó<8rÚb[ì<\m ú-¡ZOÊ),±NC·j´ÓûÀ¡Pï¼*B¡Óñ°X[+»”²ëÄm‰S«fò©v§îƒÉ‰@¾ðH©ÙRM ©¢ '„–Bí -…Ú' ¨x‚sÎ.²IEVDÄì&×È {JÈÎ ¦U?Mƒªr¬~œÒ"¨ªDWídM–Bµ3T$»Ê²´ôkeYz‹=—Ú)׉[»Â~ÈÂnµÅÁJHý×Ç#{­'wg¤sàÈ›z X^Ûøþ„06 ‡L¡v¸y«~š†Xç8ˆj´3LYDß”}BêÝÉá8QªwÿÆ¢ÞjŽ‘RMA¢ŸV- ô$Û42Z 5:ZA6ùEÄ9/"ž™,bY§Ðü|Su4“OmE]5z¤™êö¨À”„sމœ‹Q"ŠªàëDÐÌgÚR¨ãóǦJÙm½ˆVåûL:bj<²·ä9‡ý»‡¢¾íàV0³*$rÊšcèQñÖ¶ªŽ–Bí*b§÷œc rDäU5:rR¦Û¦c<„GËͺ«LÑ-ÃÚ ®@&b¤ªAäß<´ê ~kg´j+ˆÙ*HDØÁ¢Ï=3#Ÿð˜IS ÕøéÕèr\ðXå»ÕÇu0÷­e QÇøÂ†ïh´j+Z µ/¤ímQñ)ͳªúx&# JüWj%.›‡ñ„Õ-ñh±¨*ƒš¬ B(Ca"9Z ÕR¨I‚ˆˆ‘BÏUÕ{gñèVO&*‰hƒOÂ7uØføRc>TµmŠ…)«i Jr!j5g0™ c£P-‹š*¨ˆª¨ªs\/'ù9ç˜b|È8S»Z³Ž4 ]êö¯6v;P­ÚPU%€¨ @wæ1ŸÏh)TFK¡& ‹'´3}ªÊõò|–ÒV&´Ýw¨ØUݘ–OM-ˆ£y|¦iì~Ú–BmEK¡ÆbÎF¶)…hg˜ÅA#Ð ãºÝÕ³9l ƒ¶]'ˆœìjĦœa)”ˆ¨ëóVÑDh8Z ÕâÖ¨­ÿ"R–¥y¨8¿ÉU1TÇGHFÁq•#MEÇ ±ÙŸTLÈ6.S£¥P7CK¡Æ´g!Aòþ…ˆTZa“ÞÀ¸©½±ÿ£=ûÁØäd˜Z9Iåù(eñÌ™tާÃvg´jÊ¡5yïU5šÛùjµïÝ0mÝ?éÝªŠÆJȨ֙; †â>R4:RÉ¥¦i–BÓ) ˆ‰s vq ïÅ"ùs¢Ò4ób ˜ˆÂóî']ü”@RõoQ• T‹¸mšL´j?h)ÔHH´É2ˆØ!pT±¶É]•€­a£<¿=HÈ^›ÓD´ôjœ°¬LNP‚ &רóŽ–Bµ "DÄ.ú¢¬Ø,nʶø¬™y%vU)´'•vÀn÷¯% „my瀩X›¯’‚qˆCK¡n‰–B –ÃÛÛÕ8ÑP„È8÷ÀwÐù‡DLUL\[Œ3>=cWC:U¢¢)Y§*‚ˆ‘§x -…Ú-…ÚvÊÕŒŒ|PÃ3“U(3«PH§6ÂØÛw ºá˜Qª:Æ™æs:,1#àU,11Âù‘ÉÒyµs!¿:PÚlT;€SÖ‚÷Eˆ¼¨€àEJQh^ íqňUh£W˜91ßTV"ÊŽ“‰1nqh)ÔÎh)ÔaÃŒ óÈŠˆYX"*râ)UÐ8iÕÁaD 9ºSí`—€ÖCµ ¢›ÃiUć#e÷lq¨˜”s¬•…maûÞõƒñ|†•®¤*²*–gšp{[l‡I1Ç©"MqµªÊL”R!0󓈖Bí ­ÚØ "…Ös¯UÎ[£Q)ÿQmq¤%dR­j÷ëàXŽÙê¼j<ȧ©ŸÑâ–h)Ôá“Aµ£ßeY2@–䙈T!*GNgŒ~^©™ c·˜½ÂˆŠ¦ …UÜ`zq´|SGZ"Z ÕØ!>ËôLDÌËè0켟6-O ™Ò;U°Ó| ’z H­ÚÌ‘@K¡öƒVmdÄi¯1ßšˆÄÜ!Ä ‘+±)ÁXõÓÅ®²:jKö–^MDäØq ²@ÃüÖ‘ÛÏ8Òh¾´{TǺk‡ÀU•Ua„övîõÈŸ¸wJ¢iwƦ#¯²µM:–FK¡ö–BDDEE$'ž2ÍÁPKªÿ‹á¸GB>vƒæKˆaW'Ã'h•WHÚµÈ$‹™UETZÕX´êP’šg*n[X^[f®Š/Õqì4Ç‘À®ÔÛ1>´5~HQEQ €ùp'Ù¸EK¡n‰VmŠQQ¦0`34nq…m€k° ðãB«Ž} ƒMEuP·:’ ªsàvDCS!),»Ô´jR° p›@8ç™8„ëg¤Ò}`&&¶€Ü#±¥±ó”:¢Úb·Í§Ú8Æz…# s2¥ê(Ó‡[¢¥P£ãOõD‚‚ÀvÚ•ˆ¸®Ì急L8Záè-Zìù˜ëV¶dû&Ѩݡ¥Ph)ÔÁÁQôÖZ¤-&¶|S¢ª„¸ãl*ôÚtlKŽ:Ÿš¬œl+ ÓP¦Æ2?ÛA%"°ã¼ˆ±§)lÑb ²\¶D1ލx"(ÀÌV¹/ Ñ®£‚#­!ˆc¯¶Â(•Ä,"ÄÎêgØ!ØI·nTl›)ù¨KÇèiÒ cH–>¢êG @bx“‡P–Up:T¥y§”“éDÜÏHµe‚gß;jÅÚ™p°˜ 1 QU"ˆe¡åbžÛêÈ+be×c:ut±U:²ä-=Ë®£"R £šhÓ¦ôÖN',ú#ˆ°svÅ9çœgfÊ¥û²PÄ|è­4-a+,‹gŒ›Åú®hóÚ6-…'HÁÌL¬"v²Ï2PqвÒa¡èGq?ã£eXㄊ Ö³ÌÉ:[ƒ»)h)ÔØ@̪Z†2žæs.¾Èûò~†}©*8Óbâ8êa`G1ÐVcÁŒºå=Éfµ¨¡¥PãÍxç\®È‡\§O+-`û­4­æ8lhLÒË.!JÎñ9Ów<Ð ÂO¶‚‚ bf©tFvRYÆô#t>cÚÐJËa€haÇ´‰9Y I6­ÅV´šãPe¤{ùoVE¶6ˆ ’¶Â‰Zi1U©ª¥€Ñ:qjIT3ѪCBv5™Pˆ5—줒H0× Œ–Yµ˜&0ñ¦ø¨$ Zû·E‹iV{¹þ·4i® IDATròÒ¦T·9)ºjK¬‹c™V¨ HuÉ¢êPUá!“lX‹cF^ÿc„¼N±Ü±ÄÄ –xÊ 3M®¹-ZŒŠè•@5™iCÏŽ–Bì ·¤‚KÏð©Ï•, $ ÑX$€™%L´É-vıL+Ôd%Q×­ÙÝbª ª¶ Hàœg"ò”öºë"±IZZ4­’8@Ĭ9Ì U6¡È¤ªÕ FK¡悲óLñˆ†ªÆœ…ö¡¬)˜9–0½E‹iÅ„S"Áq ‘ *DœCGZ4­’8XØ )œcçD4žÏØä·…¥Oh‰U‹)ƒˆÇ´çö¯q,Um3=·˜.¨ª™ DBçœ÷žs…™\ÀR¡G¨¶k‹&VQ‰õÇ$O"nm[L#ÒÆ§Ä|Œ›’Ê=…v?£Å”"EÚB)W(¬Ód[֢Ÿaù΃ˆYÑÎØ¤!6å+lÑb“MV·Ð(­,´˜:ˆ3[©¾¨?BàœRD Ì´µ–ZL/ÌU ²R•ÌÄÄDm‚çSS eYŠJÎ7åœóùíø/¥ãL­âh1e°ŒÎ1‰Žs¶ $ˆ´Û{-¦ "¢"Î9ÍÕÀAbˆ‘Òˆ%ÉòÆø„[Ý¢Å}Si߯Ê@­«¶Å”†ÁŒ Ue ¹U•üF¬Ÿ1Ùö¶h1¨jŒ2·¬ éªDÉhÑbªÎ´Zµ¾®ò¢ƒT«ü…­|´˜NHõ¨jïµ3ZLTªÄÌÄÎ9罉€ "õ¤9åT‹SB­²LMUXAI·®E‹±‚KÉj5çA°XÛêCDIwXtaK¬ZLDÄNfØ©o36¬r%ÚÚ®-¦ m‚Ös¡#ɦˆí³­ƒªÅt!&)¬¢§`Q„ +a9ÑÆµh1VÄP© ’CBâÿ1Wš#©”-¦ õ} ûWDÔ$¢=ÞbújÉÕâ#"Ž˜b ?a6>%í±×SÕt: nSK  =o1up̪(C)*vÊUD†ö3ìu+-¦ª¢– Š˜Ô–²|´bÑbªÏ~3YÖ*íÖdSæÐåVDZLŒLY”ˆ…ž·é:[L'¢“6mãIÊÀæ ‹ª„F+"-¦Æ©ªPŽUÉNöQ+-¦ FšÒþ71³ÙÚŒ¼?>œ½åW-¦ª0qL…P.lCÏ[LòÉ Óæ¡òDT"ŒGÁíD™tžeÐõRVÖËõ BãŽ_Ž»Ïu}׳w­Ó¯ÑPU"hrÏÆàt =ŸÑbª•€më%ˆHLŒ.!Xd:3ª j~=ð¬*VÖÊ•õpc=¬BóT£_¸™®›ëº¹žo•GC3Ú²šJÉBÐ&âö|F‹©‚UË„|Ý3“ÊÐâÅÌbIð&£ º²^^ZÙ¸´2¸²Z^]\_/×K)CãdÛ;êzžës]·4Sœš+NÍuæº~?j£áu±býÇ£•YUT㦟VÇ2$‡-VÒµ.Ë3û¼™‘>fÅ€ì›2…ñêÕW.¯¾zeýûËWo¬\[¿~uuåÊúêI·n3ºý™þÜbwvafnþôÒµµN)Àö¦6Däk_ûÚÓO?ýòË/¿úê«ËËË‡Ñæý`iiéìÙ³wß}÷ÛÞö¶‡z(çnj:R#JLL–õY­æÌÑèB´QŠiëïØ ¢1E1*D}Üö#§"{1ˆoj÷²Âøîå/ïû¯½öÚµå×Ïž\xàM§o?ýÀâÂ\£´ˆ\¹ºráû¯÷ÕK¯¾ðÊÚÒÉÁÙ³Ñ7¾Kµ!"Ï<ó̾ð…_|ñ­o}ëþè¾á o8qâ„sîðÚ¿[„._¾üÊ+¯<ÿüóÿøÇ¿øÅ/>òÈ#çÎkÔC¹˜ˆ8ÇÚ¿Gl|Ú(Å´õwl hd€™Ó8lùìóßyá›ëk«À]ªzûâHjÃ4üg?ûÙ/}éKwß}÷/þâ/...ÚY›MÞÆ&`aaaqqñܹsï{ßû>ÿùÏò“Ÿ¼xñâûßÿþ†KK”‰aéBVL\/§ÑXL¥˜¶þŽ"b™×ò>+Û—|Sª1›½ˆH©3ÖK¹´2xåòêó/¾réÕï¾åΓï}èþù^á€0qY–Z˜T•‰:,'û¼xÇÒí'Þñÿ}íüóßû®‚äž7Ž ÇþV³\U?÷¹Ï}å+_yøá‡~øaç\!„hާ/£ÀÚc+ìüüüýØ=ñÄ_ùÊWœs?üÃ?ܨ¦n‚i ¢z‘WHR!¦ ·ïæ˜6J1mý,;Ë7e#ì‰ØÎô™~`æ²À Ê ›ƒ2èòÁ…åÕ¾÷úòå×o?1ûÎÞØ÷DbR¹]ø¶_… o¼7ôÒÕµ5ôz;}mÏȇ]ˆ¨ïé¼ñúÚÆÅ˯¿Ðé÷<÷ Þ9ŒJDž{î¹§Ÿ~ú¾ûî;wîœÅ3ä$z7ëo¹F¾7­_ﯪž;wîúõëO?ýôéÓ§xàÆ2,ùk…”ꔉ6J1mý,dPcª)±”!Dä«C|P…¡Ü· Âz)Ë7¯­¿¾|¥CáÁ{Þ0Ûq”ÎêÖ’ºß\—»ýù¹=žÿ–ßûË+Þ+ןž¿çÅg^|ý³Üæz«T• ³÷à=w\yæ¥K——/.Í/Ív–fŠ›™ªZ–åW¿úÕn·k ®ÉvìïÚÓsw} ûOÿüòϽmÜœkSE„™xà×_ý‰'žxó›ß\E3E…ˆ `feÞ[¥TT£¡z{¥5L’BíR´jl¬º«ˆøÂŽ·F#@²Ç­K L°¹Ûbe½\¾¾ñÊÅå+'ç{K3…cä3Š#ÄNø¾eîcë?ý½kß|áò_~jí§Á?óƒóß/“ï¦TUci¦89߬^åâòòõ•õr‡¯?õÔS¯½öÚÙ³g;Ný>;ô×nwó»"¶öWU;ÎÙ³g/^¼øÔSO5–³©" "R…f’ D =Ó·-¥°aGí)ܯÿãO.½íÝsï~÷ü½{áΓ‹ú}âÉ•[·?7’ˆ<éâL§ç±rcõÚZ¹²VÞìXIá[ßúÖìììÜÜœ$µgOèH]+‹ßúÐÉþqǾòíO.|ð—ç/lÿÝQû+"sss³³³Ï?ÿ|hêYQU¡Xc)ÖY‚mo0 Í<Ó·7J‘Ð µ+JÑR¨ñ!âÓtÃÚÏ)UL€Fº¤ ë¥\[+¯­6ÖVg;ÜuºríJ!×_l‹×_e üGW8ñž•K—.àLµº­¿0{îÑÞcw ~ïÖïø—ÅÿÀÂ+DëÏÌ=ú·Š…ŸYûô¿Yýñkþ§~~ö:QXs=Îû˜û;Ù8Û¿ÙonÓ¶Âʵ+]§³ÞX[½¶:¸¶V®—Û8‘ŒU½ôÒK‹‹‹DtåÊ.ú‹X`®Â¨]+ä4ÑÇþIï‘ÿÓß(Ÿ‘þvß½¿W®\!¢ÅÅÅ—_~¹±ÜÊ3+, ¶ïm ‘ãÅálÂ~(Es(Ô蔢¥PcƒeAÇ®.Ý€5š@!ZV¨¯qR½²^ÞX+ëåúÊ•…“'{!„W_}Õ9çœ+ŠBUszóM°ë×—€5wåÒ¥­o-€_¾téËÿvØøã¿ÿÒƒ³zçÿõ†Çþó™?ùÜ•Ÿ¼}ãúý÷ü'—΢xðÝwá ¯_º„åÐù­?ýÖ¿1à ¶ÞÓ×M" !¡ç°0ÛýÎ¥×s¿f»Û¬M"rñâÅw¼ãf†ØßëË sýÊò¥Kƒ|ñ£víâþg]ü\ÿß=séQœþðSø¹ðô¿½{ëwÿæƒÛ0·›õ—™—––¾ô¥/IÃÂZ2º½îziµˆÅ\ED$p#÷32¥xãßh”¢×ëÙÄ@ŠrÁÍçÖ)”]9ñž•K—Ðõ‹™BÍœ{´‡ŸüÞÈc?Õýñ¹ðÕKWO?=÷èß*~â#kŸþAý“ßèÿÔÏϾøç+Xs=Îxç#kgû7]ÃíéÛºiS¥,ËL)^z饲,½÷Û~Ïý­S¨|qíÛ#víÆi¢øOz¿ü“ƒÓpúÅãïYë¿0{ß–ïÞuÐý,ÌÇçŒH"ÂQg m‘3skH ¦)2èzJÑÁÆÚl¿×ïª÷ÄÌÞ{ÛVeæ¼9¼ý-‚KT,ùtåÊìïþÎÜÛöâÛ à‹Þ³Ÿu€ûïzKþÒ*|AÅ«__øk]Œ—þ:EO@xÓ]\;-(y‹H§Ó ÖÂ>Ül ÁÆZ)º^†m}Sf^½zu~~Þ&Öˆý-<pÞE¾æFïÚÒ_Yùëèÿù3 oÌía÷—¿³Ýw‹í{Ûþšª»zõjžm"èu}ЂrAD¤PÇÌä×kª -ïº7Jah…‘R´jlÈ~'ëBŽIó&Ì¢‡Šˆbº•¦ÏðŽ:Ž=“ó2°ï:§N'¯ž¶1³Ã‰ž»ßZÝ?ùÂ̯üÕ »òìÿ{òþ ÷¯~ÁY¨«÷t÷;ÿÏà³Ï~ÿLIåJïSŸêüÈ[é…Ç–þ—? ÿúß]~Ï[ÖŸûßïü‰MÞ{xÈÁ{³Œ°c\Ã{ïœÛØØ`ÐF¹Î®ðLwÓX[UíõzƒÁ ßï›'t”þzOz=®7nô®y¿þ³W?ô?Ÿ¾ôŒûá¿ù¾]Øî»7ëù¶ý%¢ÕÕÕn·ÛLÇ0u{ÝÕ M±ˆ©cˆDçCØ3¥ˆh……R´jœˆzBTU%ç@U½¦jdù£LdÂA #Vs]?Ûó3wç¯Ýاç:‘Mš¼†âæBrö?\ùïÐýÍÝÆÿâêÏüo?µáo2,÷l×ñ›õÀÕ«Wo»í¶[<æÉa0ôgû!çA°“áªéL_w5öF) ¢P#RŠ–B±À«sy?ÆÃðþøÊ»ýÊ_ÙxzþÀmþÓŸüŸ¿ý»sÿæQt®{ !É;uëëë¶âÑ ”¸rc£èÎÌõü|Ïwo’òÄ9wûí·_¼xñܹsfÒŽÒßn>ù›ýO¦+ïûGò¿8j×þìï®->´þß¿­÷ïYÿ«orØvXF﯈8ç.^¼xûí·çö7 ëƒþ,õ»þúz "‘@Ä*šê7N¶÷L) ¢PR´jœˆ»Þõ£`ªÌ¾#h ±jefN´Í›áÍõü\×Íôº—_{ýÚZyva¶SDõî½·Õ3›¨ÛãŒ|øÿ¾úß,»k«(úrzIÀÒ;×–—×ìõßøÈµ ¿â.—˜_ sž€âÄ;×?wað½ËÔ?–zøç¿Nèiχü•#µ{½^Y–ʲ ¤×.__^Y;qÛ S„7óM1ó=÷ÜóÙÏ~ö½ï}ïÜÜœõô–ý-Þ¶¾¼¼¾åfnÄ®¾ðáÏ]ù0;¸õ»£÷ÀÊÊÊ‹/¾øþ÷¿œ‡˜ž{î¹Ñ?eú³ÝõRBEÑQÕÕá5r?Ø¥Hh …R´jl0] AT5„À.ÆÄuG’¯Ð®JS…d¾Wœ˜ëÞqjáÚÕ+/¼òÚÝgî_ìvÃÕŠIrŸÅSXvHŽ2{ ³P³sgq×l¼2»¸ý·¶…Ëxê½·Ñ_ÞØxá•×|oæö“ó'ç{ó½í×_›dçÎûêW¿úÔSO}à0βÛþT×¶ùîlío§Ó !üÅ_üŃ>8æsà<ðÀˆŸTÁúÚúìüL¦wc-oRml¸íÞ(E…fP(ŒL)Z µìŠB©•“Ú‚“/zeD•™IÙ’yRS“?w=Ÿ˜íܶÐ{maþâÅï=÷Ò…3ï¼ß;t;UXáÖ.Õ1¢,K[èíÏõ² 8ÿòË—WNœ¹ãìbi¦¸™c €snffæíoû¿øÅ»ï¾ûï|§ÝÐÞm~­yO>ùäùóçßóž÷ÌÍ5+SýAu}½ìÏÌ®ÊQ‚– ÛÝŽRL–BN)Z µì†B !'eae&f®ÅÚŠ)óZíí6¼£³;NÌ\[=Q®ßxö… Þûw¿ý~OEc«¥šï¹mÀ—ž:ÿÌ·¿7¿°xÏí'î81sb¶³Cˉ¨×ë=ôÐC—.]züñÇ;ÎÃ?<®¶ï¹¿O<ñÄã?~ï½÷>ôÐCÝn·óêù矿ï¾û ‘õõÁÌœ›éÓµ• À¤l:ÍôQŠiëïÁ̹ZkýŒG:ø]?ÞDáHèî¶ÅÞF¹ žI¿zþ;¯^^yøÜ›ï<½h„½šÃ´ÅòÁw¿å‰g¿uáû˳sóo¾ûö7ݶpÛb¯WìÄSŒ[ÍÏÏ?òÈ#?þø§?ýé .<òÈ#‹‹»aw•+W>ÿùÏ?ùä“wÝu×#<’C$'Ý®!<ÿüó=öدýÚ¯Y=× acPÎÌöKá7DJ&×Ìc}ÓF)¦­¿Q(b–¤ëÌŒ³0/2t⛉f4Õ=å-ô;w2w^¼ÐûîòÊ…ÿÄ™¥Ù»n;qÇ™S ó³RÝ"rõÚõï]¼ô×._\¾.®»xâÌ=·Ÿ¸÷öwš]èïdd˜¹×ë:uê}ï{ßO<ñä“O~ík_{ðÁßüæ7ßyçKKKMëïòòòw¿ûÝo}ë[ßøÆ7TõÁ|×»Þuúôé^¯×¨¦øæ7¿ùñü~áÊ+¼¾¾13×ïõеµ…jÌ&Ò4L¥˜¶þŽ™B©§ýyµà£‘‘sÆÿ‹™{'Ñæ[Ã;šïwŸžóŒ™nñ½×g..¯\¼¾öÝg^.ŸøÆ`}mÒ Ü ßé½Ù¢7Ó›?qfiswš½ãäì|¯Ñ*"¢~¿úôéú¡º÷Þ{Ÿ}öÙ^xáË_þòÊÊÊõë×7}r[eØ×ó»úýþüüüÉ“'ßô¦7;wîìÙ³sss½^¯i3*+ y´~)ƒôz½Âß(`æ@MT†i£ÓÖßq¢N¡€”°Ó^×Ïgئú7¥Ù:@áy¡ßéܶ8Û뜚ï]XœY¾¾qeµÜ¤Y9FÇÑbß/Ívn_š9½Ð?5ßëwvQ ÜX¿ß÷Þ÷z½3gÎ\»víúõëkkkeYÆŒaµÕœj7Ýç°¯Û×^¯g‰äfgg{½ž÷¾iâñÜsÏýþïÿ¾)Œ¸Ÿ &Q][ôgú³3åË«*ÔL##cÚ(Å´õw<ØD¡`5ÇÒÁ ; ;Ÿ¡É¼ ¢¸žöÊ'Ôø‘àÍ9ßõ³§æ{gg–o .¯¬¯B°=ËÚêi/¶®n‡z½þÇèîÄ\wi¦89×ínoEˉ¨( ï}¿ß?qâ„i N=ñþšnpΙžpµ¤ÍÁ³Ï>û‡ø‡YaÄý (Y]t»}çWÉí°4LÓF)¦­¿cÀ6Š(go‡ªˆ»7E˜æP%fVVæÐÀc¯Û¡ð\xîîÔ¼¬,õ×6 4NÕu¼ë<×õ]Ï{Ó”ÀêPu Ñ4ma…qþüùO|âÉy@DÄkëýùÙÙÙÞúêõXI Ù˜6J1mý=ñ 1NPôidcc£Óé0ÓÑQ-ZŒ„§Ÿ~z[…qþüyR(ˆì˜=RƒpÓáïx…ùHã-ZŒ‚­Jb«1}Íì@P;`}mÃÏÌök\(}‹{Æ-)1›-aÛà–l‘ˆ|T DÄÌ`Q&b+»DMÍËÖ¢Ånqÿý÷§È¨›¾ˆMRQrEY6B˜™é^)¦e·¬Å±Ç(ŠØ9_0{¨ŽEàœ§÷þíßU)E‚†JÐ0冔R®‡r]Êu)7ÂÆš„„ ¥J)¡”0€Õ ªPQ•œÖÐvÛ‘’[¨^§Xß]wÑîîCiß^ ß.¦X¬Iù•¦zXºca,MßÚÚÐÚEMýÙî#ÞŠ7Ëñ šúh&T-ñÚP—R ¶ég­åH L€™jrÕƒm·´•ºu‹K‘zZoŠV_Ë_¯eë by€eäÏŽêüÚÎ£Ú Y‰çÍ¡,v+Uk69Û«`ç‰órÎù‚}‡}Qtú®ÓõžïôŠnŸ‹Ž+ övˆ ¥Ó² péµ×>õ¿þ½\-Z5<÷Üs£ç(üÀõë½¹E¾33ÏE·Óí3y©¯ì9ÐÌDÄDLΓ £P@²mf¶}û©jS¼ß¦Åwx­V·ah}™ˆÖiÛw).¨/{¶¶Äom³ð%\}õÏú,ÇçåÏÓ6kjz×v‡Rà¬S *×úüSUS†UÆŸ¢š.dǶ&Òð_}drl¸}FDꋚݞ™Òø¤ )µa˯k÷©=”ø[”Öëj¨ë#—–r$•uz:^]mU¦Ÿ£Í pùÙy¢ú£HÊ©³©Z1‰aÉÕÅèq%vžœcväœóç vöŸ#vÄÎöðˆ™ˆ™+1±–ƒÒ1ÏÏϼï¿üðÚÊõËåÆ0ØrC¤bK÷õ ”E5Þ<® €í Q¿ë Ç«ƒÔÅ­¥Í³h+K»5uKÍÞžén]%¶`ÛŸØ–uÕ¿²õÃùßMâdóWTËA¨Ÿ+MËF^¶û¹<ë¶[ «îLjYf_øN¯èÍvfæ{sK½…Sì ç ç;ogúˆÈ3“J+3;Q1³ƒsP/ÑŒP)!PP"U&v*ÑÔ¨$'·§>L[ž0§?7}rÈ©IÚßú ”%*M̺©ÆM•˜T,>Lë¸Ùniaþÿ$ÛÃëkzÕ…z·j릦·¨ºØƒŠc3ó&;`³…Q{ðöV„”ò‚M¤B4vÏ´¨)tè»Ð!{"޹©Z3ªEªêLlEÕÓ\Þnë¨Ru -aõ¾Õ™ü& ©)§Úèmósñ ;gBA숙]ÁÞGâ bÏìœóì¼ÂTR,úFLÌRöú3s‹‹ƒÐÑyç‹PÂ`*ĶöIý`XU-"[f ƒ4­©Ý”†½ú¢XfEÌ”8†ÝI6­hÕŠD“t-¢B£úlÍÚB$hh&)Lv~Í *õúži)¬Hµ¾kÕz"&S}•¦¿¬½ŽïwÜZ)žØ¯2 CôªR—}U¥³'r5å(köd­æ*j2?­ôjܽzôµµc˜ªÔ—Z›•‚EµlWúlëNÍÐÒ .4|½Z݆ÖúÌÚâ‚[[ÿÀ•ú²q tû¸( ÑHVv ræTÒjò'ªFµ.m6Èj‚j×Ù1jütSÇóZ•{4<ZÛü1Îfe]>k ¨?ÓmœH¶¤›l±)‡ž|MÅ×ÉŸÖE,+!-BÄ­r.ÚÍìÈÎhTQ°/ȹø.;bb’¨sŽ]o°¾ ÇóK'W×Öˇr& ÖT¤"È©Á„4‡(„ÀD’4hf”ù[ÌlOT¶g¬l‘Ž”mô0LZëÊ©F]9• ß0®Né>Hª)ÿƒ ¥¿®êðÐ"Bd{¥¹…"Bäj¶ ¤Vqj ÕžoZ÷E±:üIDAT•Á U "ýNqb¡·¾1ÚqLQÖcŸ™Ù6f ¨ Ò‘Z™GÛì6¤†VS×Þª ¸¡?­°P^LT„,Cx~šièëvdV¢q<‡ÝùÙÀBmçÔ`ÇæÍ!U]ß(ý† ˆf¦‘ÄÆ3»TUS³ëÊ#_¬?Ð4=”ˆAì»=ßé¹NÏuûE·_t{`ÇìET£Ån{àñù3‘D;C™Ø‘8bGγ* B URRb&qªAUˆ›µüTêË}E ’ MÃZ ºAžM5;:Þ†D…7ñ¸4Bõ_®ëŽüÕD©VØšŽ]7}l¨Si~ò û<×&On†ÿ\ºžucMÿg™gÇ6orÓ+Í·™)¦B’uéÍ 3E{2¥CÖÁÖ“«ÚUtÓÏÕ†ìf†Z³º¥˜Ö8›È\÷WÔú[ˆÄd¡yrç>m"ª¨µ<€‘¨‘Y¶¹m¾)vì¼óûÂì ç »ÈÎ[Рù©˜­ê ¹ÎŒh9¿Ôˆ\»|]ECXµ€’ü|ûׇGD ŠòLÊ[•U¦‘ ²u„ɪ–5Nr.y•·3UdUBëÓÆ9—ë‰V3ÞV"ç½-y"‘ä!ê ‰ÕD¤"Ì”ßY­¨é9Iï*ƒ’NJ½!˜X¡™˜ç8ÛÄŠßU€ìUÐa9µ4+¡\[ ³*®(lÁ¯Í°º§œw¾ ç\Ñ1UÄý;"³Ç@détHÊåâ䩳¡ü~)P™Ó²7&Î ›ŠL*1å‘¤Ô Ö,c˜”Rå-L­~1µÐÆÔ÷L¨Q›áDj‹¢öÅ^Û[Ñô·Ò’‰]ûp·H9U‘´o–Â!»S5)¨ª¨Âéw‘ÀÔE’ˆ¢ª™9#Éëàìy9 ÊÐÀÀ'ç;¯^_ŸëG{†™)ùñl5LVo€šˆ«p>y&d•ªØ´ºØ|²,UöÉ8ûUlò;f…±ˆØÍMaTÄŽ)5̾ߊ™ˆ™êz¢“lpÔ„ÌÑ£`çØX/Õ—^‚ •*Åá â˜Ì–cÄlê4tè·âŠKÙ®«(_x ´= ö…+zì 网MkšˆøìŒ LLì j»ßì¼Í 0‡É4·•#e"@$/Ùõ&f—Qu†V‘˜lÅ€\â„ Mn%Š6» jÖù3ñá@_gViゼ!_ß™7×¹jœm›œÎ8­l·®ÙYy4ª_å¼ JñÕûª~aëêŸG¯bŸë–OIª+cà&¤Ÿ5ÞíB¢ä§­îXyTb;«Ö¦öÔfuõœ©Fí•(®›é]¤Uch(âèQ2>DìÏä9;R¸¦ÞlYᪿÙªd´îUó^³cSìœmבYfj8Oì9Z…}RD‹¢ˆÏÚ{ÏÅ@Ë¥…K–—WˆX$¯×ÚFnº"Öñ,Ï[´™&á¼eÀ^DÇȰ·-É`æÊUb6G2#ìFU4DRE¶NYŒ½›S‘æ†Ï5Úò6‰¤ÜyÐ+¿ªØïÖŒ]³e;:…b\Ùu‰S! /‚ Ý9ïæf_]Ù(fމ™‚ªË6w` e\÷³Òµ÷Íb«_ɵî''ºI1g —YŽj³'^"ùIPêNkI6z bçê®û€ó %êt{&Äl1Q9¨– ٢ѤÄlºƒØÙ°S %»ŽCE¤T‹¯U ѰR¥šÙÒºo~ÞÚ“³L[W(HöøPši3ÖT‚‚êu ì+°æ¥OMª‚²ld)JÞÀÊ Ù:ÉjK§=0ÞôVOU6G6¹6kŒú‡Z!V@Ùs^;hTþÅ<Ïp+8—ö­‰2 -ÉF¢Ý--²_¥æ–%2W~9@UÌç™ÕWCÇÁÍMÕ%’ÜH¸’4*¥Õ*M­HÙæjª¦ší/døˆa^&%ç˜8J9GìØwœ÷ì ³6lÏùqŒ³JsOØw¡DE±pòäj ¨‹ks&¶ˆb¶—ˆxËAÇžcW{[CãÂ*jÑ‹`Їª²sr˜V“-¯û̬jn˜…c† ҉żØå»‰ÄîD5ºDDD$”ÙÊI´`Ë Åµš4Iµ¯ómnæe7/ÜeD 0™Ë‹BèôÙÊ¿1äì\ï¶ÅþÅ«eÇu»³žŽz".£@¢&•KT³ºìs¡´ó‘çxg&]Úi°ÖjÚKÈc¾U¹æûÛ•Hó¡QÿמZ x7·BÒè•þÓ¬<@JDÂo Ä…u/%Q|AR„ª* "q:eYñµi#*UÅÒ5îç×vƒT“û!ƒ™Ýì<‹ªm"qw³;”‰á5”À, !ö¤bÄN‚C…ÄAU$ Q‚ª0¶Y¶†—Ñ!ªU"µ!³eÈ–ûì¬îÊɬ6Yuiê$ÂÍ.‚Z3"eÈV¹E)@I«é•>¶ %ÙÒ)¤¡O1Z35Bµ“UGš:4<‡ÐôFEE €zïê[FK%-Ðæ—Ìý5Æš E¾•ª:ÇĦ¢’ÃŒnDkNk¾‹ü[ùOŽ+Zê@³% "dë…ñ{{‚fÑ'’%Ù’p.®nYRš¸i" $&ÊL¡nÑjåh«}æ™™¤!ñ&Kf!“gfrJ rDľ ffÏÞ;ßqÞƒs9#[,¼g8ba¢îê@N,œ8%ÅååkPÍiJ£g#’DgyˆHyÈíŸ{þSU½÷uÞš‡‹˜%Dc"NûÌT‘Nª«j]iåeÚnb¬6¿•?qN„‰Dl«ª#rµyhýñVà³,í IngÓŽ™R\Sš=À'K(·œl·&R@•¼mŸ¸°1X˜+ÞpzáÊZ¸Á\ôËžï¡ %ˆl´ËrÀìäím󺊪OmÈ£êUÍÔè8gÏÈö{¶“ë4JFjê9NHÉ<À'Ö?ãÓ¨ÖÇ? ¿Öd» ånÚ´<ÖÚ Põ®3 rèô;ªö“_,µ‡ë3çÓdÞe6f XÕyŸ[’[BÌŽBÙ9mÇÎL¥Ü~ODš6æˆÙ?*JìUJb;;ˆ=û`«vŽOAÐí2G7TÍÊÛtÝ–Ÿ ¦*a I´,æP1{?û”Ò!z‡`š½…>IWü`µ™a$e«gDµí,c©9Doór B4äã ¼§tF^kf|@€:6i—t%#ª˜:µd‡¸}FIT8>¦èMSsh ¨´)+„™¼wÙgVQ?Õ¼ÝZ­eÉÅaL2+3­ÛUæ°·W3x3ó1ùN–"›æˆºµÚkw0ágfA⌹/ÆÊ³ñ™Ô1r*`d/¬9/Œm1AÈÄÎy09Ç®ÐÖGf˜;£k ±óžœ„(fççn¬ ÊRòÚç]¡9ˆñ0ãÃä*•¹éÜŒ ~Á|q«™™“* !8ßIó'“D!2F_…?E¨«}žØ×ׯü»›4‡ª:_­’q}‰z%m±8çœïÔM»­%!Ï?”ÖYÑz—íÃ`? €ïœXª–3½Î=·/­•reýZÑíÁ³Š¢ðØ&»m†›C©²½ **Þ"’×33I°xr¹Ù•Ñ“»_35£¡ª(@‹2¿b²Q¨²À; H°8 ãvˆfpiÚ³q>!fï*UÄœlµ@\xDZ= G\&åÄÌÆ ò“òއȀ²,³Õ˜–r¾FXò˜ã‚@2^o¤ÅQs¨ª ¤z8YrtV£´©›T[ ë[ÔBqPÕÂ0ã! ‘™,Ž•½¨5·_|¡ÙÇ(D{:M-b3 R(³÷E! fvlÿ9gâh-Ó ‰ªc_\¿±¶´´07?sõêjž¶Î±ˆ0ƒâ¾8YvF‰£¿7Ī⋎ ˆc—Waëò”ˆÌ`—ìf;Št’¢×1¤ð§4ø y!ËV)ÔÙCìÒÑQN:€Ù™•f+š9Mì¶Î»úZ €]eÁäE ÇC^©jz0EŸU„ZÎÝqæ”'¼~cÝ~TÂÞx55i©’ ›Z*Bp)ÔÁåÕ ÓmUãÎQy§ÙCײ‹K6„õ…ˆØ¹²,™¹dµdÊÏÝC¦Y‹´Ë¥µ­œÌY\R[Y~}Q8€²,CP‹hÇ;[Ä¡pD¶ 6oA3•AlåQU_t¢¦ ©¤’Å&X6LUÕÕ"3¬›ù-›H^‰l—ªÞq%@Ê (S¡R‚ÀPÑ@ á©!”Dq_Eµ¶gY¤ “L!Í i1u)ð KNåŽj¡Æ êD‰ñ”ä©D²ñãø8Óù²Iqƒ³Cl“±R ™Kß% ­®q‰Ïò/ŠˆªcX쀱%¢Â|cyZlJ4‘ûbj µéÀ%ÎG€ÀlLM›ªFßT …ú Žc•œ¿ˆª®ð1bÉ$JkJò úÌ®7ó›(ŒÎe÷©˜aÖ(YgS,?¸ÜÂ4n6·Yd÷–âúÀLDÑ,p¬Z‚=”×׵܈CWJÜ} Pvl«ŸÔ4mÍÏ.oÃP-Û ×\èõ§Ÿ—¦ü¯5©î«·uéÿ{Ï* YIEND®B`‚libjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/code/0000755000175000017500000000000011621261107022236 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/code/SimpleChat.java0000644000175000017500000000475311366547366025167 0ustar twernertwernerimport org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.View; import org.jgroups.util.Util; import java.util.List; import java.util.LinkedList; import java.io.BufferedReader; import java.io.InputStreamReader; public class SimpleChat extends ReceiverAdapter { JChannel channel; String user_name=System.getProperty("user.name", "n/a"); final List state=new LinkedList(); public void viewAccepted(View new_view) { System.out.println("** view: " + new_view); } public void receive(Message msg) { String line=msg.getSrc() + ": " + msg.getObject(); System.out.println(line); synchronized(state) { state.add(line); } } public byte[] getState() { synchronized(state) { try { return Util.objectToByteBuffer(state); } catch(Exception e) { e.printStackTrace(); return null; } } } public void setState(byte[] new_state) { try { List list=(List)Util.objectFromByteBuffer(new_state); synchronized(state) { state.clear(); state.addAll(list); } System.out.println("received state (" + list.size() + " messages in chat history):"); for(String str: list) { System.out.println(str); } } catch(Exception e) { e.printStackTrace(); } } private void start() throws Exception { channel=new JChannel(); channel.setReceiver(this); channel.connect("ChatCluster"); channel.getState(null, 10000); eventLoop(); channel.close(); } private void eventLoop() { BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); while(true) { try { System.out.print("> "); System.out.flush(); String line=in.readLine().toLowerCase(); if(line.startsWith("quit") || line.startsWith("exit")) { break; } line="[" + user_name + "] " + line; Message msg=new Message(null, null, line); channel.send(msg); } catch(Exception e) { } } } public static void main(String[] args) throws Exception { new SimpleChat().start(); } } libjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/master.xml0000644000175000017500000000604711366547366023374 0ustar twernertwerner ]> JGroups tutorial $Date: 2007/07/18 14:26:26 $ $Id: master.xml,v 1.4 2007/07/18 14:26:26 belaban Exp $ Bela Ban JGroups Project

belaban@yahoo.com
1998-2006 Bela Ban 2006-2007 Red Hat Inc This document is copyrighted. Copies are allowed for personal use. Redistribution only with written permission of the author(s). About the tutorial This is a short tutorial on how to install JGroups and write a simple application. The goal is to show how to configure JGroups and how to write a simple application showing the major methods of the API. Bela Ban, Kreuzlingen Switzerland August 2007 &installation; &sampleapp; The Ensemble Distributed Communication System , CS Dept Cornell University , 1997 . . Erich Gamma , Richard Helm , Ralph Johnson , and John Vlissides . Design Patterns: Elements of Reusable Object-Oriented Software . Addison-Wesley , 1995 . libjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/modules/0000755000175000017500000000000011621261107022774 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/modules/installation.xml0000644000175000017500000002263011366547366026246 0ustar twernertwerner Installation
Download JGroups can be downloaded here. For this tutorial, I'm using the binary version of JGroups 2.5, so the ZIP file to download is JGroups-2.5.0.bin.zip. Note that JGroups 2.5 requires JDK 1.5 or higher. Unzip JGroups-2.5.0.bin.zip into a directory JGroups-2.5.0.bin. The contents of the directory are
Screenshot of the JGroups binary distribution
The most important files are: jgroups.bat and jgroups.sh: scripts to run a JGroups application (including the correct JARs and XML files) INSTALL.html: detailed configuration instructions plus trouble shooting commons-logging.jar: required JAR that provides general logging. This might get dropped in 3.0 jgroups-all.jar (required): JGroups functionality, including demo and junit apps. If a smaller JAR is required, this can be done by downloading the source distribution and invoking the "jar" target, which creates a jgroups-core.jar file (ca 1MB). log4j.jar (optional): commons-logging can also use JDK logging Various XML file: different JGroups configurations, e.g. mping.xml: TCP based stack with dynamic discovery sfc.xml: UDP (using IP multicasting) based stack with simple flow control tcp-nio.xml: TCP based stack with fixed configuration (list of nodes) using NIO (thread pool for all TCP connections) tcp-nio.xml: TCP based stack with fixed configuration (list of nodes) using plain TCP (1 thread / TCP connection) tcpgossip.xml: tunnel based configuration which routes messages to a remote GossipRouter, used to tunnel firewalls udp.xml: default IP multicast based configuration config.txt: configuration file for performance tests
Configuration Add jgroups-all.jar and commons-logging.jar to your CLASSPATH. If you use the log4j logging system, you also have to add log4j.jar (this is not necessary if you use the JDK logging system). As an alternative, you can also use jgroups.bat. Note that jgroups.sh requires work, as it uses backslashes (developed under Cygwin/Windows), so it is currently not usable under UNIX as is.
Testing your Setup To see whether your system can find the JGroups classes, execute the following command: java org.jgroups.Version or java -jar jgroups-all.jar You should see the following output (more or less) if the class is found: $ java -jar jgroups-all.jar Version: 2.5.0 CVS: $Id: installation.xml,v 1.3 2007/07/16 11:04:12 belaban Exp $ History: (see doc/history.txt for details)
Running a Demo Program To test whether JGroups works okay on your machine, run the following command twice: java org.jgroups.demos.Draw 2 whiteboard windows should appear as shown in .
Screenshot of 2 Draw instances
If you started them simultaneously, they could initially show a membership of 1 in their title bars. After some time, both windows should show 2. This means that the two instances found each other and formed a group.
When drawing in one window, the second instance should also be updated. As the default group transport uses IP multicast, make sure that - if you want start the 2 instances in different subnets - IP multicast is enabled. If this is not the case, the 2 instances won't 'find' each other and the sample won't work. If the 2 instances find each other and form a cluster, you can skip ahead to the next chapter ("Writing a simple application").
Using JGroups without a network (You may skip this section if the 2 instances found each other correctly in the previous section). Sometimes there isn't a network connection (e.g. DSL modem is down), or we want to multicast only on the local machine. To do this, we can use the loopback device (127.0.0.1): java -Djgroups.bind_addr=127.0.0.1 org.jgroups.demos.Draw You should again see 2 instances of Draw which form a cluster. If this is not the case, you may have to add a multicast route to the loopback device (this requires superuser or admin privileges): route add -net 224.0.0.0 netmask 240.0.0.0 dev lo This means that all traffic directed to the 224.0.0.0 network will be sent to the loopback interface, which means it doesn't need any network to be running. Typical home networks have a gateway/firewall with 2 NICs: the first (eth0) is connected to the outside world (Internet Service Provider), the second (eth1) to the internal network, with the gateway firewalling/masquerading traffic between the internal and external networks. If no route for multicast traffic is added, the default will be to use the fdefault gateway, which will typically direct the multicast traffic towards the ISP. To prevent this (e.g. ISP drops multicast traffic, or latency is too high), we recommend to add a route for multicast traffic which goes to the internal network (e.g. eth1).
Trouble shooting If the 2 Draw instances don't find each other, read INSTALL.html, which comes with JGroups and has more detailed trouble shooting information. In a nutshell, there are multiple possible reasons the cluster doesn't form: A firewall discards packets. To verify this, turn the firewall off. If the cluster forms, then turn the firewall back on and selectively add rules to let JGroups traffic pass. Use of IPv6. JGroups does work with IPv6, but some JDK implementations still have issues with it, so you can turn IPv6 off by passing the "-Djava.net.preferIPv4Stack=true" system property to the JVM. You don't use the right network interface (NIC): define the NIC with the jgroups.bind_addr system property: java -Djgroups.bind_addr=192.168.5.2 java.org.jgroups.demos.Draw There is no multicast route for the chosen NIC.
libjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/en/modules/sampleapp.xml0000644000175000017500000004571311366547366025536 0ustar twernertwerner Writing a simple application The goal of this chapter is to write a simple text-based chat application (SimpleChat), with the following features: All instances of SimpleChat find each other and form a cluster. There is no need to run a central chat server to which instances have to connect. Therefore, there is no single point of failure. A message is sent to all instances of the cluster. An instance gets a notification callback when another instance leaves (or crashes) and when other instances join. (Optional) We maintain a common cluster-wide shared state, e.g. the chat history. New instances acquire that history from existing instances.
JGroups overview JGroups uses a JChannel as the main API to connect to a cluster, send and receive messages, and to register listeners that are called when things (such as member joins) happen. What is sent around are Messages, which contain a byte buffer (the payload), plus the sender's and receiver's address. Addresses are subclasses of org.jgroups.Address, and usually contain an IP address plus a port. The list of instances in a cluster is called a view (org.jgroups.View), and every instance contains exactly the same View. The list of the addresses of all instances can get retrieved by calling View.getMembers(). Instances can only send or receive messages when they've joined a cluster. When an instance wants to leave the cluster, methods JChannel.disconnect() or JChannel.close() can be called. The latter actually calls disconnect() if the channel is still connected before closing the channel.
Creating a channel and joining a cluster To join a cluster, we'll use a JChannel. An instance of JChannel is created with a configuration (e.g. an XML file) which defines the properties of the channel. To actually connect to the cluster, the connect(String name) method is used. All channel instances which call connect() with the same argument will join the same cluster. So, let's actually create a JChannel and connect to a cluster called "ChatCluster": import org.jgroups.JChannel; public class SimpleChat { JChannel channel; String user_name=System.getProperty("user.name", "n/a"); private void start() throws Exception { channel=new JChannel(); channel.connect("ChatCluster"); } public static void main(String[] args) throws Exception { new SimpleChat().start(); } } First we create a channel using the empty contructor. This configures the channel with the default properties. Alternatively, we could pass an XML file to configure the channel, e.g. new JChannel("/home/bela/udp.xml"). The connect() method joins cluster "ChatCluster". Note that we don't need to explicitly create a cluster beforehand; connect() creates the cluster if it is the first instance. All instances which join the same cluster will be in the same cluster (of course!), for example if we have ch1 joining "cluster-one" ch2 joining "cluster-two" ch3 joining "cluster-two" ch4 joining "cluster-one" ch5 joining "cluster-three" , then we will have 3 clusters: "cluster-one" with instances ch1 and ch4, "cluster-two" with ch2 and ch3, and "cluster-three" with only ch5.
The main event loop and sending chat messages We now run an event loop, which reads input from stdin ('a message') and sends it to all instances currently in the cluster. When "exit" or "quit" quit are entered, we fall out of the loop and close the channel. private void start() throws Exception { channel=new JChannel(); channel.connect("ChatCluster"); eventLoop(); channel.close(); } private void eventLoop() { BufferedReader in=new BufferedReader(new InputStreamReader(System.in)); while(true) { try { System.out.print("> "); System.out.flush(); String line=in.readLine().toLowerCase(); if(line.startsWith("quit") || line.startsWith("exit")) { break; } line="[" + user_name + "] " + line; Message msg=new Message(null, null, line); channel.send(msg); } catch(Exception e) { } } } We added the call to eventLoop() and the closing of the channel to the start() method, and we provided an implementation of eventLoop. The event loop blocks until a new line is ready (from standard input), then sends a message to the cluster. This is done by creating a new Message and calling Channel.send() with it as argument. The first argument of the Message constructor is the destination address. A null destination address means send the message to everyone in the cluster (a non-null address of an instance would send a message from us to only 1 instance). The second argument is our own address. This is null as well, as the stack will insert the correct address anyway. The third argument is the line that we read from stdin, this uses Java serialization to create a byte[] buffer and set the message's payload to it. Note that we could also serialize the object ourselves (which is actually the recommended way !) and use the Message contructor which takes a byte[] buffer as third argument. The application is now fully functional, except that we don't yet receive messages or view notifications. This is done in the next section below.
Receiving messages and view change notifications Let's now register as a Receiver to receive message and view changes. To this end, we could implement org.jgroups.Receiver (with 6 methods), however, I chose to extend ReceiverAdapter which has default implementations, and only override callbacks (receive() and viewChange()) we're interested in. We now need to extend ReceiverAdapter: public class SimpleChat extends ReceiverAdapter { , set the receiver in start(): private void start() throws Exception { channel=new JChannel(); channel.setReceiver(this); channel.connect("ChatCluster"); eventLoop(); channel.close(); } , and implement receive() and viewAccepted(): public void viewAccepted(View new_view) { System.out.println("** view: " + new_view); } public void receive(Message msg) { System.out.println(msg.getSrc() + ": " + msg.getObject()); } The viewAccepted() callback is called whenever a new instance joins the cluster, or an existing instance leaves (crashes included). Its toString() method prints out the view ID (an increasing ID) and a list of the current instances in the cluster In receive(), we get a Message as argument. We simply get its buffer as an object (again using Java serialization) and print it to stdout. We also print the sender's address (Message.getSrc()). Note that we could also get the byte[] buffer (the payload) by calling Message.getBuffer() and then de-serializing it ourselves, e.g. String line=new String(msg.getBuffer()).
Trying out the SimpleChat application Now that the demo chat application is fully functional, let's try it out. Start an instances of SimpleChat: [mac] /Users/bela$ java SimpleChat ------------------------------------------------------- GMS: address is 192.168.0.6:49963 ------------------------------------------------------- ** view: [192.168.0.6:49963|0] [192.168.0.6:49963] > The address of this instance is 192.168.0.6:49963 (IP address:port). It is the only instance so far. So let's start the second instance and type something: [mac] /Users/bela$ java SimpleChat ------------------------------------------------------- GMS: address is 192.168.0.6:49964 ------------------------------------------------------- ** view: [192.168.0.6:49963|1] [192.168.0.6:49963, 192.168.0.6:49964] > The cluster list is now [192.168.0.6:49963, 192.168.0.6:49964], showing the first and second instance that joined the cluster. Note that the first instance (192.168.0.6:49963) also received the same view, so both instances have the exact same view with the same ordering of its instances in the list. The instances are listed in order of joining the cluster, with the oldest instance as first element. Sending messages is now as simple as typing a message after the prompt and pressing return. The message will be sent to the cluster and therefore it will be received by both instances, including the sender. If the word "exit" or "quit" is entered, then the instance will leave the cluster gracefully. This means, a new view will be installed immediately. To simulate a crash, simply kill an instance (e.g. via CTRL-C, or from the process manager). The other surviving instance will receive a new view, with only 1 instance (itself) and excluding the crashed instance.
Extra credits: maintaining shared cluster state One of the uses of JGroups is for maintaining state that is replicated across a cluster. For example, state could be all the HTTP sessions in a web server. If those sessions are replicated across a cluster, then clients can access any server in the cluster after a server which hosted the client's session crashed, and the user sessions will still be available. Any update to a session is replicated across the cluster, e.g. by serializing the attribute that was modified and sending the modification to every server in the cluster via JChannel.send(). This is needed so that all servers have the same state. However, what happens when a new server is started ? That server has to somehow get the existing state (e.g. all HTTP sessions) from an existing server in the cluster. This is called state transfer. State transfer in JGroups is done by implementing 2 (getState() and setState()) callbacks and calling the JChannel.getState() method. method. Note that, in order to be able to use state transfer in an application, the protocol stack has to have a state transfer protocol (the default stack used by the demo app does). The start() method is now modified to include the call to JChannel.getState(): private void start() throws Exception { channel=new JChannel(); channel.setReceiver(this); channel.connect("ChatCluster"); channel.getState(null, 10000); eventLoop(); channel.close(); } The getState() method actually returns a boolean, which is false for the first instance in a cluster, and should be true for subsequent instances. The Receiver interface defines a callback getState() which is called on an existing instance to fetch the cluster state. In our demo application, we define the state to be the chat conversation. This is a simple list, to the tail of which we add every message we receive. (Note that this is probably not the best example for state, as this state always grows. As a workaround, we could have a bounded list, which is not done here though). The list is defined as an instance variable: final List<String> state=new LinkedList<String>(); The getState() callback implementation is public byte[] getState() { synchronized(state) { try { return Util.objectToByteBuffer(state); } catch(Exception e) { e.printStackTrace(); return null; } } } The getState() method is called in the state provider, ie. an existing instance, to return the shared cluster state. Since access to state may be concurrent, we synchronize it. Then we call Util.objectToByteBuffer() which is a JGroups utility method using simple serialization to generate a byte buffer from an object. The setState() method is called on the state requester, ie. the instance which called JChannel.getState(). Its task is to deserialize the byte buffer and set its state accordingly: public void setState(byte[] new_state) { try { List<String> list=(List<String>)Util.objectFromByteBuffer(new_state); synchronized(state) { state.clear(); state.addAll(list); } System.out.println("received state (" + list.size() + " messages in chat history):"); for(String str: list) { System.out.println(str); } } catch(Exception e) { e.printStackTrace(); } } We again call a JGroups utility method (Util.objectFromByteBuffer()) to create an object from a byte buffer (using Java serialization). Then we synchronize on state, and set its contents from the received state. We also print the number of messages in the received chat history to stdout. Note that this is not feasible with a large chat history, but - again - we could have a bounded chat history list.
Conclusion In this tutorial, we showed how to create a channel, join and leave a cluster, send and receive messages, get notified of view changes and implement state transfer. This is the core functionality provided by JGroups through the JChannel and Receiver APIs. JGroups has two more areas that weren't covered: building blocks and the protocol stack. Building blocks are classes residing on top of a JChannel and provide a higher abstraction level, e.g. request-response correlators, cluster-wide method calls, replicated hashmaps and so forth. The protocol stack allows for complete customization of JGroups: protocols can be configured, removed, replaced, enhanced, or new protocols can be written and added to the stack. We'll cover the protocol stack and available protocols in a later article. The code for SimpleChat can be found here. Here are some links for further information about JGroups: SimpleChat code: SimpleChat.java JGroups web site: http://www.jgroups.org Downloads: here JIRA bug tracking: http://jira.jboss.com/jira/browse/JGRP Mailing lists: http://sourceforge.net/mail/?group_id=6081
libjgroups2.6-java-2.6.15.GA.orig/doc/tutorial/build.xml0000644000175000017500000000162111366547366022567 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/doc/persistence.html0000644000175000017500000002256711366547366022331 0ustar twernertwerner Persistence for Hashtables

Persistence

Using Persistence

  • The package is meant for users who need to maintain the state of their Serializable name-value pair sets for better fault tolerance. Using this package will improve usability but will affect performance depending on the usage of the persist API.
  • The current package will support only 2 known implementations, the DB storage way (which is vendor independant) and an ad-hoc (or some open source) implementation.
  • The Persistence package assumes  that the user will provide appropriate properties to initialize and use the storage mechanism.

Code Example

Code examples make it easy to comprehend the package.


import org.jgroups.persistence.*;

// getting default factor
PersistenceFactory factory = PersistenceFactory();
 
//get reference to PersistenceManager (handle Exception as required by application)
// if user defines own properties
 try
{
    String filepath = "/home/user/userdefined.properties"
    PersistenceManager manager = factory.createPersistenceManager(filepath);
   
    //if user used existing properties by filling the values (default properties)
    // PersistenceManager manager = factory.createPersistenceManager();
}catch (Exception e)
{
    //handle Exception
}


//once the persistencemanager reference is obtained, use available
// to save a NV
HashMap map = new HashMap();
map.put("bela", "bela");
map.put("mandar", "mandar");
try
{
    manager.saveAll(map);                      // will store all values from map into storage
}catch (CannotPersistException cpe)
{
    //handle exception
}


//to update a value;
try
{
    map.put("bela", "geek");
    manager.save("bela", "geek");              // will update { {bela,bela}, {mandar,mandar})
                                               // to          { {bela,geek}, {mandar,mandar})
}catch (CannotPersistException cpe)
{
    //handle exception
}


// to remove an entry
try
{
    map.remove("mandar");
    String value = (String) manager.remove("mandar");// will update {{bela,geek}, {mandar, mandar}}
    //use value if required                           // to          {bela,geek}
}catch (CannotRemoveException cre)
{
    // handle exception
}

//**************************************************************************

// say, at start of application , need to get back stored state
//use above example to get back reference to "manager"
try
{
    HashMap map = (HashMap) manager.retrieveAll();    // map will get {{bela,geek}}
}catch (CannotRetrieveException cree)
{
    // handle exception
}

Entering Properties (persist.properties)

Currently, the properties reflect only the DB related, as the FILE based implementation is not in place.
 
Name Description Typical Value
persist Used to decide between DB or FILE. DB/FILE (No default assumed)
jdbc.table Lets the user dictate the schema used to storage purposes on the database. create table replhashmap(key varchar(100), keybin blob, valbin blob) 
jdbc.Driver Classname for driver that needs to be loaded for DB storage. oracle.jdbc.driver.OracleDriver
jdbc.Conn Connection string required for user to connect to Db instance jdbc:oracle:oci8:@instance
jdbc.User User name required to connect to provided DB instance user
jdbc.Pass Password wrt to User provided pass



Using org.jgroups.blocks.DistributedHashtable with PersistenceManager

// Example of how to setup and use DistributedHashtable
  • Create persist.properties (classpath or home dir)
  • Start one instance with -persist
  • Start another instance with -persist
  • Add a couple of values
  • Delete both instances
  • Start first instance again: previous values should be available





libjgroups2.6-java-2.6.15.GA.orig/doc/structure.txt0000644000175000017500000000276111366547366021712 0ustar twernertwerner Outline of the new structure of JGroups ------------------------------------------ / Readme files, credits, quick installation /bin Executable files, e.g. scripts to run a demo /build ANT-based build system (contains build.xml). Produces files in /dist /classes Classes generates by the build process (either ANT or makefiles) /conf JGroups sample configuration files, e.g. JGroups.properties /dist JAR files generated by the build process (e.g. JGroups.jar), documentation (e.g. UsersGuide, ProgrammersGuide and Javadoc) /doc/ Documentation images Images used by both UG and PG javadoc Generated Javadoc documentation. [Move to dist ?] progguide Programmers Guide (in DocBook format) usersguide Users Guide (in DocBook format). Common files (e.g. style sheets) are stored in a directory directly under /doc (similar to images) /lib 3rd party libraries (e.g. JUnit, JAXP) /src/ The source code for JGroups jgroups/ Top level public files, e.g. Channel, Message etc blocks Building blocks debug Debugging functionality demos All the demos jmx/ The JMX instrumentation code protocols/ The main protocol branch (including virtual synchrony) pbcast The PBCAST protocol branch stack Functionality related to the protocol stack tests JUnit unit (and other) tests util Utility classes libjgroups2.6-java-2.6.15.GA.orig/doc/NullingSrcAddresses.txt0000644000175000017500000000175611366547366023573 0ustar twernertwerner Loopback adaptor issues on Windows ---------------------------------- JIRA: http://jira.jboss.com/jira/browse/JGRP-79 Version: $Id: NullingSrcAddresses.txt,v 1.1 2005/05/19 07:57:46 belaban Exp $ On Windows, when a loopback adaptor is created, we can associate multiple (virtual) IP addresses with it, e.g. 10.0.0.1 and 10.0.0.2. However, when we have a member M1 bound to 10.0.0.1, and another member M2 bound to 10.0.0.2, and bind_to_all_interfaces is set to true, then it was observed that - regardless of the bind address - the sender's address in a DatagramPacket received was always 10.0.0.1 (the first address assigned) ! Therefore, members would never find each other. The reason this shows up now (in 2.2.8) is that as an optimization, we *don't* send the src address in the Message anymore, so we can save a few bytes, but we null the src address, and set it to the sender's address when we *receive* the packet. This can be disabled by setting null_src_addresses to false (default is true)libjgroups2.6-java-2.6.15.GA.orig/doc/PrimaryPartition.txt0000644000175000017500000000600311366547366023160 0ustar twernertwerner Design of the PRIMARY_PARTITION protocol ======================================== Author: Bela Ban Version: $Id: PrimaryPartition.txt,v 1.1 2005/07/21 20:33:01 belaban Exp $ Dawid Kurzyniec wrote: > Bela Ban wrote: > >> I think adding a protocol on top of (or below ?) GMS will work. However, there is the question of how you actually determine the primary and secondary partitions ? >> For example, if we have a switch crash, and all 5 members in the group become singleton groups, then the switch is turned back on, which one >> is the primary partition ? There is no majority. Of course, you simply need to take a deterministic decision, e.g. in this case do a lexical sort and take >> the first (A). Is this what you are thinking of doing ? So B, C, D and E would get an EXIT event, would have to leave and possibly re-join ? This >> would be simple to implement. > > > Yes, this is pretty much what we have in mind. We intend in a conflicting case to find the greatest address of all members from candidate groups, then pick as a surviving group the one where this greatest guy belongs to (or should we take the smallest one? It would be nice to bias towards the group containing the current coordinator; does the coordinator has the smallest or the largest address in its group?) It is a lexical sort, e.g. Address extends Comparable, so we sort and take the first member of the resulting merged group as the coordinator > In fact we figured we don't even need a protocol - handling this in MembershipListener should do the job, I guess?... (We are lazy and we want to deal with JGroups at the highest level possible). I think it should be a protocols, PRIMARY_PARTITION, and can be implemented as follows: * Place it somewhere below GMS, but above MERGE2, It probably needs lossless delivery, so it should be ablove UNICAST and NAKACK as well * Handle the MERGE event on the up() method: o Get the subgroups, e.g. {A,B}, {C}, {D,E} and {F} o Consult a *merge policy*, which determines (given the list of subgroups), the primary partition o If we are the coordinator of the primary partition: + Send an exit message to all other coordinators (hmm, you probably can't do that as you are not a member of the subgroups, so probably we have to handle VIEW(MergeView) rather than the MERGE event + The other coordinators forward the EXIT event to everyone else in their group, so all members leave (and possibly rejoin) the group o Else + Send the EXIT event to everyone in my group + Everyone shuts down and possibly rejoins later The MergePolicy implementation needs to be configurable, so devs can specify their own implementation. We would supply a default impl if not specified. Looks relatively straightforward. The only thing I don't really like is that we have to merge *first* before we send the EXIT message to members of the *previous* subgroups. However, this is probably necessary as we cannot send messages to members *not* in our grouplibjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.4.txt0000644000175000017500000000507111366547366022705 0ustar twernertwerner Release Notes JGroups 2.6.4 =========================== Version: $Id: ReleaseNotes-2.6.4.txt,v 1.1.2.2 2008/09/17 07:39:22 belaban Exp $ Author: Bela Ban JGroups 2.6.4 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with 2.6, 2.6.1, 2.6.2 and 2.6.3. Below is a summary (with links to the detailed description) of the major new features between 2.6.3 and 2.6.4. Performance enhancement: HashMaps in Messages replaced by more compact structures --------------------------------------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-806] This speeds up creation of messages and access to a message's headers. It also reduces memory needed per message. Preliminary measurements showed a 10-20% performance gain in the JGroups perf test. Define max blocking times in FC ------------------------------- [https://jira.jboss.org/jira/browse/JGRP-804] New property max_block_times, which defines the max number of milliseconds to blocks for a given message M, according to M's size. Example: max_block_times="50:2,500:5,1500:10,10000:20,100000:500" This means that messages smaller than or equal to 50 bytes will block for 2 ms max (or not block at all if enough credits are available, of course). Messages between 51 and 500 bytes will block a max time of 5 ms, and so on. All message larger than 100'000 bytes will block for a max time of 500 ms. Documentation: http://www.jgroups.org/javagroupsnew/docs/manual/html/protlist.html#d0e3803 Manual ------ The manual is online at http://www.jgroups.org/javagroupsnew/docs/manual/html/index.html Bug fixes --------- FRAG/FRAG2: fragments from X are not cleared if X crashes --------------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-800] Critical fix to prevent memory leaks when X crashes while it still hasn't sent all fragments for a large message. MethodCall: match between formal parameters and actual arguments fails if an argument is a subtype of a parameter ----------------------------------------------------------------------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-817] Message.setFlag is using addition instead of bit-wise or -------------------------------------------------------- [https://jira.jboss.org/jira/browse/JGRP-810] The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Sept 2008 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.6.13.txt0000644000175000017500000000272411366547366022767 0ustar twernertwerner Release Notes JGroups 2.6.13 ============================ Author: Bela Ban Version: $Id: ReleaseNotes-2.6.13.txt,v 1.1.2.1 2009/09/30 14:34:15 belaban Exp $ JGroups 2.6.13 is still API-backwards compatible with previous versions (down to 2.2.7). It is also binary backwards compatible with all previous patch versions 2.6.X. The major work done in 2.6.13 was performance related: JGRP-1033 and JGRP-1034 back ported UNICAST and NAKACK from CVS head to 2.6.x. JGRP-1043 fixed a performance bottleneck in 2.6.x. A more detailed discussion is at http://belaban.blogspot.com/2009/09/jgroups-2613cr2-released.html. Below is a summary (with links to the detailed description) of the major new features between 2.6.12 and 2.6.13. Release Notes - JGroups - Version 2.6.13 ** Bug * [JGRP-1060] - NAKACK has inconsistent internal state after concurrent node startup * [JGRP-1062] - TimeScheduler: if a periodic task throws an exception, task is not run again * [JGRP-1064] - Serialization untidiness in AuthToken ** Task * [JGRP-1033] - UNICAST: backport from head * [JGRP-1034] - NAKACK: backport from head * [JGRP-1043] - UNICAST: high contention Manual ------ The manual is online at http://www.jgroups.org/manual/html/index.html The complete list of features and bug fixes can be found at http://jira.jboss.com/jira/browse/JGRP. Bela Ban, Kreuzlingen, Switzerland Vladimir Blagojevic, Toronto, Canada Richard Achmatowicz, Montreal, Canada Sept 2009 libjgroups2.6-java-2.6.15.GA.orig/doc/ReleaseNotes-2.2.8.txt0000644000175000017500000000617611366547366022714 0ustar twernertwerner Release Notes JGroups 2.2.8 =========================== Version: $Id: ReleaseNotes-2.2.8.txt,v 1.2 2007/08/20 11:15:39 belaban Exp $ Author: Bela Ban Fast Message marshalling ------------------------ - Replaced Externalizable for Message with Streamable, resulting in much faster marshalling and reduced size of marshalled messages, allowing for more messages to be sent / second - org.jgroups.tests.MessageSerializationTest2 can be used to compare Externalizable with Streamable: - For 50000 messages, size reduction is almost 50%, marshalling 150% faster and unmarshalling 650% faster flags="-Xmx500M -Xms500M -XX:NewRatio=1 -XX:+AggressiveHeap -verbose:gc -XX:+DisableExplicitGC -XX:ThreadStackSize=32 -XX:CompileThreshold=100" java $flags org.jgroups.tests.MessageSerializationTest2 -num 100000 -add_headers false serialized size=8588935, streamable size=5788899, streamable is 48 percent smaller serialized write=831, streamable write=331, streamable write is 151 percent faster serialized read=1352, streamable read=180, streamable read is 651 percent faster Performance numbers ------------------- - Real tests will be produced in 2.2.9, using org.jgroups.tests.perf.Test - With JGroups/conf/fc-fast-minimalthreads.xml, I got ca 5000 1K messages on my laptop: (2 members, 1 sender, 1 receiver, 1 CPU laptop, 20000 1K msgs, 100Mbps switch) - April 20 2005: **5006** msgs/sec (on 192.168.5.1): -Xmx500M -Xms500M -XX:NewRatio=1 -XX:+AggressiveHeap -verbose:gc -XX:+DisableExplicitGC -XX:ThreadStackSize=32 -XX:CompileThreshold=100: combined: num_msgs_expected=20000, num_msgs_received=20000 (loss rate=0%), received=20MB, time=3995ms, msgs/sec=5006.26, throughput=5.01MB/sec UDP ---------- - bind_to_all_interfaces="true" now allows to listen for multicast messages on *all* available interfaces. This requires 1.4, under 1.3 the default interface will be selected MPING ----- - MPING allows for a combination where node discovery in a cluster uses multicast, but the real transport uses TCP. This is an addition to TCP:TCPPING and TCP:TCPGOSSIP. Example (short version of JGroups/conf/mping.xml): Concurrent startup ------------------ - When multiple members are started simultaneously, and no other member is running yet, they form singleton groups, and merge after some time. The new version avoids this merge, so merging occurs only after network partitions now, never on concurrent startup of initial members libjgroups2.6-java-2.6.15.GA.orig/doc/ENCRYPT1_4.html0000644000175000017500000001504311366547366021424 0ustar twernertwerner

Encryption Protocol Document

(Author : Mandar Shinde)

Introduction

This document is required to be read by both the developers and the users. The
ENCRYPT protocol will not work by directly compiling the javagroups source
and then using the required "encrypt.xml" file. Since an external provider needs
to be used (JDK1.4 does not provider for RSA as of now), hence this provider
needs to be added to the users/developers security list.
 

Installation

The following steps need to be followed to make sure that ENCRYPT protocol
can ne used
 
  • Make sure that JDK1.4 is installed, ENCRYPT will not work with any other
    version under JDK1.4.
  • After installation JDK1.4, set $JAVA_HOME to the installation root.
  • cd $JAVA_HOME/jre/lib/security. Open java.security file.
  • Search for security.provider.{list} in the opened file.
  • At the end of the list add Provider.{list+1}=org.bouncycastle.jce.provider.BouncyCastleProvider.
  • The above provider is the default provider that comes along with Javagroups; 
    users can use their own providers by adding the right provider jars in the classpath
    and adding the provider to the required list.
  • The encrypt.xml file is the default configuration on top of default.xml. Any stack can use the 
    Encrypt protocol by adding this stack below GMS.
    <protocol>

  •                         <protocol-name> Encryption Protocol </protocol-name>
                            <description> Protocol provides encryption to all communication </description>
                            <class-name>org.jgroups.protocols.ENCRYPT1_4</class-name>
                            <protocol-params>
                                   <protocol-param name="asymInit" value="512"/>
                                   <protocol-param name="symInit" value="56"/>
                                   <protocol-param name="asymAlgorithm" value="RSA"/>
                                   <protocol-param name="symAlgorithm" value="DES/ECB/PKCS5Padding"/>
                            </protocol-params>
    </protocol>

Demo

To test the usage of the protocol use any demo; I have used the Draw demo for this purpose;.
  • First up uncomment the Print protocol and comment the Encrypt protocol from the 
    encrypt.xml file. When the Draw demo is used, the user will see the deserialized
    messages being exchanged between the 2 members.
  • Now, uncomment the Encrypt protocol (leave the Print Protocol intact)  from the protocol 
    stack. Run the same demo as above; the user will see that an ioexception will thrown while 
    deserializing the message because it has been encrypted by the Encrypt protocol.

How does Encrypt protocol work ?

The mechanism used by this protocol is the oldest know way for high performing peer communication
protocols.  To understand the working a couple algos need to be detailed; these are very basic in
today's encryption world.
Asymmetric algo: this algo uses a set of keys; a public-key and a private-key. The public-key is
public; it is published to the public. The private-key is known only to the entity which makes the
public-key available. Any other entity will encrypt a message using the former  public-key and the
former will  decrypt  the message using the private-key.
Symmetric algo: this algo is the simplest known where a set of communication peers know a single
shared secret-key(private-key) and ecrypt/decrypt messages to communicate with each other.
Final algo : It is obvious that the asym algo seems more powerful than the symm algo. But, the asym
algorithm is very expensive and the symm algorithm has a basic fault on how to distribute the key. A
combination where the asym algo is used only for handshake and distribute the shared key is the best bet.
Only when a member leaves does it require to re-generate a shared key.
 

Step-by-Step

  1. The first-member in the group becomes the admin and generates the shared-key.
  2. When a new peer requests to join, the new-member publishes its public-key.
  3. Using the public-key the admin encodes its shared-key.
  4. Using its own private-key, client decodes the shared-key.
  5. New member lets the admin know, it is ready.
  6. Members use shared-key to encrypt/decrypt messages.
  7. When member leaves, admin regenerates shared-key. If admin leaves, another member becomes admin 
    and regenerates key.
     
     
     
     
libjgroups2.6-java-2.6.15.GA.orig/src/0000755000175000017500000000000011621261110017073 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/0000755000175000017500000000000011621261110017662 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/0000755000175000017500000000000011621261110021353 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/0000755000175000017500000000000011621261110023677 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/PersistenceManager.java0000644000175000017500000000310311366547366030350 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This interface defines the interface that needs to be implemented to * persist any Map(Serializable) object. Primary usage would be users who * need to store the state of a given NV for fault tolerance. */ import java.io.Serializable; import java.util.Map; public interface PersistenceManager { /** * Save new NV pair as serializable objects or if already exist; store * new state * @param key * @param val * @exception CannotPersistException; */ void save(Serializable key, Serializable val) throws CannotPersistException; /** * Remove existing NV from being persisted * @param key value * @return Serializable; gives back the value * @exception CannotRemoveException; */ Serializable remove(Serializable key) throws CannotRemoveException; /** * Use to store a complete map into persistent state * @param map * @exception CannotPersistException; */ void saveAll(Map map) throws CannotPersistException; /** * Gives back the Map in last known state * @return Map; * @exception CannotRetrieveException; */ Map retrieveAll() throws CannotRetrieveException; /** * Clears the complete NV state from the DB * @exception CannotRemoveException; */ void clear() throws CannotRemoveException; /** * Used to handle shutdown call the PersistenceManager implementation. * Persistent engines can leave this implementation empty. */ void shutDown(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/package.html0000644000175000017500000000014211366547366026207 0ustar twernertwerner Provides features for storing information to a database or file. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/CannotCreateSchemaException.java0000644000175000017500000000176411366547366032152 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This exception inherits the Exception class and is used in * cases where the Persistence Storage cannot create schema to * use the API as required. At this point, top level use needs to * decide whether to continue or abort. */ public class CannotCreateSchemaException extends Exception { private static final long serialVersionUID = 291582260022140141L; /** * @param t * @param reason implementor-specified runtime reason */ public CannotCreateSchemaException(Throwable t, String reason) { this.t = t; this.reason = reason; } /** * @return String */ public String toString() { String tmp = "Exception " + t.toString() + " was thrown due to " + reason; return tmp; } /** * members are made available so that the top level user can dump * appropriate members on to his stack trace */ private Throwable t = null; private String reason = null; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/FilePersistenceManager.java0000644000175000017500000001126411366547366031157 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * The class implements the PersistenceManager interface and provides users * a file based implementation when required. * The state of this class is current NOOP. Implementation will be in place * once a better structure for file based properties will be designed. */ import java.io.*; import java.util.*; public class FilePersistenceManager implements PersistenceManager { private final File file; /** * Default constructor */ public FilePersistenceManager(String propertiesFilename) throws Exception { Properties properties = new Properties(); properties.load(new FileInputStream(propertiesFilename)); String path = properties.getProperty(PersistenceFactory.persistProp); file = new File(path); file.createNewFile(); } /** * Save new NV pair as serializable objects or if already exist; store * new state */ public void save(Serializable key, Serializable val) throws CannotPersistException { try { Map map = retrieveAll(); map.put(key, val); saveAll(map); } catch (CannotRetrieveException e) { throw new CannotPersistException(e, "Unable to pre-load existing store."); } } /** * Remove existing NV from being persisted */ public Serializable remove(Serializable key) throws CannotRemoveException { Object o; try { Map map = retrieveAll(); o = map.remove(key); saveAll(map); } catch (CannotRetrieveException e) { throw new CannotRemoveException(e, "Unable to pre-load existing store."); } catch (CannotPersistException e) { throw new CannotRemoveException(e, "Unable to pre-load existing store."); } return (Serializable) o; } /** * Use to store a complete map into persistent state * @exception CannotPersistException; */ public void saveAll(Map map) throws CannotPersistException { try { OutputStream fos = new FileOutputStream(file); Properties prop = new Properties(); // NB: For some reason Properties.putAll(map) doesn't seem to work - dimc@users.sourceforge.net for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = (Map.Entry) iterator.next(); prop.setProperty(entry.getKey().toString(), entry.getValue().toString()); } prop.store(fos, null); fos.flush(); fos.close(); } catch (IOException e) { throw new CannotPersistException(e, "Cannot save to: " + file.getAbsolutePath()); } } /** * Gives back the Map in last known state * @return Map; * @exception CannotRetrieveException; */ public Map retrieveAll() throws CannotRetrieveException { try { Properties prop = new Properties(); FileInputStream fis = new FileInputStream(file); prop.load(fis); fis.close(); return filterLoadedValues(prop); } catch (IOException e) { throw new CannotRetrieveException(e, "Unable to load from file: " + file.getAbsolutePath()); } } /** * Turns the values into Floats to enable * {@link org.jgroups.demos.DistributedHashtableDemo} to work. * Subclasses should override this method to convert the incoming map * of string/string key/value pairs into the types they want. * @param in * @return Map */ protected Map filterLoadedValues(Map in) { Map out = new HashMap(); for (Iterator iterator = in.entrySet().iterator(); iterator.hasNext();) { Map.Entry entry = (Map.Entry) iterator.next(); out.put(entry.getKey().toString(), Float.valueOf(entry.getValue().toString())); } return out; } /** * Clears the complete NV state from the DB * @exception CannotRemoveException; x*/ public void clear() throws CannotRemoveException { try { saveAll(Collections.EMPTY_MAP); } catch (CannotPersistException e) { throw new CannotRemoveException(e, "Unable to clear map."); } } /** * Used to handle shutdown call the PersistenceManager implementation. * Persistent engines can leave this implementation empty. */ public void shutDown() { return; } }// end of class libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/CannotRetrieveException.java0000644000175000017500000000170411366547366031405 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This exception inherits the Exception class and is used in * cases where the Persistence Storage cannot retrieve a pair * from its storage mechanism (leading to failure of mechanism) */ public class CannotRetrieveException extends Exception { private static final long serialVersionUID = -2523227229540681597L; /** * @param t * @param reason implementor-specified runtime reason */ public CannotRetrieveException(Throwable t, String reason) { this.t = t; this.reason = reason; } /** * @return String; */ public String toString() { String tmp = "Exception " + t.toString() + " was thrown due to " + reason; return tmp; } /** * members are made available so that the top level user can dump * appropriate members on to his stack trace */ private Throwable t = null; private String reason = null; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/DBPersistenceManager.java0000644000175000017500000005272311366547366030572 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This class implements the DB storage pattern for the Persistence * Manager interface. The implementation is open and can be used (and * tested) over more than one databases. It uses a string (VARCHAR) * as the key and either BLOB or VARBINARY db-datatype for the * serialized objects. THe user has the option to choose his/her own * schema over the ones provided. */ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.*; import java.sql.*; import java.util.*; /** * Class will be utilized */ public class DBPersistenceManager implements PersistenceManager { protected final Log log=LogFactory.getLog(this.getClass()); /** * Default construct * @param filename absolute filepath * @exception Exception; */ public DBPersistenceManager(String filename) throws Exception { String home_dir = null; // PropertyPermission not granted if running in an untrusted environment with JNLP. try { home_dir = System.getProperty("user.home"); } catch (SecurityException ex1) { } // 1. Try ${user.home}/persist.properties try { home_dir=home_dir + '/' + filename; init(new FileInputStream(home_dir)); return; } catch(Exception ex) { ; } // 2. Try to find persist.properties from somewhere on the CLASSPATH try { InputStream in=DBPersistenceManager.class.getResourceAsStream('/' + filename); if(in != null) { init(in); return; } } catch(Exception x) { if(log.isErrorEnabled()) log.error("failed reading database properties from " + filename + ", exception=" + x); } // 3. Finally maybe the user specified -Dpersist.properties=/home/user/mypersist.properties try { home_dir=System.getProperty("persist.properties"); init(new FileInputStream(home_dir)); return; } catch(Exception ex) { ; } // 4. If none of the above helped us to find persist.properties, give up and throw an exception throw new Exception("DBPersistenceManager.DBPersistenceManager(): " + "failed reading database properties from " + filename); } /** * Duplicate constructor allowing inputstream * @param input * @exception Exception */ public DBPersistenceManager(InputStream input) throws Exception { init(input); } /** * used to intitiailize complete DB access. THis method will use * existing database to create schema (if it doesnt exist) and * get PersistenceManager in usable condition * @param in * @exception Exception; */ protected void init(InputStream in) throws Exception { list=new Vector(); readProps(in); loadDriver(); //check conn Connection conn=this.getConnection(); this.closeConnection(conn); createDBTables(); retrieveAll(); // work around to make sure, no duplicates are created. log.error(" Done constructing DB Persist Manager"); } // TODO list for this implementation // add constructor for xml file // add constructor for default /** * Saves NV pair as serializable object; * creates if new, stores new state if already exists. * @param key * @param val * @exception CannotPersistException; */ public void save(Serializable key, Serializable val) throws CannotPersistException { // checking if this is update or new entry if(!entryExists(key)) { log.error(" entry doesnt exist for " + key.toString()); try { addNewEntry(key, val); list.add(key.toString()); return; } catch(Throwable t1) { t1.printStackTrace(); //trace here throw new CannotPersistException(t1, " error adding a completely new entry in to DB "); } }// checking entries // THis is for regular updates to the key,val pair Connection conn=null; PreparedStatement prepStat=null; try { conn=this.getConnection(); String keyStr=null; keyStr=key.toString(); byte[] keyBytes=getBytes(key); byte[] valBytes=getBytes(val); log.error(" value is " + val); //use simple execute, do not create prepared statement prepStat=conn.prepareStatement(updateStat); prepStat.setString(3, keyStr); prepStat.setBytes(1, keyBytes); prepStat.setBytes(2, valBytes); prepStat.executeQuery(); } catch(Throwable t) { //trace here t.printStackTrace(); // throw exception here throw new CannotPersistException(t, "error updating an existing entry in to the database "); } // cleanup finally { try { if(prepStat != null) prepStat.close(); this.closeConnection(conn); } catch(Throwable t) { // trace conn=null; prepStat=null; } } } /** * Removes existing entry. * @param key * @exception CannotRemoveException; */ public Serializable remove(Serializable key) throws CannotRemoveException { Connection conn=null; Statement stat=null; PreparedStatement prepStat=null; ResultSet set=null; Serializable val=null; try { conn=this.getConnection(); stat=conn.createStatement(); String exQuery=" select * from replhashmap where key like '" + key.toString() + '\''; set=stat.executeQuery(exQuery); set.next(); val=getSerializable(set.getBinaryStream(3)); } catch(Throwable t3) { //trace t3.printStackTrace(); throw new CannotRemoveException(t3, " Error retrieving value for given key"); } finally { try { if(prepStat != null) prepStat.close(); this.closeConnection(conn); } catch(Throwable t) { // trace conn=null; prepStat=null; } } try { conn=this.getConnection(); prepStat=conn.prepareStatement(removeStat); prepStat.setString(1, key.toString()); prepStat.executeQuery(); list.remove(key.toString()); } catch(Throwable t) { //trace here.. t.printStackTrace(); // throw Exception throw new CannotRemoveException(t, "Could not remove existing entry due to error in jdbc transaction"); } // cleanup finally { try { set.close(); stat.close(); if(prepStat != null) prepStat.close(); this.closeConnection(conn); } catch(Throwable t) { // trace conn=null; stat=null; }//end of try..catch }// end of finally.. return val; }// end of remove /** * Saves all row entries for the map to DB. * @param map * @exception CannotPersistException; */ public synchronized void saveAll(Map map) throws CannotPersistException { Iterator iter=null; try { Set keySet=map.keySet(); iter=keySet.iterator(); } catch(Throwable t) { t.printStackTrace(); //trace here throw new CannotPersistException(t, "Error with the map entered to saveAll"); } //Individually saving all while(iter.hasNext()) { try { Serializable key=(Serializable) iter.next(); Serializable val=(Serializable) map.get(key); // dont this in same thread, optimization can be added this.save(key, val); } catch(Throwable t2) { t2.printStackTrace(); //trace here continue; } }// end of while.. }// end of saveall /** * Used to retrieve the persisted map back to its last known state * @return Map; * @exception CannotRetrieveException; */ public synchronized Map retrieveAll() throws CannotRetrieveException { Connection conn=null; Statement stat=null; ResultSet set=null; Map map=null; try { conn=this.getConnection(); stat=conn.createStatement(); set=stat.executeQuery(" select * from replhashmap"); map=retrieveAll(set); } catch(Throwable t) { //trace here throw new CannotRetrieveException(t, "Error happened while querying the database for bulk retrieve, try starting DB manually"); } //finally try { stat.close(); this.closeConnection(conn); } catch(Throwable t1) { // trace it // ignore } return map; }// end of retrieveall /** * Helper method to get get back the map * @return Map; * @exception Exception; */ private Map retrieveAll(ResultSet result) throws Exception { HashMap map=new HashMap(); while(result.next()) { InputStream inputStrKey=result.getBinaryStream(2); InputStream inputStrVal=result.getBinaryStream(3); Serializable key=getSerializable(inputStrKey); Serializable val=getSerializable(inputStrVal); map.put(key, val); list.add(key.toString()); }// end of while.. return map; } /** * Clears the key-cache as well as all entries * @exception CannotRemoveException; */ public void clear() throws CannotRemoveException { Connection conn=null; Statement stat=null; try { conn=this.getConnection(); stat=conn.createStatement(); stat.executeQuery("delete from replhashmap"); } catch(Throwable t) { //trace here throw new CannotRemoveException(t, " delete all query failed with existing database"); } //finally try { stat.close(); this.closeConnection(conn); } catch(Throwable t) { conn=null; stat=null; } } /** * Shutting down the database cleanly */ public void shutDown() { // non-trivial problem, more research required // no-op for now.. } /** * The private interfaces are used specifically to this manager */ /** * Used to enter a completely new row in to the current table * @param Serializable; key * @param Serializable; value * @exception CannotPersistException; */ private void addNewEntry(Serializable key, Serializable val) throws CannotPersistException, CannotConnectException { Connection conn=getConnection(); try { PreparedStatement prepStat=conn.prepareStatement(insertStat); prepStat.setString(1, key.toString()); byte[] keyBytes=getBytes(key); byte[] valBytes=getBytes(val); //InputStream keyStream = getBinaryInputStream(key); //InputStream valStream = getBinaryInputStream(val); prepStat.setBytes(2, keyBytes); prepStat.setBytes(3, valBytes); //prepStat.setBinaryStream(keyStream); //prepStat.setBinaryStream(valStream); prepStat.executeQuery(); conn.commit(); log.error(" executing insert " + insertStat); } catch(Throwable t) { //conn.rollback(); t.printStackTrace(); //trace here throw new CannotPersistException(t, "error adding new entry using creating Db connection and schema"); } }// end of addentry.. /** * Gets a binaryinputstream from a serialized object * @param Serializable; * @return BinaryInputStream; * @exception Exception; */ private java.io.InputStream getBinaryInputStream(Serializable ser) throws Exception { ByteArrayOutputStream stream=new ByteArrayOutputStream(); ObjectOutputStream keyoos=new ObjectOutputStream(stream); keyoos.writeObject(ser); ByteArrayInputStream pipe=new ByteArrayInputStream(stream.toByteArray()); return pipe; }// end of stream conversion /** * Gets a serializable back from a InputStream * @param InputStream; * @return Serializable; * @exception Exception; */ private Serializable getSerializable(java.io.InputStream stream) throws Exception { ObjectInputStream ooStr=new ObjectInputStream(stream); Serializable tmp=(Serializable) ooStr.readObject(); return tmp; } /** * Used to enter a completely new row in to the current table * @param Serializable; key * @param Serializable; value * @exception CannotPersistException; */ private void addNewEntryGen(Serializable key, Serializable val) throws CannotPersistException, CannotConnectException { Connection conn=getConnection(); try { PreparedStatement prepStat=conn.prepareStatement(insertStat); prepStat.setString(1, key.toString()); prepStat.setBytes(2, getBytes(key)); prepStat.setBytes(3, getBytes(val)); prepStat.executeUpdate(); } catch(Throwable t) { //trace here throw new CannotPersistException(t, "error adding new entry using creating Db connection and schema"); } }// end of entering new row gen /** * Used to enter a completely new row in to the current table * @param Serializable; key * @param Serializable; value * @exception CannotPersistException; */ private void addNewEntryOra(Serializable key, Serializable val) throws CannotPersistException, CannotConnectException { Connection conn=getConnection(); try { PreparedStatement prepStat=conn.prepareStatement(insertStat); prepStat.setString(1, key.toString()); InputStream keyBin=getBinaryInputStream(key); InputStream keyVal=getBinaryInputStream(val); byte[] keyBytes=getBytes(key); byte[] valBytes=getBytes(val); prepStat.setBytes(2, keyBytes); prepStat.setBytes(3, valBytes); prepStat.executeBatch(); } catch(Throwable t) { //trace here throw new CannotPersistException(t, "error adding new entry using creating Db connection and schema"); } }// end of entering new row ora /** * Cache checking * @param java.io.Serializable * @return boolean; */ private boolean entryExists(Serializable key) { return list.contains(key.toString()); } /** * Conversion helper * @param Serializable; * @return byte[]; */ private byte[] getBytes(Serializable ser) throws Exception { ByteArrayOutputStream stream=new ByteArrayOutputStream(); ObjectOutputStream keyoos=new ObjectOutputStream(stream); keyoos.writeObject(ser); byte[] keyBytes=stream.toByteArray(); return keyBytes; }// end of getBytes /** * ALL IMPL below is for INIT purposes */ /** * This method will be invoked by defauly by each persistence * manager to read from a default location or one provided by * the caller. * @return void; * @exception Exception; */ private void readProps(String filePath) throws Exception { FileInputStream _stream=new FileInputStream(filePath); props=new Properties(); props.load(_stream); // using properties to set most used variables driverName=props.getProperty("jdbc.Driver"); connStr=props.getProperty("jdbc.Conn").trim(); userName=props.getProperty("jdbc.User").trim(); userPass=props.getProperty("jdbc.Pass").trim(); createTable=props.getProperty("jdbc.table").trim(); } /** * Duplicate reader using stream instead of dile * @param InputStream; * @exception Exception; */ private void readProps(InputStream input) throws Exception { props=new Properties(); props.load(input); // using properties to set most used variables driverName=props.getProperty("jdbc.Driver"); connStr=props.getProperty("jdbc.Conn"); userName=props.getProperty("jdbc.User"); userPass=props.getProperty("jdbc.Pass"); createTable=props.getProperty("jdbc.table"); } /** * Loads the driver using the driver class name. Drivers can be simply * loaded by loading the class or by registering specifically using the * JDBC DriverManager * @return void; * @exception Exception; */ private void loadDriver() throws Exception { // driver classes when loaded load the driver into VM Class.forName(driverName); } /** * Once the driver is loaded, the DB is ready to be connected. This * method provides a handle to connect to the DB. * @return Connection; * @exception CannotConnectException; */ private Connection getConnection() throws CannotConnectException { try { connStr=connStr.trim(); Connection conn=DriverManager.getConnection(connStr, userName, userPass); if(log.isInfoEnabled()) log.info("userName=" + userName + ", userPass=" + userPass + ", connStr=" + connStr); return conn; } catch(Throwable t) { t.printStackTrace(); //trace here throw new CannotConnectException(t, "Error in creating connection using provided properties "); } }// end of get conn.. /** * Method is used for closing created connection. * Pooling is not implemented currently, but will be made available * as soon as this manager uses large number of transactions * @param Connection */ private void closeConnection(Connection conn) { try { if(conn != null) { conn.close(); conn=null; } } catch(Throwable t) { //trace here conn=null; } }// end of closeConn /** * Used to create table provided the DB instance * @exception CannotCreateSchemaException; * @exception CannotConnectException; */ private void createDBTables() throws CannotCreateSchemaException, CannotConnectException { Connection conn=this.getConnection(); Statement stat=null; try { stat=conn.createStatement(); } catch(Exception e) { //trace here.. e.printStackTrace(); throw new CannotConnectException(e, "there was an error in creating statements for persisting data using created connection"); } try { ResultSet set=stat.executeQuery("select * from replhashmap"); } catch(Throwable t) { t.printStackTrace(); //use connection to create new statement addSchemaToDB(conn); }// end of out throwable.. }// end of method.. /** * used to create required table within the DB * @param Connection; * @exception CannotCreateSchema; */ private void addSchemaToDB(Connection conn) throws CannotCreateSchemaException { Statement stat=null; Statement stat2=null; try { stat=conn.createStatement(); log.error(" executing query for oracle " + createTable); stat.executeQuery(createTable); } catch(Throwable t) { t.printStackTrace(); // trace here throw new CannotCreateSchemaException(t, "error was using schema with blobs"); }// end of catch // clean up is required after init finally { try { if(stat != null) stat.close(); this.closeConnection(conn); } catch(Throwable t3) { } }// end of finally.. }// end of gen schema.. private Properties props=null; private String driverName=null; private String userName=null; private String userPass=null; private String connStr=null; private String createTable=null; private final boolean oracleDB=false; private Vector list=null; private static final String tabName="replhashmap"; private static final String insertStat="insert into replhashmap(key, keyBin, valBin) values (?, ?, ?)"; private static final String updateStat="update replhashmap set keyBin = ?, valBin = ? where key like ?"; private static final String removeStat=" delete from replhashmap where key like ?"; private static final String createTableGen=" create table replhashmap(key varchar, keyBin varbinary, valBin varbinary)"; private static final String createTableOra=" create table replhashmap ( key varchar2(100), keyBin blob, valBin blob)"; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/CannotRemoveException.java0000644000175000017500000000166511366547366031063 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This exception inherits the Exception class and is used in * cases where the Persistence Storage cannot remove a pair * from its storage mechanism (leading to permannt errors) */ public class CannotRemoveException extends Exception { private static final long serialVersionUID = -5777024088921236116L; /** * @param t * @param reason implementor-specified runtime reason */ public CannotRemoveException(Throwable t, String reason) { this.t = t; this.reason = reason; } /** * @return String; */ public String toString() { String tmp = "Exception " + t.toString() + " was thrown due to " + reason; return tmp; } /** * members are made available so that the top level user can dump * appropriate members on to his stack trace */ public Throwable t = null; public String reason = null; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/CannotConnectException.java0000644000175000017500000000214111366547366031205 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This exception inherits the Exception class and is used in * cases where the Persistence Storage cannot be connected to * for any purpose. */ public class CannotConnectException extends Exception { private static final long serialVersionUID = 7472528586067210747L; /** * @param t * @param reason implementor-specified runtime reason */ public CannotConnectException(Exception t, String reason) { this.t = t; this.reason = reason; } /** * @param t * @param reason implementor-specified runtime reason */ public CannotConnectException(Throwable t, String reason) { this.t = t; this.reason = reason; } /** * @return String; */ public String toString() { String tmp = "Exception " + t.toString() + " was thrown due to " + reason; return tmp; } /** * members are made available so that the top level user can dump * appropriate members on to his stack trace */ public Throwable t = null; public String reason = null; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/PersistenceFactory.java0000644000175000017500000001335711366547366030421 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This class is the factory to get access to any DB based or file based * implementation. None of the implementations should expose directly * to user for migration purposes */ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.util.Util; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.Properties; public class PersistenceFactory { protected final static Log log=LogFactory.getLog(PersistenceFactory.class); /** * Default private constructor// does nothing */ private PersistenceFactory() { } /** * Singular public method to get access to any of the Persistence * Manager implementations. It is important to known at this point * that properties determine the implementation of the Persistence * Manager, there is no direct interface which gives access to * either DB implemented ot FILE implemented storage API. * @return PersistenceFactory; */ public static PersistenceFactory getInstance() { log.debug(" getting factory instance "); if (_factory == null) _factory = new PersistenceFactory(); return _factory; } /** * Register a custom persistence manager as opposed to the * {@link FilePersistenceManager} or {@link DBPersistenceManager}. */ public synchronized void registerManager(PersistenceManager manager) { _manager = manager; } /** * Reads the default properties and creates a persistencemanager * The default properties are picked up from the $USER_HOME or * from the classpath. Default properties are represented by * "persist.properties" * @return PersistenceManager * @exception Exception; */ public synchronized PersistenceManager createManager() throws Exception { // will return null if not initialized // uses default properties if (_manager == null) { if (checkDB()) _manager = createManagerDB(propPath); else _manager = createManagerFile(propPath); } return _manager; } /** * Duplicated signature to create PersistenceManager to allow user to * provide property path. * @param filePath complete pathname to get the properties * @return PersistenceManager; * @exception Exception; */ public synchronized PersistenceManager createManager (String filePath) throws Exception { if (_manager == null) { if (checkDB(filePath)) _manager = createManagerDB(filePath); else _manager = createManagerFile(filePath); } return _manager; } /** * Internal creator of DB persistence manager, the outside user accesses only * the PersistenceManager interface API */ private PersistenceManager createManagerDB(String filePath) throws Exception { if(log.isInfoEnabled()) log.info("Calling db persist from factory: " + filePath); if (_manager == null) _manager = new DBPersistenceManager(filePath); return _manager; }// end of DB persistence /** * creates instance of file based persistency manager * @return PersistenceManager */ private PersistenceManager createManagerFile(String filePath) { if(log.isInfoEnabled()) log.info("Creating file manager: " + filePath); Properties props; try { if (_manager == null) { props=readProps(filePath); String classname=props.getProperty(filePersistMgr); if(classname != null) { Class cl=Util.loadClass(classname, this.getClass()); Constructor ctor=cl.getConstructor(new Class[]{String.class}); _manager=(PersistenceManager)ctor.newInstance(new Object[]{filePath}); } else { _manager = new FilePersistenceManager(filePath); } } return _manager; } catch (Throwable t) { t.printStackTrace(); return null; } }// end of file persistence /** * checks the default properties for DB/File flag * @return boolean; * @exception Exception; */ private boolean checkDB() throws Exception { Properties props=readProps(propPath); String persist = props.getProperty(persistProp); if ("DB".equals(persist)) return true; return false; } /** * checks the provided properties for DB/File flag * @return boolean; */ private boolean checkDB(String filePath) throws Exception { Properties props=readProps(filePath); String persist = props.getProperty(persistProp); if ("DB".equals(persist)) return true; return false; } Properties readProps(String fileName) throws IOException { Properties props; FileInputStream _stream = new FileInputStream(fileName); props=new Properties(); props.load(_stream); return props; } private static volatile PersistenceManager _manager = null; private static volatile PersistenceFactory _factory = null; /* Please set this according to configuration */ final static String propPath = "persist.properties"; final static String persistProp = "persist"; /** The class that implements a file-based PersistenceManager */ final static String filePersistMgr="filePersistMgr"; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/persistence/CannotPersistException.java0000644000175000017500000000167111366547366031254 0ustar twernertwernerpackage org.jgroups.persistence; /** * @author Mandar Shinde * This exception inherits the Exception class and is used in * cases where the Persistence Storage cannot persist a pair * from its storage mechanism (leading to fatal errors) */ public class CannotPersistException extends Exception { private static final long serialVersionUID = -5157400778265186170L; /** * @param t * @param reason implementor-specified runtime reason */ public CannotPersistException(Throwable t, String reason) { this.t = t; this.reason = reason; } /** * @return String; */ public String toString() { String tmp = "Exception " + t.toString() + " was thrown due to " + reason; return tmp; } /** * members are made available so that the top level user can dump * appropriate members on to his stack trace */ private Throwable t = null; private String reason = null; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/overview.html0000644000175000017500000000135311366547366024143 0ustar twernertwerner JGroups is a toolkit for reliable group communication. Processes can join a group, send messages to all members or single members, and receive messages from members in the group. The system keeps track of the members in every group, and notifies group members when a new member joins, or an existing member leaves or crashes. A group is identified by its name. Groups do not have to be created explicitly; when a process joins a non-existing group, that group will be created automatically. Member processes of a group can be located on the same host, within the same LAN, or across a WAN. A member can be part of multiple groups. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/MessageListener.java0000644000175000017500000000166111366547366025346 0ustar twernertwerner// $Id: MessageListener.java,v 1.2 2005/07/17 11:38:05 chrislott Exp $ package org.jgroups; /** * Allows a listener to be notified when a message arrives. * Contrary to the pull-style of channels, some building blocks * (e.g., {@link org.jgroups.blocks.PullPushAdapter}) provide an * event-like, push-style message delivery model. * In this case, the entity to be notified of message reception needs to * provide a callback to be invoked whenever a message has been received. * The MessageListener interface provides a method to do so. */ public interface MessageListener { /** * Called when a message is received. * @param msg */ void receive(Message msg); /** * Answers the group state; e.g., when joining. * @return byte[] */ byte[] getState(); /** * Sets the group state; e.g., when joining. * @param state */ void setState(byte[] state); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/auth/0000755000175000017500000000000011621261110022314 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/auth/X509Token.java0000644000175000017500000002203411366547366024660 0ustar twernertwernerpackage org.jgroups.auth; import org.jgroups.util.Util; import org.jgroups.Message; import javax.crypto.Cipher; import java.io.*; import java.util.Properties; import java.security.cert.X509Certificate; import java.security.PrivateKey; import java.security.KeyStore; /** *

* This is an example of using a preshared token that is encrypted using an X509 certificate for authentication purposes. All members of the group have to have the same string value in the JGroups config. *

*

* This example uses certificates contained within a specified keystore. Configuration parameters for this example are shown below: *

*
    *
  • keystore_type = JKS(default)/PKCS12 - see http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA
  • *
  • keystore_path (required) = the location of the keystore
  • *
  • keystore_password (required) = the password of the keystore
  • *
  • cert_alias (required) = the alias of the certification within the keystore
  • *
  • cert_password = the password of the certification within the keystore
  • *
  • auth_value (required) = the string to encrypt
  • *
  • cipher_type = RSA(default)/AES/Blowfish/DES/DESede/PBEWithMD5AndDES/PBEWithHmacSHA1AndDESede/RC2/RC4/RC5 - see http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html#AppA
  • *
* @see org.jgroups.auth.AuthToken * @author Chris Mills */ public class X509Token extends AuthToken { public static final String KEYSTORE_TYPE = "keystore_type"; public static final String KEYSTORE_PATH = "keystore_path"; public static final String KEYSTORE_PASSWORD = "keystore_password"; public static final String CERT_ALIAS = "cert_alias"; public static final String CERT_PASSWORD = "cert_password"; public static final String TOKEN_ATTR = "auth_value"; public static final String CIPHER_TYPE = "cipher_type"; private boolean valueSet = false; private String keystore_type = null; private String cert_alias = null; private String keystore_path = null; private String token_attr = null; private String cipher_type = null; private byte[] encryptedToken = null; private char[] cert_password = null; private char[] keystore_password = null; private Cipher cipher = null; private PrivateKey certPrivateKey = null; private X509Certificate certificate = null; private static final long serialVersionUID=5479828159290223720L; public X509Token() { //need an empty constructor } public void setValue(Properties properties) { if(log.isDebugEnabled()){ log.debug("setting values on X509Token object"); } if(properties.containsKey(TOKEN_ATTR)){ this.token_attr = (String) properties.get(TOKEN_ATTR); properties.remove(TOKEN_ATTR); if(log.isDebugEnabled()){ log.debug("token_attr = " + this.token_attr); } } if(properties.containsKey(KEYSTORE_TYPE)){ this.keystore_type = (String) properties.get(KEYSTORE_TYPE); properties.remove(KEYSTORE_TYPE); if(log.isDebugEnabled()){ log.debug("keystore_type = " + this.keystore_type); } }else{ this.keystore_type = "JKS"; if(log.isDebugEnabled()){ log.debug("keystore_type = " + this.keystore_type); } } if(properties.containsKey(KEYSTORE_PATH)){ this.keystore_path = (String) properties.get(KEYSTORE_PATH); properties.remove(KEYSTORE_PATH); if(log.isDebugEnabled()){ log.debug("keystore_path = " + this.keystore_path); } } if(properties.containsKey(KEYSTORE_PASSWORD)){ this.keystore_password = ((String) properties.get(KEYSTORE_PASSWORD)).toCharArray(); properties.remove(KEYSTORE_PASSWORD); if(log.isDebugEnabled()){ log.debug("keystore_password = " + this.keystore_password); } } if(properties.containsKey(CERT_ALIAS)){ this.cert_alias = (String) properties.get(CERT_ALIAS); properties.remove(CERT_ALIAS); if(log.isDebugEnabled()){ log.debug("cert_alias = " + this.cert_alias); } } if(properties.containsKey(CERT_PASSWORD)){ this.cert_password = ((String) properties.get(CERT_PASSWORD)).toCharArray(); properties.remove(CERT_PASSWORD); if(log.isDebugEnabled()){ log.debug("cert_password = " + this.cert_password); } }else{ this.cert_password = this.keystore_password; if(log.isDebugEnabled()){ log.debug("cert_password = " + this.cert_password); } } if(properties.containsKey(CIPHER_TYPE)){ this.cipher_type = (String) properties.get(CIPHER_TYPE); properties.remove(CIPHER_TYPE); if(log.isDebugEnabled()){ log.debug("cipher_type = " + this.cipher_type); } }else{ this.cipher_type = "RSA"; if(log.isDebugEnabled()){ log.debug("cipher_type = " + this.cipher_type); } } if(getCertificate()){ this.valueSet = true; if(log.isDebugEnabled()){ log.debug("X509Token created correctly"); } } } public String getName() { return "org.jgroups.auth.X509Token"; } public boolean authenticate(AuthToken token, Message msg) { if (!this.valueSet) { if(log.isFatalEnabled()){ log.fatal("X509Token not setup correctly - check token attrs"); } return false; } if((token != null) && (token instanceof X509Token)){ //got a valid X509 token object X509Token serverToken = (X509Token)token; if(!serverToken.valueSet){ if(log.isFatalEnabled()){ log.fatal("X509Token - recieved token not valid"); } return false; } try{ if(log.isDebugEnabled()){ log.debug("setting cipher to decrypt mode"); } this.cipher.init(Cipher.DECRYPT_MODE, this.certPrivateKey); String serverBytes = new String(this.cipher.doFinal(serverToken.encryptedToken)); if((serverBytes != null) && (serverBytes.equalsIgnoreCase(this.token_attr))){ if(log.isDebugEnabled()){ log.debug("X509 authentication passed"); } return true; } }catch(Exception e){ if(log.isFatalEnabled()){ log.fatal(e); } } } if(log.isWarnEnabled()){ log.warn("X509 authentication failed"); } return false; } public void writeTo(DataOutputStream out) throws IOException { if(log.isDebugEnabled()){ log.debug("X509Token writeTo()"); } Util.writeByteBuffer(this.encryptedToken, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { if(log.isDebugEnabled()){ log.debug("X509Token readFrom()"); } this.encryptedToken = Util.readByteBuffer(in); this.valueSet = true; } /** * Used during setup to get the certification from the keystore and encrypt the auth_value with the private key * @return true if the certificate was found and the string encypted correctly otherwise returns false */ private boolean getCertificate() { try{ KeyStore store = KeyStore.getInstance(this.keystore_type); java.io.FileInputStream fis = new java.io.FileInputStream(this.keystore_path); store.load(fis, this.keystore_password); this.cipher = Cipher.getInstance(this.cipher_type); this.certificate = (X509Certificate) store.getCertificate(this.cert_alias); if(log.isDebugEnabled()){ log.debug("certificate = " + this.certificate.toString()); } this.cipher.init(Cipher.ENCRYPT_MODE, this.certificate); this.encryptedToken = this.cipher.doFinal(this.token_attr.getBytes()); if(log.isDebugEnabled()){ log.debug("encryptedToken = " + this.encryptedToken); } KeyStore.PrivateKeyEntry privateKey = (KeyStore.PrivateKeyEntry)store.getEntry(this.cert_alias, new KeyStore.PasswordProtection(this.cert_password)); this.certPrivateKey = privateKey.getPrivateKey(); if(log.isDebugEnabled()){ log.debug("certPrivateKey = " + this.certPrivateKey.toString()); } return true; }catch(Exception e){ if(log.isFatalEnabled()){ log.fatal(e); } return false; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/auth/MD5Token.java0000644000175000017500000000727011366547366024605 0ustar twernertwernerpackage org.jgroups.auth; import org.jgroups.Message; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Properties; /** *

* This is an example of using a preshared token that is encrypted using an MD5/SHA hash for authentication purposes. All members of the group have to have the same string value in the JGroups config. *

*

* Configuration parameters for this example are shown below: *

*
    *
  • token_hash (required) = MD5(default)/SHA
  • *
  • auth_value (required) = the string to encrypt
  • *
* @see org.jgroups.auth.AuthToken * @author Chris Mills */ public class MD5Token extends AuthToken { public static final String TOKEN_ATTR = "auth_value"; public static final String TOKEN_TYPE = "token_hash"; private String token = null; private String hash_type = "MD5"; private static final long serialVersionUID=-3799497105109613969L; public MD5Token(){ //need an empty constructor } public MD5Token(String token){ this.token = hash(token); } public MD5Token(String token, String hash_type){ this.token = hash(token); this.hash_type = hash_type; } public void setValue(Properties properties){ this.token = hash((String)properties.get(MD5Token.TOKEN_ATTR)); properties.remove(MD5Token.TOKEN_ATTR); if(properties.containsKey(MD5Token.TOKEN_TYPE)){ hash_type = (String)properties.get(MD5Token.TOKEN_TYPE); properties.remove(MD5Token.TOKEN_TYPE); } } public String getName(){ return "org.jgroups.auth.MD5Token"; } /** * Called during setup to hash the auth_value string in to an MD5/SHA hash * @param token the string to hash * @return the hashed version of the string */ private String hash(String token){ //perform the hashing of the token key String hashedToken = null; if(hash_type.equalsIgnoreCase("SHA")){ hashedToken = Util.sha(token); }else{ hashedToken = Util.md5(token); } if(hashedToken == null){ //failed to encrypt if(log.isWarnEnabled()){ log.warn("Failed to hash token - sending in clear text"); } return token; } return hashedToken; } public boolean authenticate(AuthToken token, Message msg){ if((token != null) && (token instanceof MD5Token)){ //Found a valid Token to authenticate against MD5Token serverToken = (MD5Token) token; if((this.token != null) && (serverToken.token != null) && (this.token.equalsIgnoreCase(serverToken.token))){ //validated if(log.isDebugEnabled()){ log.debug("MD5Token match"); } return true; }else{ if(log.isWarnEnabled()){ log.warn("Authentication failed on MD5Token"); } return false; } } if(log.isWarnEnabled()){ log.warn("Invalid AuthToken instance - wrong type or null"); } return false; } public void writeTo(DataOutputStream out) throws IOException { if(log.isDebugEnabled()){ log.debug("MD5Token writeTo()"); } Util.writeString(this.token, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { if(log.isDebugEnabled()){ log.debug("MD5Token readFrom()"); } this.token = Util.readString(in); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/auth/AuthToken.java0000644000175000017500000000306211366547366025114 0ustar twernertwernerpackage org.jgroups.auth; import org.jgroups.util.Streamable; import org.jgroups.Message; import org.jgroups.protocols.AUTH; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.Serializable; import java.util.Properties; /** * Abstract AuthToken class used by implementations of AUTH, e.g. SimpleToken, X509Token * @author Chris Mills */ public abstract class AuthToken implements Serializable, Streamable{ protected final Log log = LogFactory.getLog(this.getClass()); /** A reference to AUTH */ protected AUTH auth=null; public void setAuth(AUTH auth) {this.auth=auth;} /** * Used to return the full package and class name of the implementation. This is used by the AUTH protocol to create an instance of the implementation. * @return a java.lang.String object of the package and class name */ public abstract String getName(); /** * Called during the setup of the AUTH protocol to pass property values from the JGroups config XML document to the implementing class. * @param properties a java.util.Properties object of config parameters */ public abstract void setValue(Properties properties); /** * This method should be implemented to perform the actual authentication of joining members. * @param token the token sent by the joiner * @param msg the Message object containing the actual JOIN_REQ * @return true if authenticaion passed or false if it failed. */ public abstract boolean authenticate(AuthToken token, Message msg); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/auth/FixedMembershipToken.java0000644000175000017500000001150511366547366027267 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jgroups.auth; import org.jgroups.Message; import org.jgroups.Event; import org.jgroups.Address; import org.jgroups.util.Util; import java.util.Properties; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.io.DataOutputStream; import java.io.IOException; import java.io.DataInputStream; /** *

* The FixedMemberShipToken object predefines a list of IP addresses and Ports that can join the group. *

*

* Configuration parameters for this example are shown below: *

*
    *
  • fixed_members_value (required) = List of IP addresses & ports (optionally) - ports must be seperated by a '/' e.g. 127.0.0.1/1010*127.0.0.1/4567
  • *
  • fixed_members_seperator (required) = The seperator used between IP addresses - e.g. *
  • *
* @author Chris Mills (millsy@jboss.com) */ public class FixedMembershipToken extends AuthToken { private static final String FIXED_MEMBERS_ATTR = "fixed_members_value"; private static final String FIXED_MEMBERS_SEPERATOR_ATTR = "fixed_members_seperator"; private List memberList = null; private String token = "emptyToken"; private static final long serialVersionUID=-8222380233642107792L; public FixedMembershipToken(){ } public String getName(){ return "org.jgroups.auth.FixedMembershipToken"; } public boolean authenticate(AuthToken token, Message msg) { if((token != null) && (token instanceof FixedMembershipToken) && (this.memberList != null)) { Address src=msg.getSrc(); String sourceAddressWithPort=src.toString(); String sourceAddressWithoutPort=sourceAddressWithPort.substring(0, sourceAddressWithPort.indexOf(":")); if(log.isDebugEnabled()) { log.debug("AUTHToken received from " + sourceAddressWithPort); } for(String member: memberList) { if(hasPort(member)) { if(member.equals(sourceAddressWithPort)) return true; } else { if(member.equals(sourceAddressWithoutPort)) return true; } } return false; } if(log.isWarnEnabled()) { log.warn("Invalid AuthToken instance - wrong type or null"); } return false; } private static boolean hasPort(String member) { return member.contains(":"); } public void setValue(Properties properties){ memberList = new ArrayList(); StringTokenizer memberListTokenizer = new StringTokenizer((String)properties.get(FixedMembershipToken.FIXED_MEMBERS_ATTR), (String)properties.get(FixedMembershipToken.FIXED_MEMBERS_SEPERATOR_ATTR)); while(memberListTokenizer.hasMoreTokens()){ memberList.add(memberListTokenizer.nextToken().replace('/', ':')); } properties.remove(FixedMembershipToken.FIXED_MEMBERS_ATTR); properties.remove(FixedMembershipToken.FIXED_MEMBERS_SEPERATOR_ATTR); } /** * Required to serialize the object to pass across the wire * @param out * @throws java.io.IOException */ public void writeTo(DataOutputStream out) throws IOException { if(log.isDebugEnabled()){ log.debug("SimpleToken writeTo()"); } Util.writeString(this.token, out); } /** * Required to deserialize the object when read in from the wire * @param in * @throws IOException * @throws IllegalAccessException * @throws InstantiationException */ public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { if(log.isDebugEnabled()){ log.debug("SimpleToken readFrom()"); } this.token = Util.readString(in); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/auth/SimpleToken.java0000644000175000017500000000544011366547366025446 0ustar twernertwernerpackage org.jgroups.auth; import org.jgroups.util.Util; import org.jgroups.Message; import java.io.DataOutputStream; import java.io.IOException; import java.io.DataInputStream; import java.util.Properties; /** *

* This is an example of using a preshared token for authentication purposes. All members of the group have to have the same string value in the JGroups config. *

*

JGroups config parameters:

*
    *
  • auth_value (required) = the string to encrypt
  • *
* @see org.jgroups.auth.AuthToken * @author Chris Mills */ public class SimpleToken extends AuthToken { public static final String TOKEN_ATTR = "auth_value"; private String token = null; private static final long serialVersionUID=-6889408182212567295L; public SimpleToken(){ //need an empty constructor } public SimpleToken(String token){ this.token = token; } public void setValue(Properties properties){ this.token = (String)properties.get(SimpleToken.TOKEN_ATTR); properties.remove(SimpleToken.TOKEN_ATTR); } public String getName(){ return "org.jgroups.auth.SimpleToken"; } public boolean authenticate(AuthToken token, Message msg){ if((token != null) && (token instanceof SimpleToken)){ //Found a valid Token to authenticate against SimpleToken serverToken = (SimpleToken) token; if((this.token != null) && (serverToken.token != null) && (this.token.equalsIgnoreCase(serverToken.token))){ //validated if(log.isDebugEnabled()){ log.debug("SimpleToken match"); } return true; }else{ if(log.isWarnEnabled()){ log.warn("Authentication failed on SimpleToken"); } return false; } } if(log.isWarnEnabled()){ log.warn("Invalid AuthToken instance - wrong type or null"); } return false; } /** * Required to serialize the object to pass across the wire * @param out * @throws IOException */ public void writeTo(DataOutputStream out) throws IOException { if(log.isDebugEnabled()){ log.debug("SimpleToken writeTo()"); } Util.writeString(this.token, out); } /** * Required to deserialize the object when read in from the wire * @param in * @throws IOException * @throws IllegalAccessException * @throws InstantiationException */ public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { if(log.isDebugEnabled()){ log.debug("SimpleToken readFrom()"); } this.token = Util.readString(in); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/JChannel.java0000644000175000017500000023714511366547366023746 0ustar twernertwernerpackage org.jgroups; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.conf.ConfiguratorFactory; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.stack.IpAddress; import org.jgroups.stack.ProtocolStack; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.*; import org.jgroups.protocols.TP; import org.w3c.dom.Element; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Exchanger; /** * JChannel is a pure Java implementation of Channel. * When a JChannel object is instantiated it automatically sets up the * protocol stack. *

* Properties *

* Properties are used to configure a channel, and are accepted in * several forms; the String form is described here. * A property string consists of a number of properties separated by * colons. For example: *

*

"<prop1>(arg1=val1):<prop2>(arg1=val1;arg2=val2):<prop3>:<propn>"
*

* Each property relates directly to a protocol layer, which is * implemented as a Java class. When a protocol stack is to be created * based on the above property string, the first property becomes the * bottom-most layer, the second one will be placed on the first, etc.: * the stack is created from the bottom to the top, as the string is * parsed from left to right. Each property has to be the name of a * Java class that resides in the * {@link org.jgroups.protocols} package. *

* Note that only the base name has to be given, not the fully specified * class name (e.g., UDP instead of org.jgroups.protocols.UDP). *

* Each layer may have 0 or more arguments, which are specified as a * list of name/value pairs in parentheses directly after the property. * In the example above, the first protocol layer has 1 argument, * the second 2, the third none. When a layer is created, these * properties (if there are any) will be set in a layer by invoking * the layer's setProperties() method *

* As an example the property string below instructs JGroups to create * a JChannel with protocols UDP, PING, FD and GMS:

*

"UDP(mcast_addr=228.10.9.8;mcast_port=5678):PING:FD:GMS"
*

* The UDP protocol layer is at the bottom of the stack, and it * should use mcast address 228.10.9.8. and port 5678 rather than * the default IP multicast address and port. The only other argument * instructs FD to output debug information while executing. * Property UDP refers to a class {@link org.jgroups.protocols.UDP}, * which is subsequently loaded and an instance of which is created as protocol layer. * If any of these classes are not found, an exception will be thrown and * the construction of the stack will be aborted. * * @author Bela Ban * @version $Id: JChannel.java,v 1.158.2.28 2009/09/08 17:40:25 vlada Exp $ */ public class JChannel extends Channel { /** * The default protocol stack used by the default constructor. */ public static final String DEFAULT_PROTOCOL_STACK="udp.xml"; static final String FORCE_PROPS="force.properties"; /* the protocol stack configuration string */ private String props=null; /*the address of this JChannel instance*/ private Address local_addr=null; /*the channel (also know as group) name*/ private String cluster_name=null; // group name /*the latest view of the group membership*/ private View my_view=null; /*the queue that is used to receive messages (events) from the protocol stack*/ private final Queue mq=new Queue(); /*the protocol stack, used to send and receive messages from the protocol stack*/ private ProtocolStack prot_stack=null; /** Thread responsible for closing a channel and potentially reconnecting to it (e.g., when shunned). */ protected CloserThread closer=null; /** To wait until a local address has been assigned */ private final Promise

local_addr_promise=new Promise
(); private final Promise state_promise=new Promise(); private final Exchanger applstate_exchanger=new Exchanger(); private final Promise flush_unblock_promise=new Promise(); /** wait until we have a non-null local_addr */ private long LOCAL_ADDR_TIMEOUT=30000; //=Long.parseLong(System.getProperty("local_addr.timeout", "30000")); /*if the states is fetched automatically, this is the default timeout, 5 secs*/ private static final long GET_STATE_DEFAULT_TIMEOUT=5000; /*if FLUSH is used channel waits for UNBLOCK event, this is the default timeout, 5 secs*/ private static final long FLUSH_UNBLOCK_TIMEOUT=5000; /*flag to indicate whether to receive blocks, if this is set to true, receive_views is set to true*/ private boolean receive_blocks=false; /*flag to indicate whether to receive local messages *if this is set to false, the JChannel will not receive messages sent by itself*/ private boolean receive_local_msgs=true; /*flag to indicate whether the channel will reconnect (reopen) when the exit message is received*/ private boolean auto_reconnect=true; /*flag t indicate whether the state is supposed to be retrieved after the channel is reconnected *setting this to true, automatically forces auto_reconnect to true*/ private boolean auto_getstate=false; /*channel connected flag*/ protected volatile boolean connected=false; /*channel closed flag*/ protected volatile boolean closed=false; // close() has been called, channel is unusable /** True if a state transfer protocol is available, false otherwise */ private boolean state_transfer_supported=false; // set by CONFIG event from STATE_TRANSFER protocol /** True if a flush protocol is available, false otherwise */ private volatile boolean flush_supported=false; // set by CONFIG event from FLUSH protocol /** Provides storage for arbitrary objects. Protocols can send up CONFIG events, and all key-value pairs of * a CONFIG event will be added to additional_data. On reconnect, a CONFIG event will be sent down by the channel, * containing all key-value pairs of additional_data */ protected final Map additional_data=new HashMap(); protected final ConcurrentMap info=new ConcurrentHashMap(); protected final Log log=LogFactory.getLog(getClass()); /** Collect statistics */ protected boolean stats=true; protected long sent_msgs=0, received_msgs=0, sent_bytes=0, received_bytes=0; private final TP.ProbeHandler probe_handler=new MyProbeHandler(); /** Used by subclass to create a JChannel without a protocol stack, don't use as application programmer */ protected JChannel(boolean no_op) { ; } /** * Constructs a JChannel instance with the protocol stack * specified by the DEFAULT_PROTOCOL_STACK member. * * @throws ChannelException if problems occur during the initialization of * the protocol stack. */ public JChannel() throws ChannelException { this(DEFAULT_PROTOCOL_STACK); } /** * Constructs a JChannel instance with the protocol stack * configuration contained by the specified file. * * @param properties a file containing a JGroups XML protocol stack * configuration. * * @throws ChannelException if problems occur during the configuration or * initialization of the protocol stack. */ public JChannel(File properties) throws ChannelException { this(ConfiguratorFactory.getStackConfigurator(properties)); } /** * Constructs a JChannel instance with the protocol stack * configuration contained by the specified XML element. * * @param properties a XML element containing a JGroups XML protocol stack * configuration. * * @throws ChannelException if problems occur during the configuration or * initialization of the protocol stack. */ public JChannel(Element properties) throws ChannelException { this(ConfiguratorFactory.getStackConfigurator(properties)); } /** * Constructs a JChannel instance with the protocol stack * configuration indicated by the specified URL. * * @param properties a URL pointing to a JGroups XML protocol stack * configuration. * * @throws ChannelException if problems occur during the configuration or * initialization of the protocol stack. */ public JChannel(URL properties) throws ChannelException { this(ConfiguratorFactory.getStackConfigurator(properties)); } /** * Constructs a JChannel instance with the protocol stack * configuration based upon the specified properties parameter. * * @param properties an old style property string, a string representing a * system resource containing a JGroups XML configuration, * a string representing a URL pointing to a JGroups XML * XML configuration, or a string representing a file name * that contains a JGroups XML configuration. * * @throws ChannelException if problems occur during the configuration and * initialization of the protocol stack. */ public JChannel(String properties) throws ChannelException { this(ConfiguratorFactory.getStackConfigurator(properties)); } /** * Constructs a JChannel instance with the protocol stack * configuration contained by the protocol stack configurator parameter. *

* All of the public constructors of this class eventually delegate to this * method. * * @param configurator a protocol stack configurator containing a JGroups * protocol stack configuration. * * @throws ChannelException if problems occur during the initialization of * the protocol stack. */ public JChannel(ProtocolStackConfigurator configurator) throws ChannelException { init(configurator); } /** * Creates a new JChannel with the protocol stack as defined in the properties * parameter. an example of this parameter is
* "UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE"
* Other examples can be found in the ./conf directory
* @param properties the protocol stack setup; if null, the default protocol stack will be used. * The properties can also be a java.net.URL object or a string that is a URL spec. * The JChannel will validate any URL object and String object to see if they are a URL. * In case of the parameter being a url, the JChannel will try to load the xml from there. * In case properties is a org.w3c.dom.Element, the ConfiguratorFactory will parse the * DOM tree with the element as its root element. * @deprecated Use the constructors with specific parameter types instead. */ public JChannel(Object properties) throws ChannelException { if (properties == null) properties = DEFAULT_PROTOCOL_STACK; ProtocolStackConfigurator c=null; try { c=ConfiguratorFactory.getStackConfigurator(properties); } catch(Exception x) { throw new ChannelException("unable to load protocol stack", x); } init(c); } /** * Returns the protocol stack. * Currently used by Debugger. * Specific to JChannel, therefore * not visible in Channel */ public ProtocolStack getProtocolStack() { return prot_stack; } protected Log getLog() { return log; } /** * returns the protocol stack configuration in string format. * an example of this property is
* "UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE" */ public String getProperties() { return props; } public boolean statsEnabled() { return stats; } public void enableStats(boolean stats) { this.stats=stats; } public void resetStats() { sent_msgs=received_msgs=sent_bytes=received_bytes=0; } public long getSentMessages() {return sent_msgs;} public long getSentBytes() {return sent_bytes;} public long getReceivedMessages() {return received_msgs;} public long getReceivedBytes() {return received_bytes;} public int getNumberOfTasksInTimer() { TimeScheduler timer=getTimer(); return timer != null? timer.size() : -1; } public int getTimerThreads() { TimeScheduler timer=getTimer(); return timer != null? timer.getCorePoolSize() : -1; } public String dumpTimerQueue() { TimeScheduler timer=getTimer(); return timer != null? timer.dumpTaskQueue() : "String denoting the group name. Cannot be null. * @exception ChannelException The protocol stack cannot be started * @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. * A new channel has to be created first. */ public synchronized void connect(String cluster_name) throws ChannelException { if(connected) { if(log.isTraceEnabled()) log.trace("already connected to " + cluster_name); return; } startStack(cluster_name); if(cluster_name != null) { // only connect if we are not a unicast channel Event connect_event=new Event(Event.CONNECT, cluster_name); Object res=downcall(connect_event); // waits forever until connected (or channel is closed) if(res != null && res instanceof Exception) { // the JOIN was rejected by the coordinator stopStack(true, false); init(); throw new ChannelException("connect() failed", (Throwable)res); } //if FLUSH is used do not return from connect() until UNBLOCK event is received if(flushSupported()) { try { flush_unblock_promise.getResultWithTimeout(FLUSH_UNBLOCK_TIMEOUT); } catch (TimeoutException timeout) { if(log.isWarnEnabled()) log.warn(local_addr + " waiting on UNBLOCK after connect() timed out"); } } } connected=true; notifyChannelConnected(this); } /** * Connects this channel to a group and gets a state from a specified state * provider. *

* * This method essentially invokes * connect and getState methods successively. * If FLUSH protocol is in channel's stack definition only one flush is executed for both connecting and * fetching state rather than two flushes if we invoke connect and getState in succesion. * * If the channel is already connected, an error message will be printed to the error log. * If the channel is closed a ChannelClosed exception will be thrown. * * * @param cluster_name the cluster name to connect to. Cannot be null. * @param target the state provider. If null state will be fetched from coordinator, unless this channel is coordinator. * @param state_id the substate id for partial state transfer. If null entire state will be transferred. * @param timeout the timeout for state transfer. * * @exception ChannelException The protocol stack cannot be started * @exception ChannelException Connecting to cluster was not successful * @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. * A new channel has to be created first. * @exception StateTransferException State transfer was not successful * */ public synchronized void connect(String cluster_name, Address target, String state_id, long timeout) throws ChannelException { if (connected) { if (log.isTraceEnabled()) log.trace("already connected to " + cluster_name); return; } startStack(cluster_name); boolean stateTransferOk = false; boolean joinSuccessful = false; boolean canFetchState = false; // only connect if we are not a unicast channel if (cluster_name != null) { try { Event connect_event = new Event(Event.CONNECT_WITH_STATE_TRANSFER, cluster_name); Object res = downcall(connect_event); // waits forever until // connected (or channel is // closed) joinSuccessful = !(res != null && res instanceof Exception); if (!joinSuccessful) { stopStack(true, false); init(); throw new ChannelException("connect() failed", (Throwable) res); } connected = true; notifyChannelConnected(this); canFetchState = getView() != null && getView().size() > 1; // if I am not the only member in cluster then if (canFetchState) { try { // fetch state from target stateTransferOk = getState(target, state_id, timeout, false); if (!stateTransferOk) { throw new StateTransferException(getLocalAddress() + " could not fetch state " + state_id + " from " + target); } } catch (Exception e) { throw new StateTransferException(getLocalAddress() + " could not fetch state " + state_id + " from " + target, e); } } } finally { if (flushSupported()) { // stopFlush if we fetched the state or failed to connect... if (canFetchState || !connected) stopFlush(); } } } } /** * Disconnects the channel if it is connected. If the channel is closed, * this operation is ignored
* Otherwise the following actions happen in the listed order
*

    *
  1. The JChannel sends a DISCONNECT event down the protocol stack
    *
  2. Blocks until the event has returned
    *
  3. Sends a STOP_QUEING event down the stack
    *
  4. Stops the protocol stack by calling ProtocolStack.stop()
    *
  5. Notifies the listener, if the listener is available
    *
*/ public synchronized void disconnect() { if(closed) return; if(connected) { if(cluster_name != null) { // Send down a DISCONNECT event, which travels down to the GMS, where a response is returned Event disconnect_event=new Event(Event.DISCONNECT, local_addr); down(disconnect_event); // DISCONNECT is handled by each layer } connected=false; stopStack(true, false); notifyChannelDisconnected(this); init(); // sets local_addr=null; changed March 18 2003 (bela) -- prevented successful rejoining } } /** * Destroys the channel. * After this method has been called, the channel us unusable.
* This operation will disconnect the channel and close the channel receive queue immediately
*/ public synchronized void close() { _close(true, true); // by default disconnect before closing channel and close mq } /** Shuts down the channel without disconnecting */ public synchronized void shutdown() { down(new Event(Event.SHUTDOWN)); _close(false, true); // by default disconnect before closing channel and close mq } /** * Opens the channel. Note that the channel is only open, but not connected. * This does the following actions: *
    *
  1. Resets the receiver queue by calling Queue.reset *
  2. Sets up the protocol stack by calling ProtocolStack.setup *
  3. Sets the closed flag to false *
*/ public synchronized void open() throws ChannelException { if(!closed) throw new ChannelException("channel is already open"); try { mq.reset(); // new stack is created on open() - bela June 12 2003 prot_stack=new ProtocolStack(this, props); prot_stack.setup(); closed=false; } catch(Exception e) { throw new ChannelException("failed to open channel" , e); } } /** * returns true if the Open operation has been called successfully */ public boolean isOpen() { return !closed; } /** * returns true if the Connect operation has been called successfully */ public boolean isConnected() { return connected; } public int getNumMessages() { return mq != null? mq.size() : -1; } public String dumpQueue() { return Util.dumpQueue(mq); } /** * Returns a map of statistics of the various protocols and of the channel itself. * @return Map. A map where the keys are the protocols ("channel" pseudo key is * used for the channel itself") and the values are property maps. */ public Map dumpStats() { Map retval=prot_stack.dumpStats(); if(retval != null) { Map tmp=dumpChannelStats(); if(tmp != null) retval.put("channel", tmp); } return retval; } public Map dumpStats(String protocol_name) { return prot_stack.dumpStats(protocol_name); } protected Map dumpChannelStats() { Map retval=new HashMap(); retval.put("sent_msgs", new Long(sent_msgs)); retval.put("sent_bytes", new Long(sent_bytes)); retval.put("received_msgs", new Long(received_msgs)); retval.put("received_bytes", new Long(received_bytes)); return retval; } /** * Sends a message through the protocol stack. * Implements the Transport interface. * * @param msg the message to be sent through the protocol stack, * the destination of the message is specified inside the message itself * @exception ChannelNotConnectedException * @exception ChannelClosedException */ public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { checkClosedOrNotConnected(); if(msg == null) throw new NullPointerException("msg is null"); if(stats) { sent_msgs++; sent_bytes+=msg.getLength(); } down(new Event(Event.MSG, msg)); } /** * creates a new message with the destination address, and the source address * and the object as the message value * @param dst - the destination address of the message, null for all members * @param src - the source address of the message * @param obj - the value of the message * @exception ChannelNotConnectedException * @exception ChannelClosedException * @see JChannel#send */ public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException { send(new Message(dst, src, obj)); } /** * Blocking receive method. * This method returns the object that was first received by this JChannel and that has not been * received before. After the object is received, it is removed from the receive queue.
* If you only want to inspect the object received without removing it from the queue call * JChannel.peek
* If no messages are in the receive queue, this method blocks until a message is added or the operation times out
* By specifying a timeout of 0, the operation blocks forever, or until a message has been received. * @param timeout the number of milliseconds to wait if the receive queue is empty. 0 means wait forever * @exception TimeoutException if a timeout occured prior to a new message was received * @exception ChannelNotConnectedException * @exception ChannelClosedException * @see JChannel#peek * @deprecated Use a {@link Receiver} instead */ public Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { checkClosedOrNotConnected(); try { Event evt=(timeout <= 0)? (Event)mq.remove() : (Event)mq.remove(timeout); Object retval=getEvent(evt); evt=null; return retval; } catch(QueueClosedException queue_closed) { throw new ChannelClosedException(); } catch(TimeoutException t) { throw t; } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception: " + e); return null; } } /** * Just peeks at the next message, view or block. Does not install * new view if view is received
* Does the same thing as JChannel.receive but doesn't remove the object from the * receiver queue */ public Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { checkClosedOrNotConnected(); try { Event evt=(timeout <= 0)? (Event)mq.peek() : (Event)mq.peek(timeout); Object retval=getEvent(evt); evt=null; return retval; } catch(QueueClosedException queue_closed) { if(log.isErrorEnabled()) log.error("exception: " + queue_closed); return null; } catch(TimeoutException t) { return null; } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception: " + e); return null; } } /** * Returns the current view. *
* If the channel is not connected or if it is closed it will return null. *
* @return returns the current group view, or null if the channel is closed or disconnected */ public View getView() { return closed || !connected ? null : my_view; } /** * returns the local address of the channel * returns null if the channel is closed */ public Address getLocalAddress() { return closed ? null : local_addr; } public String getLocalAddressAsString() { return local_addr != null? local_addr.toString() : "n/a"; } /** * returns the name of the channel * if the channel is not connected or if it is closed it will return null * @deprecated Use {@link #getClusterName()} instead */ public String getChannelName() { return closed ? null : !connected ? null : cluster_name; } public String getClusterName() { return closed ? null : !connected ? null : cluster_name; } /** * Sets a channel option. The options can be one of the following: *
    *
  • Channel.BLOCK *
  • Channel.LOCAL *
  • Channel.AUTO_RECONNECT *
  • Channel.AUTO_GETSTATE *
*

* There are certain dependencies between the options that you can set, * I will try to describe them here. *

* Option: Channel.BLOCK
* Value: java.lang.Boolean
* Result: set to true will set setOpt(VIEW, true) and the JChannel will receive BLOCKS and VIEW events
*
* Option: LOCAL
* Value: java.lang.Boolean
* Result: set to true the JChannel will receive messages that it self sent out.
*
* Option: AUTO_RECONNECT
* Value: java.lang.Boolean
* Result: set to true and the JChannel will try to reconnect when it is being closed
*
* Option: AUTO_GETSTATE
* Value: java.lang.Boolean
* Result: set to true, the AUTO_RECONNECT will be set to true and the JChannel will try to get the state after a close and reconnect happens
*
* * @param option the parameter option Channel.VIEW, Channel.SUSPECT, etc * @param value the value to set for this option * */ public void setOpt(int option, Object value) { if(closed) { if(log.isWarnEnabled()) log.warn("channel is closed; option not set !"); return; } switch(option) { case VIEW: if(log.isWarnEnabled()) log.warn("option VIEW has been deprecated (it is always true now); this option is ignored"); break; case SUSPECT: if(log.isWarnEnabled()) log.warn("option SUSPECT has been deprecated (it is always true now); this option is ignored"); break; case BLOCK: if(value instanceof Boolean) receive_blocks=((Boolean)value).booleanValue(); else if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " (" + value + "): value has to be Boolean"); break; case GET_STATE_EVENTS: if(log.isWarnEnabled()) log.warn("option GET_STATE_EVENTS has been deprecated (it is always true now); this option is ignored"); break; case LOCAL: if(value instanceof Boolean) receive_local_msgs=((Boolean)value).booleanValue(); else if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " (" + value + "): value has to be Boolean"); break; case AUTO_RECONNECT: if(value instanceof Boolean) auto_reconnect=((Boolean)value).booleanValue(); else if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " (" + value + "): value has to be Boolean"); break; case AUTO_GETSTATE: if(value instanceof Boolean) { auto_getstate=((Boolean)value).booleanValue(); if(auto_getstate) auto_reconnect=true; } else if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " (" + value + "): value has to be Boolean"); break; default: if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " not known"); break; } } /** * returns the value of an option. * @param option the option you want to see the value for * @return the object value, in most cases java.lang.Boolean * @see JChannel#setOpt */ public Object getOpt(int option) { switch(option) { case VIEW: return Boolean.TRUE; case BLOCK: return receive_blocks ? Boolean.TRUE : Boolean.FALSE; case SUSPECT: return Boolean.TRUE; case AUTO_RECONNECT: return auto_reconnect ? Boolean.TRUE : Boolean.FALSE; case AUTO_GETSTATE: return auto_getstate ? Boolean.TRUE : Boolean.FALSE; case GET_STATE_EVENTS: return Boolean.TRUE; case LOCAL: return receive_local_msgs ? Boolean.TRUE : Boolean.FALSE; default: if(log.isErrorEnabled()) log.error("option " + Channel.option2String(option) + " not known"); return null; } } /** * Called to acknowledge a block() (callback in MembershipListener or * BlockEvent received from call to receive()). * After sending blockOk(), no messages should be sent until a new view has been received. * Calling this method on a closed channel has no effect. */ public void blockOk() { } /** * Retrieves a full state from the target member. *

* * State transfer is initiated by invoking getState on this channel, state * receiver, and sending a GET_STATE message to a target member - state * provider. State provider passes GET_STATE message to application that is * using the state provider channel which in turn provides an application * state to a state receiver. Upon successful installation of a state at * state receiver this method returns true. * * * @param target * State provider. If null, coordinator is used * @param state_id * The ID of the substate. If null, the entire state will be * transferred * @param timeout * the number of milliseconds to wait for the operation to * complete successfully. 0 waits until the state has been * received * * @see ExtendedMessageListener#getState(OutputStream) * @see ExtendedMessageListener#setState(InputStream) * @see MessageListener#getState() * @see MessageListener#setState(byte[]) * * * @return true if state transfer was successful, false otherwise * @throws ChannelNotConnectedException * if channel was not connected at the time state retrieval * was initiated * @throws ChannelClosedException * if channel was closed at the time state retrieval was * initiated * @throws IllegalStateException * if one of state transfer protocols is not present in this * channel * @throws IllegalStateException * if flush is used in this channel and cluster could not be * flushed */ public boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException { return getState(target,null,timeout); } /** * Retrieves a substate (or partial state) indicated by state_id from the target member. *

* * State transfer is initiated by invoking getState on this channel, state * receiver, and sending a GET_STATE message to a target member - state * provider. State provider passes GET_STATE message to application that is * using the state provider channel which in turn provides an application * state to a state receiver. Upon successful installation of a state at * state receiver this method returns true. * * * @param target * State provider. If null, coordinator is used * @param state_id * The ID of the substate. If null, the entire state will be * transferred * @param timeout * the number of milliseconds to wait for the operation to * complete successfully. 0 waits until the state has been * received * * @see ExtendedMessageListener#getState(OutputStream) * @see ExtendedMessageListener#setState(InputStream) * @see MessageListener#getState() * @see MessageListener#setState(byte[]) * * * @return true if state transfer was successful, false otherwise * @throws ChannelNotConnectedException * if channel was not connected at the time state retrieval * was initiated * @throws ChannelClosedException * if channel was closed at the time state retrieval was * initiated * @throws IllegalStateException * if one of state transfer protocols is not present in this * channel * @throws IllegalStateException * if flush is used in this channel and cluster could not be * flushed */ public boolean getState(Address target, String state_id, long timeout) throws ChannelNotConnectedException, ChannelClosedException { return getState(target, state_id, timeout, true); } /** * Retrieves a substate (or partial state) indicated by state_id from the target member. *

* * State transfer is initiated by invoking getState on this channel, state * receiver, and sending a GET_STATE message to a target member - state * provider. State provider passes GET_STATE message to application that is * using the state provider channel which in turn provides an application * state to a state receiver. Upon successful installation of a state at * state receiver this method returns true. * * * @param target * State provider. If null, coordinator is used * @param state_id * The ID of the substate. If null, the entire state will be * transferred * @param timeout * the number of milliseconds to wait for the operation to * complete successfully. 0 waits until the state has been * received * @param useFlushIfPresent * whether channel should be flushed prior to state retrieval * * @see ExtendedMessageListener#getState(OutputStream) * @see ExtendedMessageListener#setState(InputStream) * @see MessageListener#getState() * @see MessageListener#setState(byte[]) * * * @return true if state transfer was successful, false otherwise * @throws ChannelNotConnectedException * if channel was not connected at the time state retrieval * was initiated * @throws ChannelClosedException * if channel was closed at the time state retrieval was * initiated * @throws IllegalStateException * if one of state transfer protocols is not present in this * channel * @throws IllegalStateException * if flush is used in this channel and cluster could not be * flushed */ public boolean getState(Address target, String state_id, long timeout, boolean useFlushIfPresent) throws ChannelNotConnectedException, ChannelClosedException { Callable flusher = new Callable() { public Boolean call() throws Exception { return Util.startFlush(JChannel.this); } }; return getState(target, state_id, timeout, useFlushIfPresent?flusher:null); } /** * Retrieves a substate (or partial state) indicated by state_id from the target member. *

* * State transfer is initiated by invoking getState on this channel, state * receiver, and sending a GET_STATE message to a target member - state * provider. State provider passes GET_STATE message to application that is * using the state provider channel which in turn provides an application * state to a state receiver. Upon successful installation of a state at * state receiver this method returns true. * * * @param target * State provider. If null, coordinator is used * @param state_id * The ID of the substate. If null, the entire state will be * transferred * @param timeout * the number of milliseconds to wait for the operation to * complete successfully. 0 waits until the state has been * received * @param flushInvoker * algorithm invoking flush * * @see ExtendedMessageListener#getState(OutputStream) * @see ExtendedMessageListener#setState(InputStream) * @see MessageListener#getState() * @see MessageListener#setState(byte[]) * * * @return true if state transfer was successful, false otherwise * @throws ChannelNotConnectedException * if channel was not connected at the time state retrieval * was initiated * @throws ChannelClosedException * if channel was closed at the time state retrieval was * initiated * @throws IllegalStateException * if one of state transfer protocols is not present in this * channel * @throws IllegalStateException * if flush is used in this channel and cluster could not be * flushed */ protected boolean getState(Address target, String state_id, long timeout,Callable flushInvoker) throws ChannelNotConnectedException, ChannelClosedException { checkClosedOrNotConnected(); if(!state_transfer_supported) { throw new IllegalStateException("fetching state will fail as state transfer is not supported. " + "Add one of the STATE_TRANSFER protocols to your protocol configuration"); } if(target == null) target=determineCoordinator(); if(target != null && local_addr != null && target.equals(local_addr)) { if(log.isTraceEnabled()) log.trace("cannot get state from myself (" + target + "): probably the first member"); return false; } boolean initiateFlush = flushSupported() && flushInvoker!=null; if (initiateFlush) { boolean successfulFlush = false; try { successfulFlush = flushInvoker.call(); } catch (Exception e) { successfulFlush = false; // http://jira.jboss.com/jira/browse/JGRP-759 } finally { if (!successfulFlush) { throw new IllegalStateException("Node "+ local_addr+ " could not flush the cluster for state retrieval"); } } } state_promise.reset(); StateTransferInfo state_info=new StateTransferInfo(target, state_id, timeout); down(new Event(Event.GET_STATE, state_info)); Boolean b=state_promise.getResult(state_info.timeout); if(initiateFlush) stopFlush(); boolean state_transfer_successfull = b != null && b.booleanValue(); if(!state_transfer_successfull) down(new Event(Event.RESUME_STABLE)); return state_transfer_successfull; } /** * Retrieves the current group state. Sends GET_STATE event down to STATE_TRANSFER layer. * Blocks until STATE_TRANSFER sends up a GET_STATE_OK event or until timeout * milliseconds have elapsed. The argument of GET_STATE_OK should be a vector of objects. * @param targets - the target members to receive the state from ( an Address list ) * @param timeout - the number of milliseconds to wait for the operation to complete successfully * @return true of the state was received, false if the operation timed out * @deprecated Not really needed - we always want to get the state from a single member, * use {@link #getState(org.jgroups.Address, long)} instead */ public boolean getAllStates(Vector targets, long timeout) throws ChannelNotConnectedException, ChannelClosedException { throw new UnsupportedOperationException("use getState() instead"); } /** * Called by the application is response to receiving a getState() object when * calling receive(). * When the application receives a getState() message on the receive() method, * it should call returnState() to reply with the state of the application * @param state The state of the application as a byte buffer * (to send over the network). */ public void returnState(byte[] state) { try { StateTransferInfo state_info=new StateTransferInfo(null, null, 0L, state); applstate_exchanger.exchange(state_info); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } /** * Returns a substate as indicated by state_id * @param state * @param state_id */ public void returnState(byte[] state, String state_id) { try { StateTransferInfo state_info=new StateTransferInfo(null, state_id, 0L, state); applstate_exchanger.exchange(state_info); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } /** * Callback method
* Called by the ProtocolStack when a message is received. * It will be added to the message queue from which subsequent * Receives will dequeue it. * @param evt the event carrying the message from the protocol stack */ public Object up(Event evt) { int type=evt.getType(); Message msg; switch(type) { case Event.MSG: msg=(Message)evt.getArg(); if(stats) { received_msgs++; received_bytes+=msg.getLength(); } if(!receive_local_msgs) { // discard local messages (sent by myself to me) if(local_addr != null && msg.getSrc() != null) if(local_addr.equals(msg.getSrc())) return null; } break; case Event.VIEW_CHANGE: View tmp=(View)evt.getArg(); if(tmp instanceof MergeView) my_view=new View(tmp.getVid(), tmp.getMembers()); else my_view=tmp; /* * Bela&Vladimir Oct 27th,2006 (JGroups 2.4)- we need to switch to * connected=true because client can invoke channel.getView() in * viewAccepted() callback invoked on this thread * (see Event.VIEW_CHANGE handling below) */ // not good: we are only connected when we returned from connect() - bela June 22 2007 // if(connected == false) { // connected=true; // } break; case Event.CONFIG: Map config=(Map)evt.getArg(); if(config != null) { if(config.containsKey("state_transfer")) { state_transfer_supported=((Boolean)config.get("state_transfer")).booleanValue(); } if(config.containsKey("flush_supported")) { flush_supported=((Boolean)config.get("flush_supported")).booleanValue(); } } break; case Event.INFO: Map m = (Map) evt.getArg(); info.putAll(m); break; case Event.GET_STATE_OK: StateTransferInfo state_info = (StateTransferInfo) evt.getArg(); byte[] state = state_info.state; try{ if(up_handler != null){ return up_handler.up(evt); } if(state != null){ String state_id = state_info.state_id; if(receiver != null){ try{ if(receiver instanceof ExtendedReceiver && state_id != null) ((ExtendedReceiver) receiver).setState(state_id, state); else receiver.setState(state); }catch(Throwable t){ if(log.isWarnEnabled()) log.warn("failed calling setState() in receiver", t); } }else{ try{ mq.add(new Event(Event.STATE_RECEIVED, state_info)); } catch(Exception e) { } } } } finally { state_promise.setResult(state != null ? Boolean.TRUE : Boolean.FALSE); } break; case Event.STATE_TRANSFER_INPUTSTREAM_CLOSED: state_promise.setResult(Boolean.TRUE); break; case Event.STATE_TRANSFER_INPUTSTREAM: StateTransferInfo sti=(StateTransferInfo)evt.getArg(); InputStream is=sti.inputStream; //Oct 13,2006 moved to down() when Event.STATE_TRANSFER_INPUTSTREAM_CLOSED is received //state_promise.setResult(is != null? Boolean.TRUE : Boolean.FALSE); if(up_handler != null) { return up_handler.up(evt); } if(is != null) { if(receiver instanceof ExtendedReceiver) { try { if(sti.state_id == null) ((ExtendedReceiver)receiver).setState(is); else ((ExtendedReceiver)receiver).setState(sti.state_id, is); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("failed calling setState() in receiver", t); } } else if(receiver instanceof Receiver){ if(log.isWarnEnabled()){ log.warn("Channel has STREAMING_STATE_TRANSFER, however," + " application does not implement ExtendedMessageListener. State is not transfered"); Util.close(is); } } else { try { mq.add(new Event(Event.STATE_TRANSFER_INPUTSTREAM, sti)); } catch(Exception e) { } } } break; case Event.SET_LOCAL_ADDRESS: local_addr_promise.setResult((Address)evt.getArg()); break; case Event.EXIT: handleExit(evt); return null; // no need to pass event up; already done in handleExit() default: break; } // If UpHandler is installed, pass all events to it and return (UpHandler is e.g. a building block) if(up_handler != null) { Object ret=up_handler.up(evt); if(type == Event.UNBLOCK){ flush_unblock_promise.setResult(Boolean.TRUE); } return ret; } switch(type) { case Event.MSG: if(receiver != null) { try { receiver.receive((Message)evt.getArg()); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("failed calling receive() in receiver", t); } return null; } break; case Event.VIEW_CHANGE: if(receiver != null) { try { receiver.viewAccepted((View)evt.getArg()); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("failed calling viewAccepted() in receiver", t); } return null; } break; case Event.SUSPECT: if(receiver != null) { try { receiver.suspect((Address)evt.getArg()); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("failed calling suspect() in receiver", t); } return null; } break; case Event.GET_APPLSTATE: if(receiver != null) { StateTransferInfo state_info=(StateTransferInfo)evt.getArg(); byte[] tmp_state=null; String state_id=state_info.state_id; try { if(receiver instanceof ExtendedReceiver && state_id!=null) { tmp_state=((ExtendedReceiver)receiver).getState(state_id); } else { tmp_state=receiver.getState(); } } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("failed calling getState() in receiver", t); } return new StateTransferInfo(null, state_id, 0L, tmp_state); } break; case Event.STATE_TRANSFER_OUTPUTSTREAM: StateTransferInfo sti=(StateTransferInfo)evt.getArg(); OutputStream os=sti.outputStream; if(receiver instanceof ExtendedReceiver) { if(os != null) { try { if(sti.state_id == null) ((ExtendedReceiver)receiver).getState(os); else ((ExtendedReceiver)receiver).getState(sti.state_id, os); } catch(Throwable t) { if(log.isWarnEnabled()) log.warn("failed calling getState() in receiver", t); } } } else if(receiver instanceof Receiver){ if(log.isWarnEnabled()){ log.warn("Channel has STREAMING_STATE_TRANSFER, however," + " application does not implement ExtendedMessageListener. State is not transfered"); Util.close(os); } } break; case Event.BLOCK: if(!receive_blocks) { // discard if client has not set 'receiving blocks' to 'on' return Boolean.TRUE; } if(receiver != null) { try { receiver.block(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed calling block() in receiver", t); } return Boolean.TRUE; } break; case Event.UNBLOCK: //invoke receiver if block receiving is on if(receive_blocks && receiver instanceof ExtendedReceiver) { try { ((ExtendedReceiver)receiver).unblock(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed calling unblock() in receiver", t); } } //flip promise flush_unblock_promise.setResult(Boolean.TRUE); return null; default: break; } if(type == Event.MSG || type == Event.VIEW_CHANGE || type == Event.SUSPECT || type == Event.GET_APPLSTATE || type== Event.STATE_TRANSFER_OUTPUTSTREAM || type == Event.BLOCK || type == Event.UNBLOCK) { try { mq.add(evt); } catch(QueueClosedException queue_closed) { ; // ignore } catch(Exception e) { if(log.isWarnEnabled()) log.warn("exception adding event " + evt + " to message queue", e); } } if(type == Event.GET_APPLSTATE) { try { return applstate_exchanger.exchange(null); } catch(InterruptedException e) { Thread.currentThread().interrupt(); return null; } } return null; } /** * Sends a message through the protocol stack if the stack is available * @param evt the message to send down, encapsulated in an event */ public void down(Event evt) { if(evt == null) return; switch(evt.getType()) { case Event.CONFIG: try { Map m=(Map)evt.getArg(); if(m != null) { additional_data.putAll(m); if(m.containsKey("additional_data")) { byte[] tmp=(byte[])m.get("additional_data"); if(local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(tmp); } } } catch(Throwable t) { if(log.isErrorEnabled()) log.error("CONFIG event did not contain a hashmap: " + t); } break; } prot_stack.down(evt); } public Object downcall(Event evt) { if(evt == null) return null; switch(evt.getType()) { case Event.CONFIG: try { Map m=(Map)evt.getArg(); if(m != null) { additional_data.putAll(m); if(m.containsKey("additional_data")) { byte[] tmp=(byte[])m.get("additional_data"); if(local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(tmp); } } } catch(Throwable t) { if(log.isErrorEnabled()) log.error("CONFIG event did not contain a hashmap: " + t); } break; } return prot_stack.down(evt); } public String toString(boolean details) { StringBuilder sb=new StringBuilder(); sb.append("local_addr=").append(local_addr).append('\n'); sb.append("cluster_name=").append(cluster_name).append('\n'); sb.append("my_view=").append(my_view).append('\n'); sb.append("connected=").append(connected).append('\n'); sb.append("closed=").append(closed).append('\n'); if(mq != null) sb.append("incoming queue size=").append(mq.size()).append('\n'); if(details) { sb.append("receive_blocks=").append(receive_blocks).append('\n'); sb.append("receive_local_msgs=").append(receive_local_msgs).append('\n'); sb.append("auto_reconnect=").append(auto_reconnect).append('\n'); sb.append("auto_getstate=").append(auto_getstate).append('\n'); sb.append("state_transfer_supported=").append(state_transfer_supported).append('\n'); sb.append("props=").append(props).append('\n'); } return sb.toString(); } /* ----------------------------------- Private Methods ------------------------------------- */ protected final void init(ProtocolStackConfigurator configurator) throws ChannelException { if(log.isInfoEnabled()) log.info("JGroups version: " + Version.description); // ConfiguratorFactory.substituteVariables(configurator); // replace vars with system props props=configurator.getProtocolStackString(); props=Util.substituteVariable(props); prot_stack=new ProtocolStack(this, props); try { prot_stack.setup(); // Setup protocol stack (creates protocol, calls init() on them) } catch(Throwable e) { throw new ChannelException("unable to setup the protocol stack: " + e.getMessage(), e); } } /** * Initializes all variables. Used after close() or disconnect(), * to be ready for new connect() */ private void init() { local_addr=null; cluster_name=null; my_view=null; // changed by Bela Sept 25 2003 //if(mq != null && mq.closed()) // mq.reset(); connected=false; } private void startStack(String cluster_name) throws ChannelException { /*make sure the channel is not closed*/ checkClosed(); /*make sure we have a valid channel name*/ if(cluster_name == null) { if(log.isDebugEnabled()) log.debug("cluster_name is null, assuming unicast channel"); } else this.cluster_name=cluster_name; try { prot_stack.startStack(cluster_name); // calls start() in all protocols, from top to bottom } catch(Throwable e) { throw new ChannelException("failed to start protocol stack", e); } String tmp=Util.getProperty(new String[]{Global.CHANNEL_LOCAL_ADDR_TIMEOUT, "local_addr.timeout"}, null, null, false, "30000"); LOCAL_ADDR_TIMEOUT=Long.parseLong(tmp); /* Wait LOCAL_ADDR_TIMEOUT milliseconds for local_addr to have a non-null value (set by SET_LOCAL_ADDRESS) */ local_addr=local_addr_promise.getResult(LOCAL_ADDR_TIMEOUT); if(local_addr == null) { log.fatal("local_addr is null; cannot connect"); throw new ChannelException("local_addr is null"); } /*create a temporary view, assume this channel is the only member and is the coordinator*/ Vector

t=new Vector
(1); t.addElement(local_addr); my_view=new View(local_addr, 0, t); // create a dummy view TP transport=prot_stack.getTransport(); transport.registerProbeHandler(probe_handler); } /** * health check
* throws a ChannelClosed exception if the channel is closed */ protected void checkClosed() throws ChannelClosedException { if(closed) throw new ChannelClosedException(); } protected void checkClosedOrNotConnected() throws ChannelNotConnectedException, ChannelClosedException { if(closed) throw new ChannelClosedException(); if(!connected) throw new ChannelNotConnectedException(); } /** * returns the value of the event
* These objects will be returned
*
     * Event Type    - Return Type
     * Event.MSG           - returns a Message object
     * Event.VIEW_CHANGE   - returns a View object
     * Event.SUSPECT       - returns a SuspectEvent object
     * Event.BLOCK         - returns a new BlockEvent object
     * Event.GET_APPLSTATE - returns a GetStateEvent object
     * Event.STATE_RECEIVED- returns a SetStateEvent object
     * Event.Exit          - returns an ExitEvent object
     * All other           - return the actual Event object
     * 
* @param evt - the event of which you want to extract the value * @return the event value if it matches the select list, * returns null if the event is null * returns the event itself if a match (See above) can not be made of the event type */ static Object getEvent(Event evt) { if(evt == null) return null; // correct ? switch(evt.getType()) { case Event.MSG: return evt.getArg(); case Event.VIEW_CHANGE: return evt.getArg(); case Event.SUSPECT: return new SuspectEvent(evt.getArg()); case Event.BLOCK: return new BlockEvent(); case Event.UNBLOCK: return new UnblockEvent(); case Event.GET_APPLSTATE: StateTransferInfo info=(StateTransferInfo)evt.getArg(); return new GetStateEvent(info.target, info.state_id); case Event.STATE_RECEIVED: info=(StateTransferInfo)evt.getArg(); return new SetStateEvent(info.state, info.state_id); case Event.STATE_TRANSFER_OUTPUTSTREAM: info = (StateTransferInfo)evt.getArg(); return new StreamingGetStateEvent(info.outputStream,info.state_id); case Event.STATE_TRANSFER_INPUTSTREAM: info = (StateTransferInfo)evt.getArg(); return new StreamingSetStateEvent(info.inputStream,info.state_id); case Event.EXIT: return new ExitEvent(); default: return evt; } } /** * Disconnects and closes the channel. * This method does the following things *
    *
  1. Calls this.disconnect if the disconnect parameter is true *
  2. Calls Queue.close on mq if the close_mq parameter is true *
  3. Calls ProtocolStack.stop on the protocol stack *
  4. Calls ProtocolStack.destroy on the protocol stack *
  5. Sets the channel closed and channel connected flags to true and false *
  6. Notifies any channel listener of the channel close operation *
*/ protected void _close(boolean disconnect, boolean close_mq) { if(closed) return; if(disconnect) disconnect(); // leave group if connected if(close_mq) closeMessageQueue(false); stopStack(true, true); closed=true; connected=false; notifyChannelClosed(this); init(); // sets local_addr=null; changed March 18 2003 (bela) -- prevented successful rejoining } protected void stopStack(boolean disconnect, boolean destroy) { if(prot_stack != null) { try { if(disconnect) prot_stack.stopStack(cluster_name); if(destroy) prot_stack.destroy(); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed destroying the protocol stack", e); } TP transport=prot_stack.getTransport(); if(transport != null) transport.unregisterProbeHandler(probe_handler); } } public final void closeMessageQueue(boolean flush_entries) { if(mq != null) mq.close(flush_entries); } /** * Creates a separate thread to close the protocol stack. * This is needed because the thread that called JChannel.up() with the EXIT event would * hang waiting for up() to return, while up() actually tries to kill that very thread. * This way, we return immediately and allow the thread to terminate. */ private synchronized void handleExit(Event evt) { notifyChannelShunned(); if(!auto_reconnect) return; if(closer != null && !closer.isAlive()) closer=null; if(closer == null) { if(log.isDebugEnabled()) log.debug("received an EXIT event, will leave the channel"); closer=new CloserThread(evt); closer.start(); } } public boolean flushSupported() { return flush_supported; } /** * Will perform a flush of the system, ie. all pending messages are flushed out of the * system and all members ack their reception. After this call returns, no member will * be sending any messages until {@link #stopFlush()} is called. *

* In case of flush collisions, random sleep time backoff algorithm is employed and * flush is reattempted for numberOfAttempts. Therefore this method is guaranteed * to return after timeout x numberOfAttempts miliseconds. * * @param automatic_resume Call {@link #stopFlush()} after the flush * @return true if FLUSH completed within the timeout */ public boolean startFlush(boolean automatic_resume) { if(!flushSupported()) { throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); } boolean successfulFlush = (Boolean) downcall(new Event(Event.SUSPEND)); if(automatic_resume) stopFlush(); return successfulFlush; } /** * Performs a partial flush in a cluster for flush participants. *

* All pending messages are flushed out only for flush participants. * Remaining members in a cluster are not included in flush. * Flush participants should be a proper subset of a current view. * *

* In case of flush collisions, random sleep time backoff algorithm is employed and * flush is reattempted for numberOfAttempts. Therefore this method is guaranteed * to return after timeout x numberOfAttempts miliseconds. * * @param automatic_resume Call {@link #stopFlush()} after the flush * @return true if FLUSH completed within the timeout */ public boolean startFlush(List

flushParticipants,boolean automatic_resume) { boolean successfulFlush = false; if(!flushSupported()){ throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); } View v = getView(); if(v != null && v.getMembers().containsAll(flushParticipants)){ successfulFlush = (Boolean) downcall(new Event(Event.SUSPEND, flushParticipants)); }else{ throw new IllegalArgumentException("Current view " + v + " does not contain all flush participants " + flushParticipants); } if(automatic_resume) stopFlush(flushParticipants); return successfulFlush; } /** * Will perform a flush of the system, ie. all pending messages are flushed out of the * system and all members ack their reception. After this call returns, no member will * be sending any messages until {@link #stopFlush()} is called. *

* In case of flush collisions, random sleep time backoff algorithm is employed and * flush is reattempted for numberOfAttempts. Therefore this method is guaranteed * to return after timeout x numberOfAttempts miliseconds. * @param timeout * @param automatic_resume Call {@link #stopFlush()} after the flush * @return true if FLUSH completed within the timeout */ public boolean startFlush(long timeout, boolean automatic_resume) { return startFlush(automatic_resume); } public void stopFlush() { if(!flushSupported()) { throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); } flush_unblock_promise.reset(); down(new Event(Event.RESUME)); //do not return until UNBLOCK event is received try { flush_unblock_promise.getResultWithTimeout(FLUSH_UNBLOCK_TIMEOUT); } catch(TimeoutException te) { log.warn("Timeout waiting for UNBLOCK event at " + getLocalAddress()); } } public void stopFlush(List

flushParticipants) { if(!flushSupported()) { throw new IllegalStateException("Flush is not supported, add pbcast.FLUSH protocol to your configuration"); } flush_unblock_promise.reset(); down(new Event(Event.RESUME, flushParticipants)); // do not return until UNBLOCK event is received try { flush_unblock_promise.getResultWithTimeout(FLUSH_UNBLOCK_TIMEOUT); } catch(TimeoutException te) { log.warn("Timeout waiting for UNBLOCK event at " + getLocalAddress()); } } @Override public Map getInfo(){ return new HashMap(info); } public void setInfo(String key, Object value) { if(key != null) info.put(key, value); } Address determineCoordinator() { Vector
mbrs=my_view != null? my_view.getMembers() : null; if(mbrs == null) return null; if(!mbrs.isEmpty()) return mbrs.firstElement(); return null; } private TimeScheduler getTimer() { if(prot_stack != null) { TP transport=prot_stack.getTransport(); if(transport != null) { return transport.getTimer(); } } return null; } /* ------------------------------- End of Private Methods ---------------------------------- */ class MyProbeHandler implements TP.ProbeHandler { public Map handleProbe(String... keys) { HashMap map=new HashMap(2); for(String key: keys) { if(key.startsWith("jmx")) { Map tmp_stats; int index=key.indexOf("="); if(index > -1) { String value=key.substring(index +1); tmp_stats=dumpStats(value); } else tmp_stats=dumpStats(); map.put("jmx", tmp_stats != null? Util.mapToString(tmp_stats) : "null"); continue; } if(key.equals("info")) { Map tmp_info=getInfo(); map.put("info", tmp_info != null? Util.mapToString(tmp_info) : "null"); } } map.put("version", Version.description + ", cvs=\"" + Version.cvs + "\""); if(my_view != null && !map.containsKey("view")) map.put("view", my_view.toString()); map.put("local_addr", local_addr != null? local_addr.toString() : "null"); map.put("cluster", getClusterName()); map.put("member", getLocalAddressAsString() + " (" + getClusterName() + ")"); return map; } public String[] supportedKeys() { return new String[]{"jmx", "info"}; } } /** * Closes, reopens and reconnects to the cluster */ class CloserThread extends Thread { final Event evt; final Thread t=null; CloserThread(Event evt) { super(Util.getGlobalThreadGroup(), "CloserThread"); this.evt=evt; setDaemon(true); } public void run() { String old_cluster_name=cluster_name; // remember because close() will null it try { if(log.isDebugEnabled()) log.debug("closing the channel"); _close(false, false); // do not disconnect before closing channel, do not close mq (yet !) } catch(Throwable ex1) { if(log.isErrorEnabled()) log.error("failed closing the channel", ex1); } try { if(up_handler != null) up_handler.up(this.evt); else { if(receiver == null) mq.add(this.evt); } } catch(Throwable ex2) { if(log.isErrorEnabled()) log.error("failed passing up EXIT event", ex2); } if(mq != null) { Util.sleep(500); // give the mq thread a bit of time to deliver EXIT to the application try { mq.close(false); } catch(Throwable ex3) {} } for(int i=0; i < 5; i++) { try { if(closed == false) break; if(log.isDebugEnabled()) log.debug("reconnecting to cluster " + old_cluster_name); open(); if(additional_data != null) { // send previously set additional_data down the stack - other protocols (e.g. TP) use it Map m=new HashMap(additional_data); down(new Event(Event.CONFIG, m)); } } catch(Throwable ex4) { if(log.isErrorEnabled()) log.error("failure reopening channel: " + ex4); Util.sleep(500); } } if(closed) { // still closed; reopening failed above if(log.isErrorEnabled()) log.error("failed reopening channel, terminating closer thread"); closer=null; return; } while(!connected) { try { connect(old_cluster_name); notifyChannelReconnected(local_addr); } catch(Throwable ex5) { if(log.isErrorEnabled()) log.error("failure reconnecting to channel, retrying", ex5); Util.sleep(1000); // sleep 1 sec between reconnect attempts } } if(auto_getstate && state_transfer_supported) { if(log.isDebugEnabled()) log.debug("fetching the state (auto_getstate=true)"); boolean rc=false; try { rc=JChannel.this.getState(null, GET_STATE_DEFAULT_TIMEOUT); if(log.isDebugEnabled()) { if(rc) log.debug("state was retrieved successfully"); else log.debug("state transfer failed"); } } catch(Throwable ex6) { if(log.isErrorEnabled()) log.error("failed auto-fetching state", ex6); } } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/0000755000175000017500000000000011621261110022462 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/TotalOrder.java0000644000175000017500000005061111366547366025441 0ustar twernertwerner// $Id: TotalOrder.java,v 1.13.10.1 2007/11/20 08:53:41 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.util.Util; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.InetAddress; import java.nio.ByteBuffer; /** * Originally written to be a demo for TOTAL order (code to be written by a student). In the meantime, * it evolved into a state transfer demo. All members maintain a shared matrix and continually * broadcast changes to be applied to a randomly chosen field (e.g. multiplication of field with new * value, division, addition, subtraction). Each member can be started independently (starts to * broadcast update messages to all members). When "Stop" is pressed, a stop message is broadcast to * all members, causing them to stop sending messages. The "Clear" button clears the shared state; * "GetState" refreshes it from the shared group state (using the state transfer protocol).

If the * demo is to be used to show TOTAL order, then the TOTAL protocol would have to be added to the * stack. * * @author Bela Ban */ public class TotalOrder extends Frame { final Font def_font=new Font("Helvetica", Font.BOLD, 12); final Font def_font2=new Font("Helvetica", Font.PLAIN, 12); MyCanvas canvas; final MenuBar menubar=createMenuBar(); final Button start=new Button("Start"); final Button stop=new Button("Stop"); final Button clear=new Button("Clear"); final Button get_state=new Button("Get State"); final Button quit=new Button("Quit"); final Panel button_panel=new Panel(); SenderThread sender=null; ReceiverThread receiver=null; Channel channel; Dialog error_dlg; long timeout=0; int field_size=0; int num_fields=0; static final int x_offset=30; static final int y_offset=40; private int num=0; private int num_additions=0, num_subtractions=0, num_divisions=0, num_multiplications=0; void error(String s) { System.err.println(s); } class EventHandler extends WindowAdapter { final Frame gui; public EventHandler(Frame g) { gui=g; } public void windowClosing(WindowEvent e) { gui.dispose(); System.exit(0); } } class SenderThread extends Thread { TotOrderRequest req; boolean running=true; public void stopSender() { running=false; interrupt(); System.out.println("-- num_additions: " + num_additions + "\n-- num_subtractions: " + num_subtractions + "\n-- num_divisions: " + num_divisions + "\n-- num_multiplications: " + num_multiplications); num_additions=num_subtractions=num_multiplications=num_divisions=0; } public void run() { this.setName("SenderThread"); byte[] buf; int cnt=0; while(running) { try { req=createRandomRequest(); buf=req.toBuffer(); channel.send(new Message(null, null, buf)); System.out.print("-- num requests sent: " + cnt + "\r"); if(timeout > 0) Util.sleep(timeout); cnt++; if(num > 0 && cnt > num) { running=false; cnt=0; } } catch(Exception e) { error(e.toString()); return; } } } } class ReceiverThread extends Thread { SetStateEvent set_state_evt; boolean running=true; public void stopReceiver() { running=false; interrupt(); } public void run() { this.setName("ReceiverThread"); Message msg; Object o; ByteBuffer buf; TotOrderRequest req; while(running) { try { o=channel.receive(0); if(o instanceof Message) { try { msg=(Message)o; req=new TotOrderRequest(); buf=ByteBuffer.wrap(msg.getBuffer()); req.init(buf); processRequest(req); } catch(Exception e) { System.err.println(e); } } else if(o instanceof GetStateEvent) { int[][] copy_of_state=canvas.getCopyOfState(); channel.returnState(Util.objectToByteBuffer(copy_of_state)); } else if(o instanceof SetStateEvent) { // state was received, set it ! set_state_evt=(SetStateEvent)o; canvas.setState(Util.objectFromByteBuffer(set_state_evt.getArg())); } else if(o instanceof View) System.out.println(o.toString()); } catch(ChannelClosedException closed) { error("Channel has been closed; receiver thread quits"); return; } catch(Exception e) { error(e.toString()); return; } } } } void processRequest(TotOrderRequest req) throws Exception { int x=req.x, y=req.y, val=req.val; if(req.type == TotOrderRequest.STOP) { stopSender(); return; } switch(req.type) { case TotOrderRequest.ADDITION: canvas.addValueTo(x, y, val); num_additions++; break; case TotOrderRequest.SUBTRACTION: canvas.subtractValueFrom(x, y, val); num_subtractions++; break; case TotOrderRequest.MULTIPLICATION: canvas.multiplyValueWith(x, y, val); num_multiplications++; break; case TotOrderRequest.DIVISION: canvas.divideValueBy(x, y, val); num_divisions++; break; } canvas.update(); } public TotalOrder(String title, long timeout, int num_fields, int field_size, String props, int num) { Dimension s; this.timeout=timeout; this.num_fields=num_fields; this.field_size=field_size; this.num=num; setFont(def_font); try { channel=new JChannel(props); channel.connect("TotalOrderGroup"); channel.getState(null, 8000); } catch(Exception e) { e.printStackTrace(); System.exit(-1); } start.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { startSender(); } }); stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { TotOrderRequest req=new TotOrderRequest(TotOrderRequest.STOP, 0, 0, 0); byte[] buf=req.toBuffer(); channel.send( new Message( null, null, buf)); } catch(Exception ex) { } } }); clear.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { canvas.clear(); } }); get_state.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { boolean rc=channel.getState(null, 3000); if(rc == false) error("State could not be retrieved !"); } catch(Throwable t) { error("exception fetching state: " + t); } } }); quit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { channel.disconnect(); channel.close(); System.exit(0); } }); setTitle(title); addWindowListener(new EventHandler(this)); setBackground(Color.white); setMenuBar(menubar); setLayout(new BorderLayout()); canvas=new MyCanvas(num_fields, field_size, x_offset, y_offset); add("Center", canvas); button_panel.setLayout(new FlowLayout()); button_panel.setFont(def_font2); button_panel.add(start); button_panel.add(stop); button_panel.add(clear); button_panel.add(get_state); button_panel.add(quit); add("South", button_panel); s=canvas.getSize(); s.height+=100; setSize(s); startReceiver(); } void startSender() { if(sender == null || !sender.isAlive()) { sender=new SenderThread(); sender.start(); } } void stopSender() { if(sender != null) { sender.stopSender(); sender=null; } } void startReceiver() { if(receiver == null) { receiver=new ReceiverThread(); receiver.setPriority(Thread.MAX_PRIORITY); receiver.start(); } } private MenuBar createMenuBar() { MenuBar ret=new MenuBar(); Menu file=new Menu("File"); MenuItem quitm=new MenuItem("Quit"); ret.setFont(def_font2); ret.add(file); file.addSeparator(); file.add(quitm); quitm.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(1); } }); return ret; } private TotOrderRequest createRandomRequest() { TotOrderRequest ret=null; byte op_type=(byte)(((Math.random() * 10) % 4) + 1); // 1 - 4 int x=(int)((Math.random() * num_fields * 2) % num_fields); int y=(int)((Math.random() * num_fields * 2) % num_fields); int val=(int)((Math.random() * num_fields * 200) % 10); ret=new TotOrderRequest(op_type, x, y, val); return ret; } public static void main(String[] args) { TotalOrder g; String arg; long timeout=200; int num_fields=3; int field_size=80; String props=null; int num=0; props="sequencer.xml"; for(int i=0; i < args.length; i++) { arg=args[i]; if("-timeout".equals(arg)) { timeout=Long.parseLong(args[++i]); continue; } if("-num_fields".equals(arg)) { num_fields=Integer.parseInt(args[++i]); continue; } if("-field_size".equals(arg)) { field_size=Integer.parseInt(args[++i]); continue; } if("-help".equals(arg)) { System.out.println("\nTotalOrder [-timeout ] [-num_fields ] " + "[-field_size ] [-props ] [-num ]\n"); return; } if("-props".equals(arg)) { props=args[++i]; continue; } if("-num".equals(arg)) { num=Integer.parseInt(args[++i]); } } try { g=new TotalOrder("Total Order Demo on " + InetAddress.getLocalHost().getHostName(), timeout, num_fields, field_size, props, num); g.setVisible(true); } catch(Exception e) { System.err.println(e); } } } class TotOrderRequest { public static final byte STOP=0; public static final byte ADDITION=1; public static final byte SUBTRACTION=2; public static final byte MULTIPLICATION=3; public static final byte DIVISION=4; final static int SIZE=Global.BYTE_SIZE + Global.INT_SIZE * 3; public byte type=ADDITION; public int x=0; public int y=0; public int val=0; public TotOrderRequest() { } TotOrderRequest(byte type, int x, int y, int val) { this.type=type; this.x=x; this.y=y; this.val=val; } public String printType() { switch(type) { case STOP: return "STOP"; case ADDITION: return "ADDITION"; case SUBTRACTION: return "SUBTRACTION"; case MULTIPLICATION: return "MULTIPLICATION"; case DIVISION: return "DIVISION"; default: return ""; } } // public void writeExternal(ObjectOutput out) throws IOException { // out.writeByte(type); // out.writeInt(x); // out.writeInt(y); // out.writeInt(val); // } // // public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // type=in.readByte(); // x=in.readInt(); // y=in.readInt(); // val=in.readInt(); // } public byte[] toBuffer() { ByteBuffer buf=ByteBuffer.allocate(SIZE); buf.put(type); buf.putInt(x); buf.putInt(y); buf.putInt(val); return buf.array(); } public void init(ByteBuffer buf) { type=buf.get(); x=buf.getInt(); y=buf.getInt(); val=buf.getInt(); } public String toString() { return "[" + x + ',' + y + ": " + printType() + '(' + val + ")]"; } } class MyCanvas extends Canvas { int field_size=100; int num_fields=4; int x_offset=30; int y_offset=30; final Font def_font=new Font("Helvetica", Font.BOLD, 14); int[][] array=null; // state Dimension off_dimension=null; Image off_image=null; Graphics off_graphics=null; final Font def_font2=new Font("Helvetica", Font.PLAIN, 12); static final Color checksum_col=Color.blue; int checksum=0; public MyCanvas(int num_fields, int field_size, int x_offset, int y_offset) { this.num_fields=num_fields; this.field_size=field_size; this.x_offset=x_offset; this.y_offset=y_offset; array=new int[num_fields][num_fields]; setBackground(Color.white); setSize(2 * x_offset + num_fields * field_size + 30, y_offset + num_fields * field_size + 50); for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) array[i][j]=0; } public void setFieldSize(int fs) { field_size=fs; } public void setNumFields(int nf) { num_fields=nf; } public void setXOffset(int o) { x_offset=o; } public void setYOffset(int o) { y_offset=o; } public void addValueTo(int x, int y, int value) { synchronized(array) { array[x][y]+=value; repaint(); } } public void subtractValueFrom(int x, int y, int value) { synchronized(array) { array[x][y]-=value; repaint(); } } public void multiplyValueWith(int x, int y, int value) { synchronized(array) { array[x][y]*=value; repaint(); } } public void divideValueBy(int x, int y, int value) { if(value == 0) return; synchronized(array) { array[x][y]/=value; repaint(); } } public void setValueAt(int x, int y, int value) { synchronized(array) { array[x][y]=value; } repaint(); } public int getValueAt(int x, int y) { synchronized(array) { return array[x][y]; } } public void clear() { synchronized(array) { for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) array[i][j]=0; checksum=checksum(); repaint(); } } public int[][] getState() { synchronized(array) { return array; } } public int[][] getCopyOfState() { int[][] retval=new int[num_fields][num_fields]; synchronized(array) { for(int i=0; i < num_fields; i++) System.arraycopy(array[i], 0, retval[i], 0, num_fields); return retval; } } public void update() { checksum=checksum(); repaint(); } public void setState(Object new_state) { if(new_state == null) return; try { int[][] new_array=(int[][])new_state; synchronized(array) { clear(); for(int i=0; i < num_fields; i++) System.arraycopy(new_array[i], 0, array[i], 0, num_fields); checksum=checksum(); repaint(); } } catch(Exception e) { System.err.println(e); return; } } public int checksum() { int retval=0; synchronized(array) { for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) retval+=array[i][j]; } return retval; } public void update(Graphics g) { Dimension d=getSize(); if(off_graphics == null || d.width != off_dimension.width || d.height != off_dimension.height) { off_dimension=d; off_image=createImage(d.width, d.height); off_graphics=off_image.getGraphics(); } //Erase the previous image. off_graphics.setColor(getBackground()); off_graphics.fillRect(0, 0, d.width, d.height); off_graphics.setColor(Color.black); off_graphics.setFont(def_font); drawEmptyBoard(off_graphics); drawNumbers(off_graphics); g.drawImage(off_image, 0, 0, this); } public void paint(Graphics g) { update(g); } /** * Draws the empty board, no pieces on it yet, just grid lines */ void drawEmptyBoard(Graphics g) { int x=x_offset, y=y_offset; Color old_col=g.getColor(); g.setFont(def_font2); old_col=g.getColor(); g.setColor(checksum_col); g.drawString(("Checksum: " + checksum), x_offset + field_size, y_offset - 20); g.setFont(def_font); g.setColor(old_col); for(int i=0; i < num_fields; i++) { for(int j=0; j < num_fields; j++) { // draws 1 row g.drawRect(x, y, field_size, field_size); x+=field_size; } g.drawString(("" + (num_fields - i - 1)), x + 20, y + field_size / 2); y+=field_size; x=x_offset; } for(int i=0; i < num_fields; i++) { g.drawString(("" + i), x_offset + i * field_size + field_size / 2, y + 30); } } void drawNumbers(Graphics g) { Point p; String num; FontMetrics fm=g.getFontMetrics(); int len=0; synchronized(array) { for(int i=0; i < num_fields; i++) for(int j=0; j < num_fields; j++) { num="" + array[i][j]; len=fm.stringWidth(num); p=index2Coord(i, j); g.drawString(num, p.x - (len / 2), p.y); } } } Point coord2Index(int x, int y) { Point ret=new Point(); ret.x=x_offset + (x * field_size); ret.y=y_offset + ((num_fields - 1 - y) * field_size); return ret; } Point index2Coord(int i, int j) { int x=x_offset + i * field_size + field_size / 2; // int y=y_offset + j*field_size + field_size/2; int y=y_offset + num_fields * field_size - j * field_size - field_size / 2; return new Point(x, y); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/Draw2Channels.java0000644000175000017500000002734611366547366026026 0ustar twernertwerner// $Id: Draw2Channels.java,v 1.11.4.1 2007/11/20 08:53:42 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.Event; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.Random; /** * Same as Draw but using 2 channels: one for view changes (control channel) and the other one for drawing * (data channel). Ported to use Swing Nov 1 2001, not tested. * @author Bela Ban, Nov 1 2001 */ public class Draw2Channels implements ActionListener { private final String control_groupname="Draw2ChannelsGroup-Control"; private final String data_groupname="Draw2ChannelsGroup-Data"; private Channel control_channel=null; private Channel data_channel=null; String control_props=null, data_props=null; private Receiver control_receiver=null; private Receiver data_receiver=null; private int member_size=1; final boolean first=true; private JFrame mainFrame=null; private JPanel sub_panel=null; private DrawPanel panel=null; private JButton clear_button, leave_button; private final Random random=new Random(System.currentTimeMillis()); private final Font default_font=new Font("Helvetica", Font.PLAIN, 12); private final Color draw_color=selectColor(); private final Color background_color=Color.white; boolean no_channel=false; public Draw2Channels(String control_props, String data_props, boolean no_channel) throws Exception { this.control_props=control_props; this.data_props=data_props; this.no_channel=no_channel; } public static void main(String[] args) { Draw2Channels draw=null; String control_props=null, data_props=null; boolean no_channel=false; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-no_channel".equals(args[i])) { no_channel=true; continue; } help(); return; } control_props="UDP(mcast_addr=224.0.0.35;mcast_port=45566;ip_ttl=32;" + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" + "PING(timeout=2000;num_initial_members=3):" + "MERGE2(min_interval=5000;max_interval=10000):" + "FD_SOCK:" + "VERIFY_SUSPECT(timeout=1500):" + "pbcast.NAKACK(gc_lag=50;retransmit_timeout=300,600,1200,2400,4800):" + "UNICAST(timeout=5000):" + "pbcast.STABLE(desired_avg_gossip=20000):" + "FRAG(frag_size=4096;down_thread=false;up_thread=false):" + "pbcast.GMS(join_timeout=5000;" + "shun=false;print_local_addr=true)"; data_props="UDP(mcast_addr=224.10.10.200;mcast_port=5679)"; try { draw=new Draw2Channels(control_props, data_props, no_channel); draw.go(); } catch(Exception e) { System.err.println(e); System.exit(0); } } static void help() { System.out.println("Draw2Channels [-help] [-no_channel]"); } private Color selectColor() { int red=(Math.abs(random.nextInt()) % 255); int green=(Math.abs(random.nextInt()) % 255); int blue=(Math.abs(random.nextInt()) % 255); return new Color(red, green, blue); } public void go() { try { if(!no_channel) { control_receiver=new ControlReceiver(); data_receiver=new DataReceiver(); System.out.println("Creating control channel"); control_channel=new JChannel(control_props); control_channel.setReceiver(control_receiver); System.out.println("Creating data channel"); data_channel=new JChannel(data_props); data_channel.setReceiver(data_receiver); // data_channel.SetOpt(Channel.VIEW, Boolean.FALSE); System.out.println("Connecting data channel"); data_channel.connect(data_groupname); System.out.println("Connecting control channel"); control_channel.connect(control_groupname); } mainFrame=new JFrame(); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); panel=new DrawPanel(); panel.setBackground(background_color); sub_panel=new JPanel(); mainFrame.getContentPane().add("Center", panel); clear_button=new JButton("Clear"); clear_button.setFont(default_font); clear_button.addActionListener(this); leave_button=new JButton("Leave & Exit"); leave_button.setFont(default_font); leave_button.addActionListener(this); sub_panel.add("South", clear_button); sub_panel.add("South", leave_button); mainFrame.getContentPane().add("South", sub_panel); mainFrame.setVisible(true); mainFrame.setBackground(background_color); clear_button.setForeground(Color.blue); leave_button.setForeground(Color.blue); setTitle(); mainFrame.pack(); mainFrame.setLocation(15, 25); mainFrame.setVisible(true); } catch(Exception e) { System.err.println(e); } } void setTitle() { String title=""; if(no_channel) { mainFrame.setTitle(" Draw Demo "); return; } if(control_channel.getLocalAddress() != null) title+=control_channel.getLocalAddress(); title+=" (" + member_size + ") mbrs"; mainFrame.setTitle(title); } public void clearPanel() { if(panel != null) panel.clear(); } public void sendClearPanelMsg() { int tmp[]=new int[1]; tmp[0]=0; DrawCommand comm=new DrawCommand(DrawCommand.CLEAR); try { byte[] buf=Util.streamableToByteBuffer(comm); data_channel.send(new Message(null, null, buf)); } catch(Exception ex) { System.err.println(ex); } } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if("Clear".equals(command)) { if(no_channel) { clearPanel(); return; } sendClearPanelMsg(); } else if("Leave & Exit".equals(command)) { if(!no_channel) { try { control_channel.close(); } catch(Exception ex) { System.err.println(ex); } try { data_channel.close(); } catch(Exception ex) { System.err.println(ex); } } mainFrame.setVisible(false); mainFrame.dispose(); System.exit(0); } else System.out.println("Unknown action"); } private class DrawPanel extends JPanel implements MouseMotionListener { final Dimension preferred_size=new Dimension(235, 170); Image img=null; // for drawing pixels Dimension d, imgsize; Graphics gr=null; public DrawPanel() { addMouseMotionListener(this); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { if(getWidth() <= 0 || getHeight() <= 0) return; createOffscreenImage(); } }); } void createOffscreenImage() { d=getSize(); if(img == null || imgsize == null || imgsize.width != d.width || imgsize.height != d.height) { img=createImage(d.width, d.height); gr=img.getGraphics(); imgsize=d; } } /* ---------------------- MouseMotionListener interface------------------------- */ public void mouseMoved(MouseEvent e) { } public void mouseDragged(MouseEvent e) { int x=e.getX(), y=e.getY(); DrawCommand comm=new DrawCommand(DrawCommand.DRAW, x, y, draw_color.getRed(), draw_color.getGreen(), draw_color.getBlue()); if(no_channel) { drawPoint(comm); return; } try { byte[] buf=Util.streamableToByteBuffer(comm); data_channel.send(new Message(null, null, buf)); Thread.yield(); // gives the repainter some breath } catch(Exception ex) { System.err.println(ex); } } /* ------------------- End of MouseMotionListener interface --------------------- */ /** * Adds pixel to queue and calls repaint() whenever we have MAX_ITEMS pixels in the queue * or when MAX_TIME msecs have elapsed (whichever comes first). The advantage compared to just calling * repaint() after adding a pixel to the queue is that repaint() can most often draw multiple points * at the same time. */ public void drawPoint(DrawCommand c) { if(c == null) return; gr.setColor(new Color(c.r, c.g, c.b)); gr.fillOval(c.x, c.y, 10, 10); repaint(); } public void clear() { gr.clearRect(0, 0, getSize().width, getSize().height); repaint(); } public Dimension getPreferredSize() { return preferred_size; } public void paintComponent(Graphics g) { super.paintComponent(g); if(img != null) { g.drawImage(img, 0, 0, null); } } } class ControlReceiver extends ExtendedReceiverAdapter { public void viewAccepted(View v) { member_size=v.size(); if(mainFrame != null) mainFrame.setTitle(member_size + " mbrs"); data_channel.down(new Event(Event.VIEW_CHANGE, v)); } } class DataReceiver extends ExtendedReceiverAdapter implements ChannelListener { public void receive(Message msg) { byte[] buf=msg.getRawBuffer(); DrawCommand comm=null; try { comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, msg.getOffset(), msg.getLength()); switch(comm.mode) { case DrawCommand.DRAW: if(panel != null) panel.drawPoint(comm); break; case DrawCommand.CLEAR: clearPanel(); break; default: System.err.println("***** Draw2Channels.run(): received invalid draw command " + comm.mode); break; } } catch(Exception ex) { ex.printStackTrace(); } } public void viewAccepted(View v) { System.out.println("** View=" + v); member_size=v.size(); if(mainFrame != null) setTitle(); } public void channelConnected(Channel channel) { } public void channelDisconnected(Channel channel) { } public void channelClosed(Channel channel) { } public void channelShunned() { System.out.println("received EXIT, waiting for ChannelReconnected callback"); } public void channelReconnected(Address addr) { } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/QuoteClient.java0000644000175000017500000002134211366547366025615 0ustar twernertwerner// $Id: QuoteClient.java,v 1.11 2006/05/03 08:45:19 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Enumeration; import java.util.Hashtable; /** * Used in conjunction with QuoteServer: a client is member of a group of quote servers which replicate * stock quotes among themselves. The client broadcasts its request (set, get quotes) and (in the case of get * waits for the first reply received (usually the one from the quote server closest to it). The client * can get and set quotes as long as a minimum of 1 server (in the group) is running. * @author Bela Ban */ public class QuoteClient extends Frame implements WindowListener, ActionListener, MembershipListener { static final String channel_name="Quotes"; RpcDispatcher disp; Channel channel; final Button get=new Button("Get"); final Button set=new Button("Set"); final Button quit=new Button("Quit"); final Button get_all=new Button("All"); final Label stock=new Label("Stock"); final Label value=new Label("Value"); final Label err_msg=new Label("Error"); final TextField stock_field=new TextField(); final TextField value_field=new TextField(); final java.awt.List listbox=new java.awt.List(); final Font default_font=new Font("Helvetica", Font.PLAIN, 12); final String props=null; // default stack from JChannel public QuoteClient() { super(); try { channel=new JChannel(props); channel.setOpt(Channel.LOCAL, Boolean.FALSE); disp=new RpcDispatcher(channel, null, this, this); channel.connect(channel_name); } catch(Exception e) { System.err.println("QuoteClient(): " + e); } addWindowListener(this); } private void showMsg(String msg) { err_msg.setText(msg); err_msg.setVisible(true); } private void clearMsg() { err_msg.setVisible(false); } public void start() { setLayout(null); setSize(400, 300); setFont(default_font); stock.setBounds(new Rectangle(10, 30, 60, 30)); value.setBounds(new Rectangle(10, 60, 60, 30)); stock_field.setBounds(new Rectangle(100, 30, 100, 30)); value_field.setBounds(new Rectangle(100, 60, 100, 30)); listbox.setBounds(210, 30, 150, 160); err_msg.setBounds(new Rectangle(10, 200, 350, 30)); err_msg.setFont(new Font("Helvetica", Font.ITALIC, 12)); err_msg.setForeground(Color.red); err_msg.setVisible(false); get.setBounds(new Rectangle(10, 250, 80, 30)); set.setBounds(new Rectangle(100, 250, 80, 30)); quit.setBounds(new Rectangle(190, 250, 80, 30)); get_all.setBounds(new Rectangle(280, 250, 80, 30)); get.addActionListener(this); set.addActionListener(this); quit.addActionListener(this); get_all.addActionListener(this); add(stock); add(value); add(stock_field); add(value_field); add(err_msg); add(get); add(set); add(quit); add(get_all); add(listbox); // stock_field.requestFocus(); setVisible(true); } public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowClosing(WindowEvent e) { System.exit(0); } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); RspList rsp_list; Rsp first_rsp; try { if(command.equals("Get")) { String stock_name=stock_field.getText(); if(stock_name == null || stock_name.length() == 0) { showMsg("Stock name is empty !"); return; } showMsg("Looking up value for " + stock_name + ':'); rsp_list=disp.callRemoteMethods(null, "getQuote", new Object[]{stock_name}, new String[]{String.class.getName()}, GroupRequest.GET_ALL, 10000); Float val=null; for(int i=0; i < rsp_list.size(); i++) { Rsp rsp=(Rsp)rsp_list.elementAt(i); Object obj=rsp.getValue(); if(obj == null || obj instanceof Throwable) continue; val=(Float)obj; break; } if(val != null) { value_field.setText(val.toString()); clearMsg(); } else { value_field.setText(""); showMsg("Value for " + stock_name + " not found"); } } else if(command.equals("Set")) { String stock_name=stock_field.getText(); String stock_val=value_field.getText(); if(stock_name == null || stock_val == null || stock_name.length() == 0 || stock_val.length() == 0) { showMsg("Stock name and value have to be present to enter a new value"); return; } Float val=new Float(stock_val); disp.callRemoteMethods(null, "setQuote", new Object[]{stock_name, val}, new Class[]{String.class, Float.class}, GroupRequest.GET_FIRST, 0); showMsg("Stock " + stock_name + " set to " + val); } else if(command.equals("All")) { listbox.removeAll(); showMsg("Getting all stocks:"); rsp_list=disp.callRemoteMethods(null, "getAllStocks", (Object[])null, (Class[])null, GroupRequest.GET_ALL, 5000); System.out.println("rsp_list is " + rsp_list); Hashtable all_stocks=null; for(int i=0; i < rsp_list.size(); i++) { Rsp rsp=(Rsp)rsp_list.elementAt(i); Object obj=rsp.getValue(); if(obj == null || obj instanceof Throwable) continue; all_stocks=(Hashtable)obj; break; } if(all_stocks == null) { showMsg("No stocks found"); return; } clearMsg(); listbox.removeAll(); String key; Float val; for(Enumeration en=all_stocks.keys(); en.hasMoreElements();) { key=(String)en.nextElement(); val=(Float)all_stocks.get(key); if(val == null) continue; listbox.add(key + ": " + val.toString()); } } else if(command.equals("Quit")) { setVisible(false); channel.close(); System.exit(0); } else System.out.println("Unknown action"); } catch(Exception ex) { value_field.setText(""); ex.printStackTrace(); showMsg(ex.toString()); } } public void setQuote(String stock_name, Float value) { ; } public void printAllStocks() { } public void viewAccepted(View new_view) { setTitle("Members in " + channel_name + ": " + (new_view.size() - 1)); } public void suspect(Address suspected_mbr) { } public void block() { } public static void main(String args[]) { QuoteClient client=new QuoteClient(); client.start(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/ReplicatedHashMapDemo.java0000644000175000017500000002105311366547366027503 0ustar twernertwerner package org.jgroups.demos; import org.jgroups.*; import org.jgroups.blocks.ReplicatedHashMap; import org.jgroups.persistence.PersistenceFactory; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Map; import java.util.Vector; import java.io.Serializable; /** * Uses the ReplicatedHashMap building block, which subclasses java.util.HashMap and overrides * the methods that modify the hashmap (e.g. put()). Those methods are multicast to the group, whereas * read-only methods such as get() use the local copy. A ReplicatedtHashMap is created given the name * of a group; all hashmaps with the same name find each other and form a group. * @author Bela Ban * @version $Id: ReplicatedHashMapDemo.java,v 1.1 2007/07/23 09:27:51 belaban Exp $ */ public class ReplicatedHashMapDemo extends Frame implements WindowListener, ActionListener, ReplicatedHashMap.Notification { static final String groupname="HashMapDemo"; ReplicatedHashMap map=null; final JButton get=new JButton("Get"); final JButton set=new JButton("Set"); final JButton quit=new JButton("Quit"); final JButton get_all=new JButton("All"); final JButton delete=new JButton("Delete"); final JLabel stock=new JLabel("Key"); final JLabel value=new JLabel("Value"); final JLabel err_msg=new JLabel("Error"); final JTextField stock_field=new JTextField(); final JTextField value_field=new JTextField(); final List listbox=new List(); final Font default_font=new Font("Helvetica", Font.PLAIN,12); public ReplicatedHashMapDemo() { super(); addWindowListener(this); } private void showMsg(String msg) { err_msg.setText(msg); err_msg.setVisible(true); } private void clearMsg() {err_msg.setVisible(false);} private void removeItem() { int index=listbox.getSelectedIndex(); if(index == -1) { showMsg("No item selected in listbox to be deleted !"); return; } String s=listbox.getSelectedItem(); String key=s.substring(0, s.indexOf(':', 0)); if(key != null) map.remove(key); } private void showAll() { if(listbox.getItemCount() > 0) listbox.removeAll(); if(map.isEmpty()) return; clearMsg(); String key; Float val; for(Map.Entry entry: map.entrySet()) { key=entry.getKey(); val=entry.getValue(); if(val == null) continue; listbox.add(key + ": " + val.toString()); } } public void start(ChannelFactory factory, String props, boolean persist) throws ChannelException { map=new ReplicatedHashMap(groupname, factory, props, persist, 10000); map.addNotifier(this); setLayout(null); setSize(400, 300); setFont(default_font); stock.setBounds(new Rectangle(10, 30, 60, 30)); value.setBounds(new Rectangle(10, 60, 60, 30)); stock_field.setBounds(new Rectangle(100, 30, 100, 30)); value_field.setBounds(new Rectangle(100, 60, 100, 30)); listbox.setBounds(new Rectangle(210, 30, 150, 160)); err_msg.setBounds(new Rectangle(10, 200, 350, 30)); err_msg.setFont(new Font("Helvetica",Font.ITALIC,12)); err_msg.setForeground(Color.red); err_msg.setVisible(false); get.setBounds(new Rectangle(10, 250, 60, 30)); set.setBounds(new Rectangle(80, 250, 60, 30)); quit.setBounds(new Rectangle(150, 250, 60, 30)); get_all.setBounds(new Rectangle(220, 250, 60, 30)); delete.setBounds(new Rectangle(290, 250, 80, 30)); get.addActionListener(this); set.addActionListener(this); quit.addActionListener(this); get_all.addActionListener(this); delete.addActionListener(this); add(stock); add(value); add(stock_field); add(value_field); add(err_msg); add(get); add(set); add(quit); add(get_all); add(delete); add(listbox); _setTitle(); showAll(); setVisible(true); } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) {System.exit(0);} public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); try { if(command.equals("Get")) { String stock_name=stock_field.getText(); if(stock_name == null || stock_name.length() == 0) { showMsg("Key is empty !"); return; } showMsg("Looking up value for " + stock_name + ':'); Float val=map.get(stock_name); if(val != null) { value_field.setText(val.toString()); clearMsg(); } else { value_field.setText(""); showMsg("Value for " + stock_name + " not found"); } } else if(command.equals("Set")) { String stock_name=stock_field.getText(); String stock_val=value_field.getText(); if(stock_name == null || stock_val == null || stock_name.length() == 0 || stock_val.length() == 0) { showMsg("Both key and value have to be present to create a new entry"); return; } Float val=new Float(stock_val); map.put(stock_name, val); showMsg("Key " + stock_name + " set to " + val); } else if(command.equals("All")) { showAll(); } else if(command.equals("Quit")) { setVisible(false); System.exit(0); } else if(command.equals("Delete")) removeItem(); else System.out.println("Unknown action"); } catch(Exception ex) { value_field.setText(""); showMsg(ex.toString()); } } public void entrySet(Serializable key, Serializable value) { showAll(); } public void entryRemoved(Serializable key) { showAll(); } public void contentsSet(Map m) { System.out.println("new contents: " + m); } public void contentsCleared() { System.out.println("contents cleared"); } public void viewChange(View view, Vector new_mbrs, Vector old_mbrs) { System.out.println("** view: " + view); _setTitle(); } private void _setTitle() { int num=map.getChannel().getView().getMembers().size(); setTitle("ReplicatedHashMapDemo: " + num + " server(s)"); } public static void main(String args[]) { ReplicatedHashMapDemo client=new ReplicatedHashMapDemo(); ChannelFactory factory=new JChannelFactory(); String arg; boolean persist=false; String props="udp.xml"; try { for(int i=0; i < args.length; i++) { arg=args[i]; if("-persist".equals(arg) && i+1]"); } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/TotalTokenDemo.java0000644000175000017500000003760411366547366026262 0ustar twernertwerner//$Id: TotalTokenDemo.java,v 1.10.14.1 2007/11/20 08:53:41 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.Serializable; import java.util.Iterator; import java.util.Random; import java.util.Vector; /** *

* Demonstration of TOTAL_TOKEN protocol stack implementing total * order. TotalTokenDemo could however be used by any other * stack configuration which does not neccesarily have to satisfy * total order. If using stack configuration other than TOTAL_TOKEN * an appropriate xml configuration file should be used. See -help for * details. *

* TotalTokenDemo will verify : *

* total ordering of messages - by computing a color to be displayed * in a gui window. *

* virtual synchrony - by displaying number of messages transmitted * in last view. *

* *@author Vladimir Blagojevic vladimir@cs.yorku.ca *@author Ivan Bilenjkij ivan@ibossa.com *@version $Revision: 1.10.14.1 $ * *@see org.jgroups.protocols.TOTAL_TOKEN * **/ public class TotalTokenDemo extends JFrame implements Runnable { private JChannel channel; //main tabbed pane final JTabbedPane tabbedPane; private ReceiverThread receiverThread; private ColorPanel colorPanel; private final ControlPanel control; private int mSize = 1024; private volatile boolean transmitting = false; private final String channelProperties; private Dimension preffered; public TotalTokenDemo(String props) { super(); tabbedPane = new JTabbedPane(); control = new ControlPanel(); channelProperties = props; try { channel = new JChannel(channelProperties); } catch (ChannelException e) { System.err.println("Could not create channel, exiting ...."); e.printStackTrace(System.err); } addPanel("Control", control); getContentPane().add(tabbedPane); connect(); } public void addPanel(String name, JPanel panel) { if(tabbedPane.getTabCount() == 0) { preffered = panel.getPreferredSize(); } panel.setPreferredSize(preffered); tabbedPane.add(name,panel); } public JChannel getChannel() { return channel; } public void connect() { try { channel.connect("TOTAL_TOKEN_DEMO_GROUP"); } catch (ChannelException e) { e.printStackTrace(); } receiverThread = new ReceiverThread(); receiverThread.start(); Address a = channel.getLocalAddress(); if(a != null) setTitle(a.toString()); else setTitle("Not connected"); control.connected(); } public void run() { Random r = new Random(); while (true) { Util.sleep(10); try { if (transmitting) { channel.send(new Message(null, null, new TotalPayload(r.nextInt(255)))); } else { Util.sleep(200); } } catch (Exception e) { e.printStackTrace(); } } } public void disconnect() { transmitting = false; receiverThread.shutDown(); channel.disconnect(); control.disconnected(); setTitle("Not connected"); } private class ReceiverThread extends Thread { volatile boolean running = true; Thread nullifier = null; private long startTimeThroughput = System.currentTimeMillis(); private final long oneSecond = 1000; private long throughput = 1; public ReceiverThread() { nullifier = new Thread(new Runnable() { public void run() { //nullifies throughput display while (running) { Util.sleep(2000); if ((System.currentTimeMillis() - startTimeThroughput) > 2000) { control.throughput.setText("0 KB/sec"); } } } }); nullifier.start(); } public void shutDown() { running = false; } private void measureThrougput(long size) { if ((System.currentTimeMillis() - startTimeThroughput) > oneSecond) { control.throughput.setText("" + (throughput / 1024) + " KB/sec"); startTimeThroughput = System.currentTimeMillis(); throughput = 0; } else { throughput += size; } } public void run() { Object tmp; Message msg = null; int counter = 0; Vector v = new Vector(); while (running) { Util.sleep(10); try { tmp = channel.receive(0); if (tmp == null) continue; if (tmp instanceof View) { View vw = (View) tmp; control.viewNumber.setText("" + vw.getVid().getId()); control.numMessagesInLastView.setText("" + counter); counter = 0; v.clear(); continue; } if (tmp instanceof ExitEvent) { System.out.println("received EXIT"); break; } if (!(tmp instanceof Message)) continue; msg = (Message) tmp; measureThrougput(msg.size()); TotalPayload p =null; p=(TotalPayload)msg.getObject(); v.addElement(new Integer(p.getRandomSequence())); int size = v.size(); if (size % 50 == 0) { int value = 0; int i = 0; Iterator iter = v.iterator(); while (iter.hasNext()) { i++; int seq = ((Integer) iter.next()).intValue(); if (i % 2 == 0) { value *= seq; } else if (i % 3 == 0) { value -= seq; } else value += seq; } v.clear(); value = Math.abs(value); int r = value % 85; int g = value % 170; int b = value % 255; colorPanel.setSeq(r, g, b); } counter++; } catch (ChannelNotConnectedException e) { e.printStackTrace(); } catch (ChannelClosedException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } } } public static class TotalPayload implements Serializable { private final int seqRandom; public TotalPayload(int random) { seqRandom = random; } public int getRandomSequence() { return seqRandom; } } class TransmitAction extends AbstractAction { private static final String TRANSMIT_OFF = "transmit off"; private static final String TRANSMIT_ON = "transmit on"; TransmitAction() { putValue(NAME, TRANSMIT_OFF); } public void actionPerformed(ActionEvent e) { if (getValue(NAME) == TRANSMIT_OFF) { putValue(NAME, TRANSMIT_ON); transmitting = true; } else { putValue(NAME, TRANSMIT_OFF); transmitting = false; } } } class ControlPanel extends JPanel { private static final String DISCONNECT = "Disconnect"; private static final String CONNECT = "Connect"; final JTextField numMessagesInLastView; final JTextField throughput; final JTextField viewNumber; final JTextField messageSize; final JTextField state; final JButton transmit; final JButton connectButton; JTabbedPane pane; public ControlPanel() { super(); //Layout the labels in a panel JPanel labelPane = new JPanel(); labelPane.setLayout(new GridLayout(0, 1)); labelPane.add(new JLabel("Message size")); labelPane.add(new JLabel("Current view")); labelPane.add(new JLabel("Throughput")); labelPane.add(new JLabel("Last view count")); colorPanel = new ColorPanel(); connectButton = new JButton(DISCONNECT); connectButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JButton b = (JButton) e.getSource(); String current_state = b.getText(); if (CONNECT.equals(current_state)) { connect(); } else if (DISCONNECT.equals(current_state)) { disconnect(); } } }); transmit = new JButton(new TransmitAction()); labelPane.add(connectButton); labelPane.add(transmit); int size = 10; messageSize = new JTextField(size); messageSize.setText("" + mSize); messageSize.addActionListener(new ActionListener() { /** * Invoked when an action occurs. */ public void actionPerformed(ActionEvent e) { mSize = Integer.parseInt(messageSize.getText()); } }); viewNumber = new JTextField(size); viewNumber.setEditable(false); throughput = new JTextField(size); throughput.setEditable(false); numMessagesInLastView = new JTextField(size); numMessagesInLastView.setEditable(false); state = new JTextField(size); state.setEditable(false); //Layout the text fields in a panel JPanel fieldPane = new JPanel(); fieldPane.setLayout(new GridLayout(0, 1)); fieldPane.add(messageSize); fieldPane.add(viewNumber); fieldPane.add(throughput); fieldPane.add(numMessagesInLastView); fieldPane.add(state); fieldPane.add(colorPanel); //Put the panels in another panel, labels on left, //text fields on right JPanel contentPane = new JPanel(); contentPane.setBorder(BorderFactory.createTitledBorder("Control")); contentPane.setLayout(new BorderLayout()); contentPane.add(labelPane, BorderLayout.CENTER); contentPane.add(fieldPane, BorderLayout.EAST); this.setLayout(new BorderLayout()); this.add(contentPane); } public void connected() { connectButton.setText(DISCONNECT); state.setText("connected ok"); } public void disconnected() { connectButton.setText(CONNECT); state.setText("disconnected ok"); } } class ColorPanel extends JPanel { long seq1,seq2,seq3; public ColorPanel() { super(); setOpaque(false); this.setLayout(new BorderLayout()); //setBorder(BorderFactory.createLineBorder(Color.black)); } public Dimension getPreferredSize() { Dimension layoutSize = super.getPreferredSize(); int max = Math.max(layoutSize.width, layoutSize.height); return new Dimension(max + 20, max + 20); } public void setSeq(long seq1, long seq2, long seq3) { this.seq1 = seq1; this.seq2 = seq2; this.seq3 = seq3; this.repaint(); } protected void paintComponent(Graphics g) { Dimension size = this.getSize(); int x = 0; int y = 0; g.setColor(new Color((int) seq1, (int) seq2, (int) seq3)); g.fillRect(x, y, size.width, size.height); } } class StackPanel extends JPanel { final ProtocolStack stack; public StackPanel(JChannel channel) { super(); setBorder(BorderFactory.createTitledBorder("ProtocolStack")); this.setLayout(new GridLayout(0, 2)); this.stack = channel.getProtocolStack(); Iterator iter = stack.getProtocols().iterator(); String debugLevels [] = new String[]{"DEBUG","INFO","ERROR"}; while (iter.hasNext()) { Protocol p = (Protocol) iter.next(); JLabel field = new JLabel(p.getName()); JComboBox pane = new JComboBox(debugLevels); this.add(field); this.add(pane); } } } static void help() { System.out.println("\nTotalTokenDemo [-help] [-props ]"); System.out.println("-props: argument can be an old-style protocol stack specification, or it can be " + "a URL. In the latter case, the protocol specification will be read from the URL\n"); } public static void main(String args[]) { String props = null; for (int i = 0; i < args.length; i++) { if ("-help".equals(args[i])) { help(); return; } if ("-props".equals(args[i])) { props = args[++i]; continue; } help(); return; } if (props == null) { props = "UDP(mcast_addr=224.0.0.35;mcast_port=45566;ip_ttl=32;" + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" + "PING(timeout=2000;num_initial_members=5):" + "FD_SOCK:" + "VERIFY_SUSPECT(timeout=1500):" + "UNICAST(timeout=5000):" + "FRAG(frag_size=8192;down_thread=false;up_thread=false):" + "TOTAL_TOKEN(block_sending=50;unblock_sending=10):" + "pbcast.GMS(join_timeout=5000;" + "shun=false;print_local_addr=true)"; } TotalTokenDemo ttd = new TotalTokenDemo(props); //StackPanel not_done_yet = new StackPanel(ttd.getChannel()); //ttd.addPanel("Debug", not_done_yet); ttd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); ttd.pack(); ttd.show(); new Thread(ttd).start(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/Draw.java0000644000175000017500000004453211366547366024264 0ustar twernertwerner// $Id: Draw.java,v 1.51.2.1 2009/09/17 13:06:53 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.util.Util; import javax.management.MBeanServer; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; /** * Shared whiteboard, each new instance joins the same group. Each instance chooses a random color, * mouse moves are broadcast to all group members, which then apply them to their canvas

* @author Bela Ban, Oct 17 2001 */ public class Draw extends ExtendedReceiverAdapter implements ActionListener, ChannelListener { String groupname="DrawGroupDemo"; private Channel channel=null; private int member_size=1; static final boolean first=true; private JFrame mainFrame=null; private JPanel sub_panel=null; private DrawPanel panel=null; private JButton clear_button, leave_button; private final Random random=new Random(System.currentTimeMillis()); private final Font default_font=new Font("Helvetica",Font.PLAIN,12); private final Color draw_color=selectColor(); private static final Color background_color=Color.white; boolean no_channel=false; boolean jmx=true; private boolean use_state=false; private long state_timeout=5000; public Draw(String props, boolean no_channel, boolean jmx, boolean use_state, long state_timeout, boolean use_blocking) throws Exception { this.no_channel=no_channel; this.jmx=jmx; this.use_state=use_state; this.state_timeout=state_timeout; if(no_channel) return; channel=new JChannel(props); if(use_blocking) channel.setOpt(Channel.BLOCK, Boolean.TRUE); channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); channel.setReceiver(this); channel.addChannelListener(this); } public Draw(Channel channel) throws Exception { this.channel=channel; channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); channel.setReceiver(this); channel.addChannelListener(this); } public Draw(Channel channel, boolean use_state, long state_timeout) throws Exception { this.channel=channel; channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); channel.setReceiver(this); channel.addChannelListener(this); this.use_state=use_state; this.state_timeout=state_timeout; } public String getGroupName() { return groupname; } public void setGroupName(String groupname) { if(groupname != null) this.groupname=groupname; } public static void main(String[] args) { Draw draw=null; String props=null; boolean no_channel=false; boolean jmx=true; boolean use_state=false; boolean use_blocking=false; String group_name=null; long state_timeout=5000; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-props".equals(args[i])) { props=args[++i]; continue; } if("-no_channel".equals(args[i])) { no_channel=true; continue; } if("-jmx".equals(args[i])) { jmx=true; continue; } if("-groupname".equals(args[i])) { group_name=args[++i]; continue; } if("-state".equals(args[i])) { use_state=true; continue; } if("-use_blocking".equals(args[i])) { use_blocking=true; continue; } if("-timeout".equals(args[i])) { state_timeout=Long.parseLong(args[++i]); continue; } if("-bind_addr".equals(args[i])) { System.setProperty("jgroups.bind_addr", args[++i]); continue; } help(); return; } try { draw=new Draw(props, no_channel, jmx, use_state, state_timeout, use_blocking); if(group_name != null) draw.setGroupName(group_name); draw.go(); } catch(Throwable e) { e.printStackTrace(); System.exit(0); } } static void help() { System.out.println("\nDraw [-help] [-no_channel] [-props ]" + " [-groupname ] [-state] [-use_blocking] [-timeout ] [-bind_addr ]"); System.out.println("-no_channel: doesn't use JGroups at all, any drawing will be relected on the " + "whiteboard directly"); System.out.println("-props: argument can be an old-style protocol stack specification, or it can be " + "a URL. In the latter case, the protocol specification will be read from the URL\n"); } private Color selectColor() { int red=(Math.abs(random.nextInt()) % 255); int green=(Math.abs(random.nextInt()) % 255); int blue=(Math.abs(random.nextInt()) % 255); return new Color(red, green, blue); } public void go() throws Exception { if(!no_channel && !use_state) { channel.connect(groupname); if(jmx) { MBeanServer server=Util.getMBeanServer(); if(server == null) throw new Exception("No MBeanServers found;" + "\nDraw needs to be run with an MBeanServer present, or inside JDK 5"); JmxConfigurator.registerChannel((JChannel)channel, server, "jgroups", channel.getClusterName(), true); } } mainFrame=new JFrame(); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); panel=new DrawPanel(use_state); panel.setBackground(background_color); sub_panel=new JPanel(); mainFrame.getContentPane().add("Center", panel); clear_button=new JButton("Clear"); clear_button.setFont(default_font); clear_button.addActionListener(this); leave_button=new JButton("Leave"); leave_button.setFont(default_font); leave_button.addActionListener(this); sub_panel.add("South", clear_button); sub_panel.add("South", leave_button); mainFrame.getContentPane().add("South", sub_panel); mainFrame.setBackground(background_color); clear_button.setForeground(Color.blue); leave_button.setForeground(Color.blue); mainFrame.pack(); mainFrame.setLocation(15, 25); mainFrame.setBounds(new Rectangle(250, 250)); if(!no_channel && use_state) { channel.connect(groupname,null,null, state_timeout); } mainFrame.setVisible(true); setTitle(); } void setTitle(String title) { String tmp=""; if(no_channel) { mainFrame.setTitle(" Draw Demo "); return; } if(title != null) { mainFrame.setTitle(title); } else { if(channel.getLocalAddress() != null) tmp+=channel.getLocalAddress(); tmp+=" (" + member_size + ")"; mainFrame.setTitle(tmp); } } void setTitle() { setTitle(null); } public void receive(Message msg) { byte[] buf=msg.getRawBuffer(); if(buf == null) { System.err.println("[" + channel.getLocalAddress() + "] received null buffer from " + msg.getSrc() + ", headers: " + msg.printHeaders()); return; } try { DrawCommand comm=(DrawCommand)Util.streamableFromByteBuffer(DrawCommand.class, buf, msg.getOffset(), msg.getLength()); switch(comm.mode) { case DrawCommand.DRAW: if(panel != null) panel.drawPoint(comm); break; case DrawCommand.CLEAR: clearPanel(); break; default: System.err.println("***** received invalid draw command " + comm.mode); break; } } catch(Exception e) { e.printStackTrace(); } } public void viewAccepted(View v) { if(v instanceof MergeView) System.out.println("** MergeView=" + v); else System.out.println("** View=" + v); member_size=v.size(); if(mainFrame != null) setTitle(); } public void block() { System.out.println("-- received BlockEvent"); } public void unblock() { System.out.println("-- received UnblockEvent"); } public byte[] getState() { return panel.getState(); } public void setState(byte[] state) { panel.setState(state); } public void getState(OutputStream ostream) { try { try { panel.writeState(ostream); } catch(IOException e) { e.printStackTrace(); } } finally { Util.close(ostream); } } public void setState(InputStream istream) { try { try { panel.readState(istream); } catch(IOException e) { e.printStackTrace(); } } finally { Util.close(istream); } } /* --------------- Callbacks --------------- */ public void clearPanel() { if(panel != null) panel.clear(); } public void sendClearPanelMsg() { DrawCommand comm=new DrawCommand(DrawCommand.CLEAR); try { byte[] buf=Util.streamableToByteBuffer(comm); channel.send(new Message(null, null, buf)); } catch(Exception ex) { System.err.println(ex); } } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if("Clear".equals(command)) { if(no_channel) { clearPanel(); return; } sendClearPanelMsg(); } else if("Leave".equals(command)) { stop(); } else System.out.println("Unknown action"); } public void stop() { if(!no_channel) { try { channel.close(); } catch(Exception ex) { System.err.println(ex); } } mainFrame.setVisible(false); mainFrame.dispose(); } /* ------------------------------ ChannelListener interface -------------------------- */ public void channelConnected(Channel channel) { } public void channelDisconnected(Channel channel) { } public void channelClosed(Channel channel) { } public void channelShunned() { System.out.println("-- received EXIT, waiting for ChannelReconnected callback"); setTitle(" Draw Demo - shunned "); } public void channelReconnected(Address addr) { setTitle(); } /* --------------------------- End of ChannelListener interface ---------------------- */ private class DrawPanel extends JPanel implements MouseMotionListener { final Dimension preferred_size=new Dimension(235, 170); Image img=null; // for drawing pixels Dimension d, imgsize=null; Graphics gr=null; final Map state; public DrawPanel(boolean use_state) { if(use_state) state=new LinkedHashMap(); else state=null; createOffscreenImage(false); addMouseMotionListener(this); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { if(getWidth() <= 0 || getHeight() <= 0) return; createOffscreenImage(false); } }); } public byte[] getState() { byte[] retval=null; if(state == null) return null; synchronized(state) { try { retval=Util.objectToByteBuffer(state); } catch(Exception e) { e.printStackTrace(); } } return retval; } public void setState(byte[] buf) { synchronized(state) { try { Map tmp=(Map)Util.objectFromByteBuffer(buf); state.clear(); state.putAll(tmp); System.out.println("received state: " + buf.length + " bytes, " + state.size() + " entries"); createOffscreenImage(true); } catch(Exception e) { e.printStackTrace(); } } } public void writeState(OutputStream outstream) throws IOException { synchronized(state) { if(state != null) { DataOutputStream dos=new DataOutputStream(outstream); dos.writeInt(state.size()); Point point; Color col; for(Map.Entry entry: state.entrySet()) { point=entry.getKey(); col=entry.getValue(); dos.writeInt(point.x); dos.writeInt(point.y); dos.writeInt(col.getRGB()); } dos.flush(); } } } public void readState(InputStream instream) throws IOException { DataInputStream in=new DataInputStream(instream); Map new_state=new HashMap(); int num=in.readInt(); Point point; Color col; for(int i=0; i < num; i++) { point=new Point(in.readInt(), in.readInt()); col=new Color(in.readInt()); new_state.put(point, col); } synchronized(state) { state.clear(); state.putAll(new_state); System.out.println("read state: " + state.size() + " entries"); createOffscreenImage(true); } } final void createOffscreenImage(boolean discard_image) { d=getSize(); if(discard_image) { img=null; imgsize=null; } if(img == null || imgsize == null || imgsize.width != d.width || imgsize.height != d.height) { img=createImage(d.width, d.height); if(img != null) { gr=img.getGraphics(); if(gr != null && state != null) { drawState(); } } imgsize=d; } repaint(); } /* ---------------------- MouseMotionListener interface------------------------- */ public void mouseMoved(MouseEvent e) {} public void mouseDragged(MouseEvent e) { int x=e.getX(), y=e.getY(); DrawCommand comm=new DrawCommand(DrawCommand.DRAW, x, y, draw_color.getRed(), draw_color.getGreen(), draw_color.getBlue()); if(no_channel) { drawPoint(comm); return; } try { byte[] buf=Util.streamableToByteBuffer(comm); channel.send(new Message(null, null, buf)); // Thread.yield(); } catch(Exception ex) { System.err.println(ex); } } /* ------------------- End of MouseMotionListener interface --------------------- */ /** * Adds pixel to queue and calls repaint() whenever we have MAX_ITEMS pixels in the queue * or when MAX_TIME msecs have elapsed (whichever comes first). The advantage compared to just calling * repaint() after adding a pixel to the queue is that repaint() can most often draw multiple points * at the same time. */ public void drawPoint(DrawCommand c) { if(c == null || gr == null) return; Color col=new Color(c.r, c.g, c.b); gr.setColor(col); gr.fillOval(c.x, c.y, 10, 10); repaint(); if(state != null) { synchronized(state) { state.put(new Point(c.x, c.y), col); } } } public void clear() { if(gr == null) return; gr.clearRect(0, 0, getSize().width, getSize().height); repaint(); if(state != null) { synchronized(state) { state.clear(); } } } /** Draw the entire panel from the state */ public void drawState() { // clear(); Map.Entry entry; Point pt; Color col; synchronized(state) { for(Iterator it=state.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); pt=(Point)entry.getKey(); col=(Color)entry.getValue(); gr.setColor(col); gr.fillOval(pt.x, pt.y, 10, 10); } } repaint(); } public Dimension getPreferredSize() { return preferred_size; } public void paintComponent(Graphics g) { super.paintComponent(g); if(img != null) { g.drawImage(img, 0, 0, null); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/ViewDemo.java0000644000175000017500000000452111366547366025100 0ustar twernertwerner// $Id: ViewDemo.java,v 1.14 2007/09/21 07:15:05 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.util.Util; import java.util.HashMap; import java.util.Map; /** * Demos the reception of views using a PullPushAdapter. Just start a number of members, and kill them * randomly. The view should always be correct. */ public class ViewDemo extends ReceiverAdapter { private Channel channel; public void viewAccepted(View new_view) { System.out.println("** New view: " + new_view); } /** * Called when a member is suspected */ public void suspect(Address suspected_mbr) { System.out.println("Suspected(" + suspected_mbr + ')'); } public void start(String props, boolean use_additional_data) throws Exception { channel=new JChannel(props); channel.setReceiver(this); channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); if(use_additional_data) { Map m=new HashMap(); m.put("additional_data", "bela".getBytes()); channel.down(new Event(Event.CONFIG, m)); } channel.connect("ViewDemo"); while(true) { Util.sleep(10000); } } public static void main(String args[]) { ViewDemo t=new ViewDemo(); boolean use_additional_data=false; String props="udp.xml"; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-props".equals(args[i])) { props=args[++i]; continue; } if("-use_additional_data".equals(args[i])) { use_additional_data=Boolean.valueOf(args[++i]).booleanValue(); continue; } if("-bind_addr".equals(args[i])) { System.setProperty("jgroups.bind_addr", args[++i]); continue; } help(); return; } try { t.start(props, use_additional_data); } catch(Exception e) { System.err.println(e); } } static void help() { System.out.println("ViewDemo [-props ] [-help] [-use_additional_data ] [-bind_addr

]"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/Chat.java0000644000175000017500000000557311366547366024250 0ustar twernertwernerpackage org.jgroups.demos; import java.awt.Frame; import java.awt.Label; import java.awt.Rectangle; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.JButton; public class Chat extends ChatCore implements MouseListener, WindowListener { Frame mainFrame; TextArea txtArea; TextField txtField; Label csLabel; JButton leaveButton; JButton sendButton; JButton clearButton; public Chat(String props) { super(props); } public static void main(String[] args) { String props = null; for (int i = 0; i < args.length; i++) { if ("-props".equals(args[i])) { props = args[++i]; continue; } help(); return; } Chat instance = new Chat(props); instance.start(); } void post(String msg) { txtArea.append(msg); } static void help() { System.out.println("Chat [-help] [-props ]"); } public void start() { mainFrame = new Frame(); mainFrame.setLayout(null); mainFrame.setSize(600, 507); mainFrame.addWindowListener(this); txtArea = new TextArea(); txtArea.setBounds(12, 36, 550, 348); txtArea.setEditable(false); mainFrame.add(txtArea); txtField = new TextField(); txtField.setBounds(100, 392, 400, 30); mainFrame.add(txtField); csLabel = new Label("Send:"); csLabel.setBounds(12, 392, 85, 30); mainFrame.add(csLabel); leaveButton = new JButton("Leave"); leaveButton.setBounds(12, 428, 150, 30); leaveButton.addMouseListener(this); mainFrame.add(leaveButton); sendButton = new JButton("Send"); sendButton.setBounds(182, 428, 150, 30); sendButton.addMouseListener(this); mainFrame.add(sendButton); clearButton = new JButton("Clear"); clearButton.setBounds(340, 428, 150, 30); clearButton.addMouseListener(this); mainFrame.add(clearButton); link(); mainFrame.pack(); mainFrame.setLocation(15, 25); mainFrame.setBounds(new Rectangle(580, 480)); mainFrame.setVisible(true); mainFrame.show(); dumpHist(); } public void mouseClicked(MouseEvent e) { Object obj = e.getSource(); if (obj == leaveButton) { handleLeave(); System.exit(0); } else if (obj == sendButton) handleSend(txtField.getText()); else if (obj == clearButton) txtArea.setText(""); } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowClosing(WindowEvent e) { handleLeave(); System.exit(0); } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/package.html0000644000175000017500000000012311366547366024771 0ustar twernertwerner Provides demonstrations of JGroups functionality. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/CausalDemo.java0000644000175000017500000001233511366547366025400 0ustar twernertwerner// $Id: CausalDemo.java,v 1.6.14.1 2007/11/20 08:53:42 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.Serializable; import java.util.Random; import java.util.Vector; /** * Simple causal demo where each member bcast a consecutive letter from the * alphabet and picks the next member to transmit the next letter. Start a * few instances of CausalDemo and pass a paramter "-start" to a CausalDemo * that initiates transmission of a letter A. All participanting members should * have correct alphabet. DISCARD layer has been added to simulate lost messages, * thus forcing delaying of delivery of a certain alphabet letter until the causally * prior one has been received. Remove CAUSAL from the stack and witness how FIFO * alone doesn't provide this guarantee. * * @author Vladimir Blagojevic */ public class CausalDemo implements Runnable { private Channel channel; private final Vector alphabet = new Vector(); private boolean starter = false; private int doneCount=0; private Log log=LogFactory.getLog(getClass()); private final String props = "causal.xml"; public CausalDemo(boolean start) { starter = start; } public String getNext(String c) { char letter = c.charAt(0); return new String(new char[]{++letter}); } public void listAlphabet() { System.out.println(alphabet); } public void run() { Object obj; Message msg; Random r = new Random(); try { channel = new JChannel(props); channel.connect("CausalGroup"); System.out.println("View:" + channel.getView()); if (starter) channel.send(new Message(null, null, new CausalMessage("A", (Address) channel.getView().getMembers().get(0)))); } catch (Exception e) { System.out.println("Could not conec to channel"); } try { Runtime.getRuntime().addShutdownHook( new Thread("Shutdown cleanup thread") { public void run() { listAlphabet(); channel.disconnect(); channel.close(); } } ); } catch (Exception e) { System.out.println("Exception while shutting down" + e); } while (true) { try { CausalMessage cm = null; obj = channel.receive(0); // no timeout if (obj instanceof Message) { msg = (Message) obj; cm = (CausalMessage) msg.getObject(); Vector members = channel.getView().getMembers(); String receivedLetter = cm.message; if("Z".equals(receivedLetter)) { channel.send(new Message(null, null, new CausalMessage("done", null))); } if("done".equals(receivedLetter)) { if(++doneCount >= members.size()) { System.exit(0); } continue; } alphabet.add(receivedLetter); listAlphabet(); //am I chosen to transmit next letter? if (cm.member.equals(channel.getLocalAddress())) { int nextTarget = r.nextInt(members.size()); //chose someone other than yourself while (nextTarget == members.indexOf(channel.getLocalAddress())) { nextTarget = r.nextInt(members.size()); } Address next = (Address) members.get(nextTarget); String nextChar = getNext(receivedLetter); if (nextChar.compareTo("Z") < 1) { System.out.println("Sending " + nextChar); channel.send(new Message(null, null, new CausalMessage(nextChar, next))); } } } } catch (ChannelNotConnectedException conn) { break; } catch (Exception e) { log.error(e); } } } public static void main(String args[]) { CausalDemo test = null; boolean start=false; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { System.out.println("CausalDemo [-help] [-start]"); return; } if("-start".equals(args[i])) { start=true; continue; } } //if parameter start is passed , start the demo test = new CausalDemo(start); try { new Thread(test).start(); } catch (Exception e) { System.err.println(e); } } } class CausalMessage implements Serializable { public final String message; public final Address member; public CausalMessage(String message, Address member) { this.message = message; this.member = member; } public String toString() { return "CausalMessage[" + message + '=' + message + "member=" + member + ']'; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/DrawMultiplexer.java0000644000175000017500000000302111366547366026503 0ustar twernertwernerpackage org.jgroups.demos; import org.jgroups.Channel; import org.jgroups.JChannelFactory; /** * @author Bela Ban * @version $Id: DrawMultiplexer.java,v 1.8 2007/09/14 22:44:50 vlada Exp $ */ public class DrawMultiplexer { JChannelFactory factory; public static void main(String[] args) throws Exception { String props="stacks.xml"; String stack_name="udp"; boolean state=false; for(int i=0; i < args.length; i++) { String arg=args[i]; if(arg.equals("-props")) { props=args[++i]; continue; } if(arg.equals("-stack_name")) { stack_name=args[++i]; continue; } if(arg.equals("-state")) { state=true; continue; } System.out.println("DrawMultiplexer [-help] [-props ] [-stack_name ] [-state]"); return; } new DrawMultiplexer().start(props, stack_name, state); } private void start(String props, String stack_name, boolean state) throws Exception { factory=new JChannelFactory(); factory.setMultiplexerConfig(props); final Channel ch1, ch2; ch1=factory.createMultiplexerChannel(stack_name, "id-1"); Draw draw1=new Draw(ch1, state, 5000); ch2=factory.createMultiplexerChannel(stack_name, "id-2"); Draw draw2=new Draw(ch2, state, 5000); draw1.go(); draw2.go(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/Topology.java0000644000175000017500000001423311366547366025176 0ustar twernertwerner// $Id: Topology.java,v 1.7.14.1 2007/11/20 08:53:41 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.blocks.PullPushAdapter; import java.awt.*; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Vector; /** * Demonstrates the membership service. Each member is represented by a rectangle that contains the * addresses of all the members. The coordinator (= oldest member in the group) is painted in blue. * New members can be started; all existing members will update their graphical appearance to reflect * the new membership. When the coordinator itself is killed, another one will take over (the next in rank).

* A nice demo is to start a number of Topology instances at the same time. All of them will be blue (all are * coordinators since they don't find each other). Then the MERGE2 protocol sets in and only one will retain * its coordinator role. * @todo Needs to be ported to Swing. * @author Bela Ban */ public class Topology extends Frame implements WindowListener, MembershipListener { private final Vector members=new Vector(); private final Font myFont; private final FontMetrics fm; private final Color node_color=new Color(250, 220, 100); private boolean coordinator=false; private static final int NormalStyle=0; private static final int CheckStyle=1; private Channel channel; private Object my_addr=null; private static final String channel_name="FD-Heartbeat"; public Topology() { addWindowListener(this); //g=getGraphics(); fm=getFontMetrics(new Font("Helvetica", Font.PLAIN, 12)); myFont=new Font("Helvetica", Font.PLAIN, 12); } public void addNode(Object member) { Object tmp; for(int i=0; i < members.size(); i++) { tmp=members.elementAt(i); if(member.equals(tmp)) return; } members.addElement(member); repaint(); } public void removeNode(Object member) { Object tmp; for(int i=0; i < members.size(); i++) { tmp=members.elementAt(i); if(member.equals(tmp)) { members.removeElement(members.elementAt(i)); break; } } repaint(); } public void drawNode(Graphics g, int x, int y, String label, int style) { Color old=g.getColor(); int width, height; width=fm.stringWidth(label) + 10; height=fm.getHeight() + 5; g.setColor(node_color); g.fillRect(x, y, width, height); g.setColor(old); g.drawString(label, x + 5, y + 15); g.drawRoundRect(x - 1, y - 1, width + 1, height + 1, 10, 10); if(style == CheckStyle) { g.drawRoundRect(x - 2, y - 2, width + 2, height + 2, 10, 10); g.drawRoundRect(x - 3, y - 3, width + 3, height + 3, 10, 10); } } public void drawTopology(Graphics g) { int x=20, y=50; String label; Dimension box=getSize(); Color old=g.getColor(); if(coordinator) { g.setColor(Color.cyan); g.fillRect(11, 31, box.width - 21, box.height - 61); g.setColor(old); } g.drawRect(10, 30, box.width - 20, box.height - 60); g.setFont(myFont); for(int i=0; i < members.size(); i++) { label=members.elementAt(i).toString(); drawNode(g, x, y, label, NormalStyle); y+=50; } } public void paint(Graphics g) { drawTopology(g); } /* ------------ Callbacks ------------- */ public void viewAccepted(View view) { setState(view.getMembers()); } public void suspect(Address suspected_mbr) { } public void block() { } public void setState(Vector mbrs) { members.removeAllElements(); for(int i=0; i < mbrs.size(); i++) addNode(mbrs.elementAt(i)); if(mbrs.size() <= 1 || (mbrs.size() > 1 && mbrs.elementAt(0).equals(my_addr))) coordinator=true; else coordinator=false; repaint(); } public void coordinatorChosen() { coordinator=true; repaint(); } public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowClosing(WindowEvent e) { channel.close(); System.exit(0); } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void start() throws Exception { //String props="TCP:" + // "TCPPING(timeout=2000;num_initial_members=1;port_range=3;" + // "initial_hosts=localhost[8880]):" + // "FD:STABLE:NAKACK:FLUSH:GMS(join_timeout=12000):VIEW_ENFORCER:QUEUE"; // test for pbcast //String props="UDP:PING:FD:" + // "pbcast.PBCAST:UNICAST:FRAG:pbcast.GMS:" + // "STATE_TRANSFER:QUEUE"; // test for pbcast //String props="TCP:TCPPING(port_range=2;initial_hosts=daddy[8880],terrapin[8880]):FD:" + // "pbcast.PBCAST:UNICAST:FRAG:pbcast.GMS:" + // "STATE_TRANSFER:QUEUE"; // test2 for pbcast //String props="UDP:PING:FD:" + // "pbcast.PBCAST:UNICAST:FRAG:pbcast.GMS:" + // "pbcast.STATE_TRANSFER"; // String props=null; // default properties String props="udp.xml"; channel=new JChannel(props); channel.connect(channel_name); new PullPushAdapter(channel, this); my_addr=channel.getLocalAddress(); if(my_addr != null) setTitle(my_addr.toString()); pack(); show(); } public static void main(String[] args) { try { Topology top=new Topology(); top.setLayout(null); top.setSize(240, 507); top.start(); } catch(Exception e) { System.err.println(e); e.printStackTrace(); System.exit(0); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/NotificationBusDemo.java0000644000175000017500000000634011366547366027267 0ustar twernertwerner// $Id: NotificationBusDemo.java,v 1.7.10.1 2007/11/20 08:53:41 belaban Exp $ package org.jgroups.demos; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.blocks.NotificationBus; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.Serializable; import java.util.Vector; /** * Demoes the NotificationBus (without caching). Start a number of members and type in messages. All members will * receive the messages. View changes will also be displayed (e.g. member joined, left). * @author Bela Ban */ public class NotificationBusDemo implements NotificationBus.Consumer { NotificationBus bus=null; BufferedReader in=null; String line; final long timeout=0; final Vector cache=null; Log log=LogFactory.getLog(getClass()); public void start(String bus_name, String props) { try { bus=new NotificationBus(bus_name, props); bus.setConsumer(this); bus.start(); //System.out.println("Getting the cache from coordinator:"); //cache=(Vector)bus.getCacheFromCoordinator(3000, 3); //if(cache == null) cache=new Vector(); //System.out.println("cache is " + cache); in=new BufferedReader(new InputStreamReader(System.in)); while(true) { try { System.out.print("> "); System.out.flush(); line=in.readLine(); if(line.startsWith("quit") || line.startsWith("exit")) { bus.stop(); bus=null; break; } bus.sendNotification(line); } catch(Exception e) { log.error(e); } } } catch(Exception ex) { log.error(ex); } finally { if(bus != null) bus.stop(); } } public void handleNotification(Serializable n) { System.out.println("** Received notification: " + n); //if(cache != null) // cache.addElement(n); //System.out.println("cache is " + cache); } public Serializable getCache() { // return cache; return null; } public void memberJoined(Address mbr) { System.out.println("** Member joined: " + mbr); } public void memberLeft(Address mbr) { System.out.println("** Member left: " + mbr); } public static void main(String[] args) { String name="BusDemo"; String props="udp.xml"; for(int i=0; i < args.length; i++) { if("-bus_name".equals(args[i])) { name=args[++i]; continue; } if("-props".equals(args[i])) { props=args[++i]; continue; } System.out.println("NotificationBusDemo [-help] [-bus_name ] " + "[-props ]"); return; } System.out.println("Starting NotificationBus with name " + name); new NotificationBusDemo().start(name, props); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/0000755000175000017500000000000011621261110023072 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/package.html0000644000175000017500000000013411366547366025403 0ustar twernertwerner A distributed whiteboard applet implemented using JGroups. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/Node.java0000644000175000017500000000110511366547366024651 0ustar twernertwerner// $Id: Node.java,v 1.2.24.1 2008/01/22 10:01:26 belaban Exp $ package org.jgroups.demos.wb; import org.jgroups.Address; public class Node implements java.io.Serializable { public double x, y, dx, dy; public boolean fixed; public String lbl=null; public Address addr=null; public int xloc=0, yloc=0; public int width=0; public int height=0; public String toString() { StringBuilder ret=new StringBuilder(); ret.append("name=" + lbl + ", addr=" + addr + " at " + x + ',' + y); return ret.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/SendDialog.java0000644000175000017500000000436011366547366026003 0ustar twernertwerner// $Id: SendDialog.java,v 1.6 2006/02/16 08:22:36 belaban Exp $ package org.jgroups.demos.wb; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MethodCall; import org.jgroups.blocks.RpcDispatcher; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class SendDialog extends Dialog implements ActionListener { private final TextArea msg=new TextArea(""); private final Font default_font=new Font("Helvetica",Font.PLAIN,12); private RpcDispatcher disp=null; private Node dest=null; private String sender=null; public SendDialog(Frame parent, Node dest, String src, RpcDispatcher disp) { super(parent, "Send message to " + dest.lbl + " at " + dest.addr, true); Panel p1=new Panel(), p2=new Panel(); Button send=new Button("Send"), send_all=new Button("Send to all"); Button cancel=new Button("Cancel"); this.disp=disp; this.dest=dest; sender=src; send.setFont(default_font); send_all.setFont(default_font); cancel.setFont(default_font); msg.setFont(default_font); p1.setLayout(new BorderLayout()); p1.add(msg); p2.setLayout(new FlowLayout()); send.addActionListener(this); send_all.addActionListener(this); cancel.addActionListener(this); p2.add(send); p2.add(send_all); p2.add(cancel); add("Center", p1); add("South", p2); setSize(300, 150); Point my_loc=parent.getLocation(); my_loc.x+=50; my_loc.y+=150; setLocation(my_loc); show(); } public String getMessage() { String retval=msg.getText(); return retval.length() > 0 ? retval : null; } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); String retval=msg.getText(); if(retval == null || retval.length() < 1) { dispose(); return; } try { MethodCall call = new MethodCall("displayMessage", new Object[] {sender, retval}, new String[] {String.class.getName(), String.class.getName()}); if(command.equals("Send")) disp.callRemoteMethod(dest.addr, call, GroupRequest.GET_FIRST, 0); else if(command.equals("Send to all")) disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); } catch(Throwable ex) { System.err.println(ex); } dispose(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/GraphPanel.java0000644000175000017500000001707311366547366026020 0ustar twernertwerner// $Id: GraphPanel.java,v 1.6 2006/02/16 08:22:25 belaban Exp $ package org.jgroups.demos.wb; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MethodCall; import org.jgroups.util.Util; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.Vector; public class GraphPanel extends Panel implements MouseListener, MouseMotionListener { final Whiteboard wb; final Vector nodes=new Vector(); final Vector copy=new Vector(); String myname=null; public Object my_addr=null; Node pick; boolean pickfixed; Image offscreen; Dimension offscreensize; Graphics offgraphics; static final Color fixedColor = Color.red; static final Color selectColor = Color.pink; final Color nodeColor = new Color(250, 220, 100); final Font default_font=new Font("Helvetica",Font.PLAIN,12); Log log=LogFactory.getLog(getClass()); private Frame findParent() { Component retval=getParent(); while(retval != null) { if(retval instanceof Frame) return (Frame)retval; retval=retval.getParent(); } return null; } Node findNodeAtPoint(Point p) { int x=p.x, y=p.y; Node n; synchronized(nodes) { if(nodes.size() < 1) return null; for(int i=nodes.size()-1; i >= 0; i--) { n=(Node)nodes.elementAt(i); if(x >= n.xloc && x <= (n.xloc + n.width) && y >= n.yloc && y <= (n.yloc + n.height)) return n; } } return null; } public GraphPanel(Whiteboard wb) { this.wb = wb; addMouseListener(this); addMouseMotionListener(this); } public void addNode(String lbl, Address addr, int xloc, int yloc) { Node n = new Node(); n.x = xloc; n.y = yloc; n.lbl = lbl; n.addr=addr; nodes.addElement(n); repaint(); } public void removeNode(Object addr) { Node n; Object a; if(addr == null) { log.error("removeNode(): address of node to be removed is null !"); return; } synchronized(nodes) { for(int i=0; i < nodes.size(); i++) { n=(Node)nodes.elementAt(i); a=n.addr; if(a == null) continue; if(addr.equals(a)) { nodes.removeElement(n); System.out.println("Removed node " + n); break; } } repaint(); } } // Removes nodes that are not in the view public void adjustNodes(Vector v) { Node n; boolean removed=false; synchronized(nodes) { for(int i=0; i < nodes.size(); i++) { n=(Node)nodes.elementAt(i); if(!v.contains(n.addr)) { System.out.println("adjustNodes(): node " + n + " was removed"); nodes.removeElement(n); removed=true; } } if(removed) repaint(); } } public void paintNode(Graphics g, Node n, FontMetrics fm) { String addr=n.addr != null ? n.addr.toString() : null; int x = (int)n.x; int y = (int)n.y; g.setColor((n == pick) ? selectColor : (n.fixed ? fixedColor : nodeColor)); int w = fm.stringWidth(n.lbl) + 10; if(addr != null) w=Math.max(w, fm.stringWidth(addr) + 10); if(addr == null) addr=""; int h = (fm.getHeight() + 4) * 2; n.width=w; n.height=h; n.xloc=x - w/2; n.yloc=y - h/2; g.fillRect(x - w/2, y - h / 2, w, h); g.setColor(Color.black); g.drawRect(x - w/2, y - h / 2, w-1, h-1); g.drawString(n.lbl, x - (w-10)/2, (y - (h-4)/2) + fm.getAscent()); g.drawString(addr, x - (w-10)/2, (y - (h-4)/2) + 2 * fm.getAscent() +4 ); } public synchronized void update(Graphics g) { Dimension d = getSize(); if ((offscreen == null) || (d.width != offscreensize.width) || (d.height != offscreensize.height)) { offscreen = createImage(d.width, d.height); offscreensize = d; offgraphics = offscreen.getGraphics(); offgraphics.setFont(default_font); } offgraphics.setColor(getBackground()); offgraphics.fillRect(0, 0, d.width, d.height); FontMetrics fm = offgraphics.getFontMetrics(); for (int i = 0; i < nodes.size(); i++) { paintNode(offgraphics, (Node)nodes.elementAt(i), fm); } g.drawImage(offscreen, 0, 0, null); } public void mouseDragged(MouseEvent e) { Point p=e.getPoint(); int mod=e.getModifiers(); if(pick == null) return; pick.x=p.x; pick.y=p.y; repaint(); } public void mousePressed(MouseEvent e) { Point p=e.getPoint(); double bestdist = Double.MAX_VALUE, dist; int mod=e.getModifiers(); Node n; String msg; if((mod & MouseEvent.BUTTON3_MASK) != 0) { System.out.println("\nright button at " + p); n=findNodeAtPoint(p); if(n != null) { System.out.println("Found node at " + p + ": " + n); SendDialog dlg=new SendDialog(findParent(), n, myname, wb.disp); repaint(); } e.consume(); return; } for (int i = 0 ; i < nodes.size() ; i++) { n=(Node)nodes.elementAt(i); dist = (n.x - p.x) * (n.x - p.x) + (n.y - p.y) * (n.y - p.y); if (dist < bestdist) { pick = n; bestdist = dist; } } pickfixed = pick.fixed; pick.fixed = true; pick.x = p.x; pick.y = p.y; repaint(); } public void mouseReleased(MouseEvent e) { Point p=e.getPoint(); int mod=e.getModifiers(); if(pick == null) return; pick.x = p.x; pick.y = p.y; pick.fixed = pickfixed; try { MethodCall call = new MethodCall("moveNode", new Object[] {pick}, new String[] {Node.class.getName()}); wb.disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); } catch(Exception ex) { log.error(ex); } pick = null; } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseMoved(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} public void start(String name) { myname=name; int xloc = (int)(10 + 250*Math.random()); int yloc = (int)(10 + 250*Math.random()); try { MethodCall call=new MethodCall("addNode", new Object[] {name, my_addr, new Integer(xloc), new Integer(yloc)}, new String[] {String.class.getName(), Address.class.getName(), int.class.getName(), int.class.getName()}); wb.disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); } catch(Exception e) { log.error(e); } repaint(); } public void stop() { nodes.removeAllElements(); } public void saveState() { copy.removeAllElements(); synchronized(nodes) { for(int i=0; i < nodes.size(); i++) copy.addElement(nodes.elementAt(i)); } } public byte[] getState() { // return the copy previously saved by saveState() try { return Util.objectToByteBuffer(copy); } catch(Throwable ex) { ex.printStackTrace(); return null; } } public void setState(byte[] data) { Vector n; Object new_state; try { new_state=Util.objectFromByteBuffer(data); } catch(Exception ex) { ex.printStackTrace(); return; } synchronized(nodes) { nodes.removeAllElements(); if(new_state != null) { n=(Vector)new_state; for(int i=0; i < n.size(); i++) nodes.addElement(n.elementAt(i)); repaint(); } } } public void moveNode(Node n) { Node tmp; boolean changed=false; synchronized(nodes) { for(int i=0; i < nodes.size(); i++) { tmp=(Node)nodes.elementAt(i); if(n.addr.equals(tmp.addr)) { tmp.x=n.x; tmp.y=n.y; changed=true; break; } } if(changed) repaint(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/Whiteboard.java0000644000175000017500000001701011366547366026056 0ustar twernertwerner// $Id: Whiteboard.java,v 1.6.2.1 2008/01/22 10:01:26 belaban Exp $ package org.jgroups.demos.wb; import org.jgroups.*; import org.jgroups.blocks.*; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.applet.Applet; import java.awt.*; import java.awt.event.*; /** * Shared whiteboard: members are represented by rectangles that contain their names and the OS/arch of * the machine they are working on. The boxes can be moved by anyone and by clicking on them, messages can * be sent to specific or all members. Whiteboard is both an application and an applet. * @author Bela Ban */ public class Whiteboard extends Applet implements ActionListener, MessageListener, MembershipListener, ComponentListener, FocusListener { public RpcDispatcher disp; Channel channel; GraphPanel panel; private Button leave_button; private Label mbr_label; private final Font default_font=new Font("Helvetica",Font.PLAIN,12); private String props=null; public static final String groupname="WbGrp"; private boolean application=false; Log log=LogFactory.getLog(getClass()); public void receive(Message m) { ; } public byte[] getState() { panel.saveState(); return panel.getState(); } public void setState(byte[] new_state) { panel.setState(new_state); } private String getInfo() { StringBuilder ret = new StringBuilder(); ret.append(" (" + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + ' ' + System.getProperty("os.arch") + ')'); return ret.toString(); } private Frame findParent() { Component retval = getParent(); while (retval != null) { if (retval instanceof Frame) return (Frame) retval; retval = retval.getParent(); } return null; } public Whiteboard() { // called when started as applet } public Whiteboard(String properties) { // called when started as application application = true; props = properties; } public void init() { setLayout(new BorderLayout()); panel = new GraphPanel(this); panel.setBackground(Color.white); add("Center", panel); Panel p = new Panel(); leave_button = new Button("Exit"); leave_button.setFont(default_font); leave_button.addActionListener(this); mbr_label = new Label("1 mbr(s)"); mbr_label.setFont(default_font); p.add("South", leave_button); p.add("South", mbr_label); add("South", p); if (!application) props = getParameter("properties"); if (props == null) { props = "udp.xml"; } System.out.println("properties are " + props); try { channel = new JChannel(props); disp = new RpcDispatcher(channel, this, this, this); channel.connect(groupname); channel.getState(null, 0); } catch (Exception e) { log.error("Whiteboard.init(): " + e); } panel.my_addr = channel.getLocalAddress(); UserInfoDialog dlg = new UserInfoDialog(findParent()); String n = dlg.getUserName(); String info = getInfo(); panel.start(n + info); addComponentListener(this); addFocusListener(this); } public void destroy() { if (disp != null) { try { MethodCall call = new MethodCall("removeNode", new Object[] {panel.my_addr}, new String[] {Object.class.getName()}); disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); } catch (Exception e) { log.error(e); } channel.close(); disp = null; if (panel != null) { panel.stop(); panel = null; } } } public void repaint() { if (panel != null) panel.repaint(); } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); if ("Exit".equals(command)) { try { setVisible(false); destroy(); if (application) { ((Frame) getParent()).dispose(); System.exit(0); } } catch (Exception ex) { log.error(ex); } } else System.out.println("Unknown action"); } public void viewAccepted(View v) { if (v != null) { if (mbr_label != null) mbr_label.setText(v.size() + " mbr(s)"); } panel.adjustNodes(v.getMembers()); } public void suspect(Address obj) { } public void block() { } public void moveNode(Node n) { panel.moveNode(n); } public void addNode(String lbl, Address addr, int xloc, int yloc) { panel.addNode(lbl, addr, xloc, yloc); } public void removeNode(Object addr) { panel.removeNode(addr); } public void displayMessage(String sender, String msg) { new MessageDialog(findParent(), sender, msg); panel.repaint(); } public void componentResized(ComponentEvent e) { if (panel != null) panel.repaint(); } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { if (panel != null) panel.repaint(); } public void componentHidden(ComponentEvent e) { } public void focusGained(FocusEvent e) { if (panel != null) panel.repaint(); } public void focusLost(FocusEvent e) { } public static void main(String[] args) { String props = null; for (int i = 0; i < args.length; i++) { if ("-props".equals(args[i])) { props = args[++i]; continue; } help(); return; } Whiteboard wb = new Whiteboard(props); new ApplFrame("Whiteboard Application", wb); } static void help() { System.out.println("Whiteboard [-help] [-props ]"); } } class ApplFrame extends Frame implements WindowListener, ComponentListener { Whiteboard wb = null; public ApplFrame(String title, Whiteboard wb) { super(title); this.wb = wb; add(wb); setSize(299, 299); setVisible(true); wb.init(); setSize(300, 300); addWindowListener(this); addComponentListener(this); } public void windowOpened(WindowEvent e) { } public void windowClosing(WindowEvent e) { dispose(); System.exit(0); } public void windowClosed(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { wb.repaint(); } public void windowActivated(WindowEvent e) { wb.repaint(); } public void windowDeactivated(WindowEvent e) { } public void componentResized(ComponentEvent e) { wb.repaint(); } public void componentMoved(ComponentEvent e) { } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/UserInfoDialog.java0000644000175000017500000000267711366547366026655 0ustar twernertwerner// $Id: UserInfoDialog.java,v 1.5 2005/05/30 16:14:37 belaban Exp $ package org.jgroups.demos.wb; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class UserInfoDialog extends Dialog implements ActionListener { final Button ok=new Button("OK"); final Label l=new Label("Name: "); final TextField name=new TextField(""); private final Font default_font=new Font("Helvetica",Font.PLAIN,12); public UserInfoDialog(Frame parent) { super(parent, "Input", true); setLayout(null); l.setFont(default_font); l.setSize(50, 30); l.setLocation(30, 50); name.setFont(default_font); name.setSize(150, 30); name.setLocation(90, 50); //name.selectAll(); ok.setFont(default_font); ok.setSize(50, 30); ok.setLocation(30, 90); add(l); add(name); add(ok); ok.addActionListener(this); setSize(300, 150); Point my_loc=parent.getLocation(); my_loc.x+=50; my_loc.y+=150; setLocation(my_loc); show(); } public String getUserName() { return name.getText(); } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); String tmp=name.getText(); if(command == "OK") { if(tmp == null || tmp.length() < 1) return; else dispose(); } else System.err.println("UserInfoDialog.actionPerfomed(): unknown action " + e.getActionCommand()); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/MessageDialog.java0000644000175000017500000000176111366547366026500 0ustar twernertwerner// $Id: MessageDialog.java,v 1.2 2004/09/23 16:29:34 belaban Exp $ package org.jgroups.demos.wb; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class MessageDialog extends Dialog implements ActionListener { private final TextArea text=new TextArea(""); private final Font default_font=new Font("Helvetica",Font.PLAIN,12); public MessageDialog(Frame parent, String sender, String msg) { super(parent, "Msg from " + sender); Button ok=new Button("OK"); setLayout(new BorderLayout()); setBackground(Color.white); ok.setFont(default_font); text.setFont(default_font); text.setEditable(false); text.setText(msg); ok.addActionListener(this); add("Center", text); add("South", ok); setSize(300, 150); Point my_loc=parent.getLocation(); my_loc.x+=50; my_loc.y+=150; setLocation(my_loc); Toolkit.getDefaultToolkit().beep(); show(); } public void actionPerformed(ActionEvent e) { dispose(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/wb/wb.html0000644000175000017500000000076411366547366024431 0ustar twernertwerner A Simple Whiteboard Program


/HTML> libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/DrawRepl.java0000644000175000017500000002206611366547366025105 0ustar twernertwerner// $Id: DrawRepl.java,v 1.7 2005/07/17 11:36:42 chrislott Exp $ package org.jgroups.demos; import org.jgroups.Channel; import org.jgroups.JChannel; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.MethodCall; import org.jgroups.blocks.RpcDispatcher; import java.awt.*; import java.awt.event.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.Hashtable; import java.util.Random; /** * * Replicates the whiteboard demo by intercepting central AWT event queue and mcasting events to * all members. Not very useful in all cases, e.g. when the "Leave" button is pressed, and this event * is broadcast to all members, all members will leave ! This demo would clearly benefit from more work ! * NOT SUPPORTED ! */ public class DrawRepl implements MouseMotionListener, WindowListener, ActionListener, Runnable { private Graphics graphics=null; private Frame mainFrame=null; private Panel panel=null, sub_panel=null; private final byte[] buf=new byte[128]; private final ByteArrayOutputStream out=new ByteArrayOutputStream(); private DataOutputStream outstream; private ByteArrayInputStream inp; private DataInputStream instream; private int x, y; private final Hashtable colors=new Hashtable(); private final Random random=new Random(System.currentTimeMillis()); private int col_val=1; private Color current_color=Color.red; private Button clear_button, leave_button; private final String groupname="DrawReplGroup"; private final Font default_font=new Font("Helvetica",Font.PLAIN,12); private EventQueue event_queue=null; private Thread mythread=null; private RpcDispatcher dispatcher; private Channel channel; public DrawRepl() { colors.put(new Integer(1), Color.white); colors.put(new Integer(2), Color.black); colors.put(new Integer(3), Color.red); colors.put(new Integer(4), Color.orange); colors.put(new Integer(5), Color.green); colors.put(new Integer(6), Color.magenta); colors.put(new Integer(7), Color.cyan); colors.put(new Integer(8), Color.blue); mythread=new Thread(this); try { channel=new JChannel(); dispatcher=new RpcDispatcher(channel, null, null, this); channel.connect(groupname); } catch(Exception e) { System.err.println(e); System.exit(0); } } public static void main(String[] args) { DrawRepl draw=new DrawRepl(); draw.go(); } private Color SelectColor() { col_val=(Math.abs(random.nextInt()) % 8)+1; Color ret=(Color)colors.get(new Integer(col_val)); if(ret == null) ret=Color.red; return ret; } AWTEvent copyEvent(Component src, AWTEvent evt) { if(evt instanceof MouseEvent) { MouseEvent mev=(MouseEvent)evt; return new MouseEvent(src, evt.getID(), mev.getWhen(), mev.getModifiers(), mev.getX(), mev.getY(), mev.getClickCount(), mev.isPopupTrigger()); } if(evt instanceof KeyEvent) { KeyEvent kev=(KeyEvent)evt; return new KeyEvent(src, evt.getID(), kev.getWhen(), kev.getModifiers(), kev.getKeyCode(), kev.getKeyChar()); } if(evt instanceof ActionEvent) return new ActionEvent(src, evt.getID(), ((ActionEvent)evt).getActionCommand(), ((ActionEvent)evt).getModifiers()); if(evt instanceof PaintEvent) return new PaintEvent(src, evt.getID(), ((PaintEvent)evt).getUpdateRect()); if(evt instanceof FocusEvent) return new FocusEvent(src, evt.getID(), ((FocusEvent)evt).isTemporary()); if(evt instanceof ComponentEvent) return new ComponentEvent(src, evt.getID()); return null; } void dispatch(Object src, AWTEvent evt) { if (src instanceof Component) ((Component)src).dispatchEvent(evt); else if (src instanceof MenuComponent) ((MenuComponent)src).dispatchEvent(evt); else System.err.println("++++++++++"); } public Component findComponent(Container parent, String comp_name) { Component retval=null; if(comp_name != null && comp_name.equals(parent.getName())) return parent; int ncomponents = parent.getComponentCount(); Component components[] = parent.getComponents(); for (int i = ncomponents-1 ; i >= 0; i--) { Component comp = components[i], tmp; if (comp != null) { if(comp instanceof Container) { retval=findComponent((Container)comp, comp_name); if(retval != null) return retval; } else if(comp_name.equals(comp.getName())) return comp; } } return retval; } // public void setSize(Integer x, Integer y) { // mainFrame.setSize(new Dimension(x.intValue(), y.intValue())); // } /* Called by Dispatcher */ public void processEvent(String comp_name, AWTEvent evt) { AWTEvent copy_evt=null; Component src=findComponent(mainFrame, comp_name); if(src == null) { System.err.println("processEvent(): src is null"); return; } System.out.println("Received " + evt.getClass().getName()); copy_evt=copyEvent(src, evt); if(copy_evt == null) { System.err.println("copy_evt is NULL"); return; } dispatch(src, copy_evt); // if(evt instanceof ComponentEvent && evt.getID() == ComponentEvent.COMPONENT_RESIZED) { // Dimension dim=mainFrame.getSize(); // try { // dispatcher.sendGetN(groupname, "setSize", new Integer(dim.height), // new Integer(dim.width), 0, 0); // } // catch(Exception e) { // System.err.println(e); // } // } } void processLocally(AWTEvent evt) { dispatch(evt.getSource(), evt); } public void run() { String comp_name; while(true) { try { AWTEvent evt=event_queue.getNextEvent(); Object obj=evt.getSource(); if(obj == null) { System.err.println("src is NULL"); continue; } if(obj instanceof Component) comp_name=((Component)obj).getName(); else if(obj instanceof MenuComponent) comp_name=((MenuComponent)obj).getName(); else { System.err.println("src is of type " + obj.getClass().getName()); continue; } if(evt instanceof FocusEvent || evt instanceof PaintEvent) { System.out.println(evt.getClass().getName() + " not copied"); processLocally(evt); continue; } System.out.println("MCasting "+evt.getClass().getName()+" event..."); MethodCall call = new MethodCall("processEvent", new Object[] {comp_name, evt}, new String[] {String.class.getName(), AWTEvent.class.getName()}); dispatcher.callRemoteMethods(null, call, GroupRequest.GET_NONE, 0); } catch(Exception e) { System.err.println(e); } } } public void go() { mainFrame=new Frame(); panel=new Panel(); sub_panel=new Panel(); event_queue=mainFrame.getToolkit().getSystemEventQueue(); mythread.start(); mainFrame.setSize(200,200); mainFrame.add("Center", panel); clear_button=new Button("Clear"); clear_button.setFont(default_font); clear_button.addActionListener(this); leave_button=new Button("Exit"); leave_button.setFont(default_font); leave_button.addActionListener(this); sub_panel.add("South", clear_button); sub_panel.add("South", leave_button); mainFrame.add("South", sub_panel); mainFrame.addWindowListener(this); // mainFrame.addComponentListener(this); panel.addMouseMotionListener(this); mainFrame.setVisible(true); graphics=panel.getGraphics(); current_color=SelectColor(); if(current_color == null) current_color=Color.red; graphics.setColor(current_color); } /* --------------- Callbacks --------------- */ public void mouseMoved(MouseEvent e) { } public void mouseDragged(MouseEvent e) { x=e.getX(); y=e.getY(); graphics.fillOval(x, y, 10, 10); } public void clearPanel() { System.out.println("CLEAR"); Rectangle bounds=panel.getBounds(); graphics.clearRect(0, 0, bounds.width, bounds.height); } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) { System.exit(0); } public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} // public void componentResized(ComponentEvent e) { // System.out.println("RESIZED, size is " + mainFrame.getBounds()); // } // public void componentMoved(ComponentEvent e) { // System.out.println("MOVED, location is: " + mainFrame.getLocation()); // } // public void componentShown(ComponentEvent e) {} // public void componentHidden(ComponentEvent e) {} public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if("Clear".equals(command)) clearPanel(); else if("Exit".equals(command)) { mainFrame.setVisible(false); System.exit(0); } else System.out.println("Unknown action"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/ReplicatedTreeDemo.java0000644000175000017500000005375111366547366027073 0ustar twernertwerner// $Id: ReplicatedTreeDemo.java,v 1.7.14.2 2008/01/22 10:00:55 belaban Exp $ package org.jgroups.demos; import org.jgroups.View; import org.jgroups.blocks.ReplicatedTree; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import javax.swing.tree.*; import java.awt.*; import java.awt.event.*; import java.io.File; import java.util.*; /** * Graphical view of a ReplicatedTree * * @author Bela Ban */ public class ReplicatedTreeDemo { /** * Graphical view of a ReplicatedTree (using the MVC paradigm). An instance of this class needs to be given a * reference to the underlying model (ReplicatedTree) and needs to registers as a ReplicatedTreeListener. Changes * to the tree structure are propagated from the model to the view (via ReplicatedTreeListener), changes from the * GUI (e.g. by a user) are executed on the tree model (which will broadcast the changes to all replicas).

* The view itself caches only the nodes, but doesn't cache any of the data (HashMap) associated with it. When * data needs to be displayed, the underlying tree will be accessed directly. * * @author Bela Ban */ static class ReplicatedTreeView extends JFrame implements WindowListener, ReplicatedTree.ReplicatedTreeListener, TreeSelectionListener, TableModelListener { DefaultTreeModel tree_model=null; JTree jtree=null; final DefaultTableModel table_model=new DefaultTableModel(); final JTable table=new JTable(table_model); final MyNode root=new MyNode(SEP); final String props=null; String selected_node=null; ReplicatedTree tree=null; // the underlying model JPanel tablePanel=null; JMenu operationsMenu=null; JPopupMenu operationsPopup=null; JMenuBar menubar=null; static final String SEP=ReplicatedTree.SEPARATOR; private static final int KEY_COL_WIDTH=20; private static final int VAL_COL_WIDTH=300; public ReplicatedTreeView(ReplicatedTree tree, Object title) throws Exception { this.tree=tree; tree.addReplicatedTreeListener(this); addNotify(); setTitle("ReplicatedTreeDemo: mbr=" + title); tree_model=new DefaultTreeModel(root); jtree=new JTree(tree_model); jtree.setDoubleBuffered(true); jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); JScrollPane scroll_pane=new JScrollPane(jtree); populateTree(); getContentPane().add(scroll_pane, BorderLayout.CENTER); addWindowListener(this); table_model.setColumnIdentifiers(new String[]{"Name", "Value"}); table_model.addTableModelListener(this); setTableColumnWidths(); tablePanel=new JPanel(); tablePanel.setLayout(new BorderLayout()); tablePanel.add(table.getTableHeader(), BorderLayout.NORTH); tablePanel.add(table, BorderLayout.CENTER); getContentPane().add(tablePanel, BorderLayout.SOUTH); jtree.addTreeSelectionListener(this);//REVISIT MouseListener ml=new MouseAdapter() { public void mouseClicked(MouseEvent e) { int selRow=jtree.getRowForLocation(e.getX(), e.getY()); TreePath selPath=jtree.getPathForLocation(e.getX(), e.getY()); if(selRow != -1) { selected_node=makeFQN(selPath.getPath()); jtree.setSelectionPath(selPath); if(e.getModifiers() == java.awt.event.InputEvent.BUTTON3_MASK) { operationsPopup.show(e.getComponent(), e.getX(), e.getY()); } } } }; jtree.addMouseListener(ml); createMenus(); setLocation(50, 50); setSize(getInsets().left + getInsets().right + 485, getInsets().top + getInsets().bottom + 367); init(); setVisible(true); } public void windowClosed(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowActivated(WindowEvent event) { } public void windowDeactivated(WindowEvent event) { } public void windowOpened(WindowEvent event) { } public void windowClosing(WindowEvent event) { System.exit(0); } public void tableChanged(TableModelEvent evt) { int row, col; String key, val; if(evt.getType() == TableModelEvent.UPDATE) { row=evt.getFirstRow(); col=evt.getColumn(); if(col == 0) { // set() key=(String)table_model.getValueAt(row, col); val=(String)table_model.getValueAt(row, col + 1); if(key != null && val != null) { tree.put(selected_node, key, val); } } else { // add() key=(String)table_model.getValueAt(row, col - 1); val=(String)table.getValueAt(row, col); if(key != null && val != null) { tree.put(selected_node, key, val); } } } } public void valueChanged(TreeSelectionEvent evt) { TreePath path=evt.getPath(); String fqn=SEP; String component_name; HashMap data=null; for(int i=0; i < path.getPathCount(); i++) { component_name=((MyNode)path.getPathComponent(i)).name; if(component_name.equals(SEP)) continue; if(fqn.equals(SEP)) fqn+=component_name; else fqn=fqn + SEP + component_name; } data=getData(tree, fqn); if(data != null) { getContentPane().add(tablePanel, BorderLayout.SOUTH); populateTable(data); validate(); } else { clearTable(); getContentPane().remove(tablePanel); validate(); } } /* ------------------ ReplicatedTree.ReplicatedTreeListener interface ------------ */ public void nodeAdded(String fqn) { MyNode n, p; n=root.add(fqn); if(n != null) { p=(MyNode)n.getParent(); tree_model.reload(p); jtree.scrollPathToVisible(new TreePath(n.getPath())); } } public void nodeRemoved(String fqn) { MyNode n; TreeNode par; n=root.findNode(fqn); if(n != null) { n.removeAllChildren(); par=n.getParent(); n.removeFromParent(); tree_model.reload(par); } } public void nodeModified(String fqn) { // HashMap data; // data=getData(tree, fqn); //populateTable(data); REVISIT /* poulateTable is the current table being shown is the info of the node. that is modified. */ } public void viewChange(View new_view) { Vector mbrship; if(new_view != null && (mbrship=new_view.getMembers()) != null) { tree._put(SEP, "members", mbrship); tree._put(SEP, "coordinator", mbrship.firstElement()); } } /* ---------------- End of ReplicatedTree.ReplicatedTreeListener interface -------- */ /*----------------- Runnable implementation to make View change calles in AWT Thread ---*/ public void run() { } /* ----------------------------- Private Methods ---------------------------------- */ /** * Fetches all data from underlying tree model and display it graphically */ void init() { Vector mbrship=null; addGuiNode(SEP); mbrship=tree != null && tree.getMembers() != null ? (Vector)tree.getMembers().clone() : null; if(mbrship != null) { tree._put(SEP, "members", mbrship); tree._put(SEP, "coordinator", mbrship.firstElement()); } } /** * Fetches all data from underlying tree model and display it graphically */ private void populateTree() { addGuiNode(SEP); } /** * Recursively adds GUI nodes starting from fqn */ void addGuiNode(String fqn) { Set children; String child_name; if(fqn == null) return; // 1 . Add myself root.add(fqn); // 2. Then add my children children=tree.getChildrenNames(fqn); if(children != null) { for(Iterator it=children.iterator(); it.hasNext();) { child_name=(String)it.next(); addGuiNode(fqn + SEP + child_name); } } } String makeFQN(Object[] path) { StringBuilder sb=new StringBuilder(""); String tmp_name; if(path == null) return null; for(int i=0; i < path.length; i++) { tmp_name=((MyNode)path[i]).name; if(tmp_name.equals(SEP)) continue; else sb.append(SEP + tmp_name); } tmp_name=sb.toString(); if(tmp_name.length() == 0) return SEP; else return tmp_name; } void clearTable() { int num_rows=table.getRowCount(); if(num_rows > 0) { for(int i=0; i < num_rows; i++) table_model.removeRow(0); table_model.fireTableRowsDeleted(0, num_rows - 1); repaint(); } } void populateTable(HashMap data) { String key, strval=""; Object val; int num_rows=0; Map.Entry entry; if(data == null) return; num_rows=data.size(); clearTable(); if(num_rows > 0) { for(Iterator it=data.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=(String)entry.getKey(); val=entry.getValue(); if(val != null) strval=val.toString(); table_model.addRow(new Object[]{key, strval}); } table_model.fireTableRowsInserted(0, num_rows - 1); validate(); } } private void setTableColumnWidths() { table.sizeColumnsToFit(JTable.AUTO_RESIZE_NEXT_COLUMN); TableColumn column=null; column=table.getColumnModel().getColumn(0); column.setMinWidth(KEY_COL_WIDTH); column.setPreferredWidth(KEY_COL_WIDTH); column=table.getColumnModel().getColumn(1); column.setPreferredWidth(VAL_COL_WIDTH); } private void createMenus() { menubar=new JMenuBar(); operationsMenu=new JMenu("Operations"); AddNodeAction addNode=new AddNodeAction(); addNode.putValue(AbstractAction.NAME, "Add to this node"); RemoveNodeAction removeNode=new RemoveNodeAction(); removeNode.putValue(AbstractAction.NAME, "Remove this node"); AddModifyDataForNodeAction addModAction=new AddModifyDataForNodeAction(); addModAction.putValue(AbstractAction.NAME, "Add/Modify data"); ExitAction exitAction=new ExitAction(); exitAction.putValue(AbstractAction.NAME, "Exit"); operationsMenu.add(addNode); operationsMenu.add(removeNode); operationsMenu.add(addModAction); operationsMenu.add(exitAction); menubar.add(operationsMenu); setJMenuBar(menubar); operationsPopup=new JPopupMenu(); operationsPopup.add(addNode); operationsPopup.add(removeNode); operationsPopup.add(addModAction); } HashMap getData(ReplicatedTree tree, String fqn) { HashMap data; Set keys; String key; Object value; if(tree == null || fqn == null) return null; keys=tree.getKeys(fqn); if(keys == null) return null; data=new HashMap(); for(Iterator it=keys.iterator(); it.hasNext();) { key=(String)it.next(); value=tree.get(fqn, key); if(value != null) data.put(key, value); } return data; } /* -------------------------- End of Private Methods ------------------------------ */ /*----------------------- Actions ---------------------------*/ class ExitAction extends AbstractAction { public void actionPerformed(ActionEvent e) { System.exit(0); } } class AddNodeAction extends AbstractAction { public void actionPerformed(ActionEvent e) { JTextField fqnTextField=new JTextField(); if(selected_node != null) fqnTextField.setText(selected_node); Object[] information={"Enter fully qualified name", fqnTextField}; final String btnString1="OK"; final String btnString2="Cancel"; Object[] options={btnString1, btnString2}; int userChoice=JOptionPane.showOptionDialog(null, information, "Add Node", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); if(userChoice == 0) { String userInput=fqnTextField.getText(); tree.put(userInput, null); } } } class RemoveNodeAction extends AbstractAction { public void actionPerformed(ActionEvent e) { tree.remove(selected_node); } } class AddModifyDataForNodeAction extends AbstractAction { public void actionPerformed(ActionEvent e) { HashMap data=getData(tree, selected_node); if(data != null) { } else { clearTable(); data=new HashMap(); data.put("Add Key", "Add Value"); } populateTable(data); getContentPane().add(tablePanel, BorderLayout.SOUTH); validate(); } } // public static void main(String args[]) { // ReplicatedTree tree; // // for(int i=0; i < args.length; i++) { // if(args[i].equals("-help")) { // System.out.println("ReplicatedTreeView [-help]"); // return; // } // } // // try { // tree=new ReplicatedTree(null); // tree.setRemoteCalls(false); // HashMap map=new HashMap(); // map.put("name", "Framework"); // map.put("pid", new Integer(322649)); // tree.put("/federations/fed1/servers/Framework", map); // tree.put("/federations/fed1/servers/Security", null); // // // demo.setVisible(true); // new ReplicatedTreeView(tree, ""); // // tree.put("/federations/fed1/servers/Security/components/RuntimeMonitor", null); // tree.put("/federations/fed1/servers/fenics", null); // // // } // catch(Exception ex) { // ex.printStackTrace(System.err); // } // } class MyNode extends DefaultMutableTreeNode { String name=""; MyNode(String name) { this.name=name; } /** * Adds a new node to the view. Intermediary nodes will be created if they don't yet exist. * Returns the first node that was created or null if node already existed */ public MyNode add(String fqn) { MyNode curr, n, ret=null; StringTokenizer tok; String child_name; if(fqn == null) return null; curr=this; tok=new StringTokenizer(fqn, ReplicatedTreeView.SEP); while(tok.hasMoreTokens()) { child_name=tok.nextToken(); n=curr.findChild(child_name); if(n == null) { n=new MyNode(child_name); if(ret == null) ret=n; curr.add(n); } curr=n; } return ret; } /** * Removes a node from the view. Child nodes will be removed as well */ public void remove(String fqn) { removeFromParent(); } MyNode findNode(String fqn) { MyNode curr, n; StringTokenizer tok; String child_name; if(fqn == null) return null; curr=this; tok=new StringTokenizer(fqn, ReplicatedTreeView.SEP); while(tok.hasMoreTokens()) { child_name=tok.nextToken(); n=curr.findChild(child_name); if(n == null) return null; curr=n; } return curr; } MyNode findChild(String relative_name) { MyNode child; if(relative_name == null || getChildCount() == 0) return null; for(int i=0; i < getChildCount(); i++) { child=(MyNode)getChildAt(i); if(child.name == null) { continue; } if(child.name.equals(relative_name)) return child; } return null; } String print(int indent) { StringBuilder sb=new StringBuilder(); for(int i=0; i < indent; i++) sb.append(' '); if(!isRoot()) { if(name == null) sb.append("/"); else { sb.append(ReplicatedTreeView.SEP + name); } } sb.append('\n'); if(getChildCount() > 0) { if(isRoot()) indent=0; else indent+=4; for(int i=0; i < getChildCount(); i++) sb.append(((MyNode)getChildAt(i)).print(indent)); } return sb.toString(); } public String toString() { return name; } } } public static void main(String args[]) { ReplicatedTree tree; String start_directory=null; boolean jmx=false; String props="udp.xml"; for(int i=0; i < args.length; i++) { if("-props".equals(args[i])) { props=args[++i]; continue; } if("-start_directory".equals(args[i])) { start_directory=args[++i]; continue; } if("-jmx".equals(args[i])) { jmx=true; continue; } help(); return; } try { tree=new ReplicatedTree("ReplicatedTreeDemo-Group", props, 10000, jmx); new ReplicatedTreeView(tree, tree.getLocalAddress()); // demo.setVisible(true); if(start_directory != null && start_directory.length() > 0) { populateTree(tree, start_directory); } else { /* HashMap map=new HashMap(); map.put("name", "Framework"); map.put("pid", new Integer(322649)); tree.put("/federations/fed1/servers/Framework", map); tree.put("/federations/fed1/servers/Security", null); tree.put("/federations/fed1/servers/Security/components/RuntimeMonitor", null); tree.put("/federations/fed1/servers/fenics", null); */ } } catch(Exception ex) { ex.printStackTrace(System.err); } } static void help() { System.out.println("ReplicatedTreeView [-help] " + "[-props ] [-start_directory ] [-jmx]"); } static void populateTree(ReplicatedTree tree, String dir) { File file=new File(dir); if(!file.exists()) return; tree.put(dir, null); if(file.isDirectory()) { String[] children=file.list(); if(children != null && children.length > 0) { for(int i=0; i < children.length; i++) populateTree(tree, dir + '/' + children[i]); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/ChatCore.java0000644000175000017500000000741411366547366025055 0ustar twernertwernerpackage org.jgroups.demos; // prepend 'bridge.' to the next 2 imports if // you want to use this class w/ JGroups-ME import java.util.Iterator; import java.util.LinkedList; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.JChannel; import org.jgroups.MembershipListener; import org.jgroups.Message; import org.jgroups.MessageListener; import org.jgroups.View; import org.jgroups.blocks.PullPushAdapter; import org.jgroups.util.Util; public abstract class ChatCore implements MessageListener, MembershipListener { Channel channel; PullPushAdapter ad; Thread mainThread; static final String group_name = "ChatGroup"; String props = null; String username = null; LinkedList history = new LinkedList(); public ChatCore(String props) { this.props = props; try { username = System.getProperty("user.name"); } catch (Throwable t) { } } abstract void post(String msg); public void link() { try { channel = new JChannel(props); channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); channel.setOpt(Channel.AUTO_GETSTATE, Boolean.TRUE); System.out.println("Connecting to " + group_name); channel.connect(group_name); ad = new PullPushAdapter(channel, this, this); channel.getState(null, 5000); } catch (Exception e) { e.printStackTrace(); post(e.toString()); } } public void dumpHist() { if (!history.isEmpty()) { for (Iterator it = history.iterator(); it.hasNext();) { String s = (String) it.next(); post(s + "\n"); System.err.print(s + "\n"); } } } /* -------------------- Interface MessageListener ------------------- */ public void receive(Message msg) { String o; try { o = new String(msg.getBuffer()); System.err.print(o + " [" + msg.getSrc() + "]\n"); post(o + " [" + msg.getSrc() + "]\n"); history.add(o); } catch (Exception e) { post("Chat.receive(): " + e); System.err.print("Chat.receive(): " + e); } } public byte[] getState() { try { return Util.objectToByteBuffer(history); } catch (Exception e) { return null; } } public void setState(byte[] state) { try { history = (LinkedList) Util.objectFromByteBuffer(state); } catch (Exception e) { e.printStackTrace(); } } /* ----------------- End of Interface MessageListener --------------- */ /* ------------------- Interface MembershipListener ----------------- */ public void viewAccepted(View new_view) { post("Received view " + new_view + '\n'); System.err.print("Received view " + new_view + '\n'); } public void suspect(Address suspected_mbr) { } public void block() { } public void stop() { System.out.print("Stopping PullPushAdapter"); ad.stop(); System.out.println(" -- done"); } public void disconnect() { System.out.print("Disconnecting the channel"); channel.disconnect(); System.out.println(" -- done"); } public void close() { System.out.print("Closing the channel"); channel.close(); System.out.println(" -- done"); } /* --------------- End of Interface MembershipListener -------------- */ protected synchronized void handleLeave() { try { stop(); disconnect(); close(); } catch (Exception e) { e.printStackTrace(); System.err .print("Failed leaving the group: " + e.toString() + '\n'); post("Failed leaving the group: " + e.toString() + '\n'); } } protected void handleSend(String txt) { String tmp = username + ": " + txt; try { // for the sake of jgroups-me compatibility we stick with // byte buffers and Streamable *only* (not Serializable) Message msg = new Message(null, null, tmp.getBytes(), 0, tmp .length()); channel.send(msg); } catch (Exception e) { System.err.print("Failed sending message: " + e.toString() + '\n'); post("Failed sending message: " + e.toString() + '\n'); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/DrawCommand.java0000644000175000017500000000327711366547366025564 0ustar twernertwerner// $Id: DrawCommand.java,v 1.6.6.1 2008/01/22 10:00:56 belaban Exp $ package org.jgroups.demos; import org.jgroups.util.Streamable; import java.io.DataOutputStream; import java.io.IOException; import java.io.DataInputStream; /** * Encapsulates information about a draw command. * Used by the {@link Draw} and other demos. * */ public class DrawCommand implements Streamable { static final byte DRAW=1; static final byte CLEAR=2; byte mode; int x=0; int y=0; int r=0; int g=0; int b=0; public DrawCommand() { // needed for streamable } DrawCommand(byte mode) { this.mode=mode; } DrawCommand(byte mode, int x, int y, int r, int g, int b) { this.mode=mode; this.x=x; this.y=y; this.r=r; this.g=g; this.b=b; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(mode); out.writeInt(x); out.writeInt(y); out.writeInt(r); out.writeInt(g); out.writeInt(b); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { mode=in.readByte(); x=in.readInt(); y=in.readInt(); r=in.readInt(); g=in.readInt(); b=in.readInt(); } public String toString() { StringBuilder ret=new StringBuilder(); switch(mode) { case DRAW: ret.append("DRAW(" + x + ", " + y + ") [" + r + '|' + g + '|' + b + ']'); break; case CLEAR: ret.append("CLEAR"); break; default: return ""; } return ret.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/DistributedHashtableDemo.java0000644000175000017500000002246311366547366030271 0ustar twernertwerner// $Id: DistributedHashtableDemo.java,v 1.8.2.1 2007/11/20 08:53:42 belaban Exp $ package org.jgroups.demos; import org.jgroups.ChannelException; import org.jgroups.ChannelFactory; import org.jgroups.JChannelFactory; import org.jgroups.blocks.DistributedHashtable; import org.jgroups.persistence.PersistenceFactory; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Enumeration; import java.util.Map; import java.util.Vector; /** * Uses the DistributedHashtable building block. The latter subclasses java.util.Hashtable and overrides * the methods that modify the hashtable (e.g. put()). Those methods are multicast to the group, whereas * read-only methods such as get() use the local copy. A DistributedHashtable is created given the name * of a group; all hashtables with the same name find each other and form a group. * @author Bela Ban */ public class DistributedHashtableDemo extends Frame implements WindowListener, ActionListener, DistributedHashtable.Notification { static final String groupname="HashDemo"; DistributedHashtable h=null; final JButton get=new JButton("Get"); final JButton set=new JButton("Set"); final JButton quit=new JButton("Quit"); final JButton get_all=new JButton("All"); final JButton delete=new JButton("Delete"); final JLabel stock=new JLabel("Key"); final JLabel value=new JLabel("Value"); final JLabel err_msg=new JLabel("Error"); final JTextField stock_field=new JTextField(); final JTextField value_field=new JTextField(); final java.awt.List listbox=new java.awt.List(); final Font default_font=new Font("Helvetica", Font.PLAIN,12); public DistributedHashtableDemo() { super(); addWindowListener(this); } private void showMsg(String msg) { err_msg.setText(msg); err_msg.setVisible(true); } private void clearMsg() {err_msg.setVisible(false);} private void removeItem() { int index=listbox.getSelectedIndex(); if(index == -1) { showMsg("No item selected in listbox to be deleted !"); return; } String s=listbox.getSelectedItem(); String key=s.substring(0, s.indexOf(':', 0)); if(key != null) h.remove(key); } private void showAll() { if(listbox.getItemCount() > 0) listbox.removeAll(); if(h.size() == 0) return; clearMsg(); String key; Float val; for(Enumeration en=h.keys(); en.hasMoreElements();) { key=(String)en.nextElement(); val=(Float)h.get(key); if(val == null) continue; listbox.add(key + ": " + val.toString()); } } public void start(ChannelFactory factory, String props, boolean persist) throws ChannelException { h=new DistributedHashtable(groupname, factory, props, persist, 10000); h.addNotifier(this); setLayout(null); setSize(400, 300); setFont(default_font); stock.setBounds(new Rectangle(10, 30, 60, 30)); value.setBounds(new Rectangle(10, 60, 60, 30)); stock_field.setBounds(new Rectangle(100, 30, 100, 30)); value_field.setBounds(new Rectangle(100, 60, 100, 30)); listbox.setBounds(new Rectangle(210, 30, 150, 160)); err_msg.setBounds(new Rectangle(10, 200, 350, 30)); err_msg.setFont(new Font("Helvetica",Font.ITALIC,12)); err_msg.setForeground(Color.red); err_msg.setVisible(false); get.setBounds(new Rectangle(10, 250, 60, 30)); set.setBounds(new Rectangle(80, 250, 60, 30)); quit.setBounds(new Rectangle(150, 250, 60, 30)); get_all.setBounds(new Rectangle(220, 250, 60, 30)); delete.setBounds(new Rectangle(290, 250, 80, 30)); get.addActionListener(this); set.addActionListener(this); quit.addActionListener(this); get_all.addActionListener(this); delete.addActionListener(this); add(stock); add(value); add(stock_field); add(value_field); add(err_msg); add(get); add(set); add(quit); add(get_all); add(delete); add(listbox); setTitle("DistributedHashtable Demo"); showAll(); // pack(); setVisible(true); // new Thread() { // public void run() { // System.out.println("-- sleeping"); // Util.sleep(10000); // for(int i=0; i < 10; i++) { // System.out.println("-- put()"); // h.put("Bela#" + i, new Float(i)); // } // while(true) { // Util.sleep(500); // System.out.println(h.get("Bela#1")); // } // } // }.start(); } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowClosing(WindowEvent e) {System.exit(0);} public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); try { if(command == "Get") { String stock_name=stock_field.getText(); if(stock_name == null || stock_name.length() == 0) { showMsg("Key is empty !"); return; } showMsg("Looking up value for " + stock_name + ':'); Float val=(Float)h.get(stock_name); if(val != null) { value_field.setText(val.toString()); clearMsg(); } else { value_field.setText(""); showMsg("Value for " + stock_name + " not found"); } } else if(command == "Set") { String stock_name=stock_field.getText(); String stock_val=value_field.getText(); if(stock_name == null || stock_val == null || stock_name.length() == 0 || stock_val.length() == 0) { showMsg("Both key and value have to be present to create a new entry"); return; } Float val=new Float(stock_val); h.put(stock_name, val); showMsg("Key " + stock_name + " set to " + val); } else if(command == "All") { showAll(); } else if(command == "Quit") { setVisible(false); System.exit(0); } else if(command == "Delete") removeItem(); else System.out.println("Unknown action"); } catch(Exception ex) { value_field.setText(""); showMsg(ex.toString()); } } public void entrySet(Object key, Object value) {showAll();} public void entryRemoved(Object key) {showAll();} public void viewChange(Vector joined, Vector left) { System.out.println("New members: " + joined + ", left members: " + left); } public void contentsSet(Map m) { System.out.println("new contents: " + m); } public void contentsCleared() { System.out.println("contents cleared"); } public static void main(String args[]) { DistributedHashtableDemo client=new DistributedHashtableDemo(); ChannelFactory factory=new JChannelFactory(); String arg; boolean persist=false; // test for pbcast /* String props="UDP:" + "PING(num_initial_members=2;timeout=3000):" + "FD:" + // "DISCARD(down=0.1):" + // this is for discarding of 10% of the up messages ! "pbcast.PBCAST(gossip_interval=5000;gc_lag=50):" + "UNICAST:" + "FRAG:" + "pbcast.GMS:" + "pbcast.STATE_TRANSFER"; */ String props="udp.xml"; try { for(int i=0; i < args.length; i++) { arg=args[i]; if("-persist".equals(arg) && i+1]"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/applets/0000755000175000017500000000000011621261110024132 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/applets/DrawApplet.java0000644000175000017500000001653611366547366027105 0ustar twernertwerner// $Id: DrawApplet.java,v 1.5 2005/05/30 16:14:36 belaban Exp $ package org.jgroups.demos.applets; import java.applet.Applet; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.Random; import java.util.Vector; import org.jgroups.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class DrawApplet extends Applet implements Runnable, MouseMotionListener, ActionListener { private Graphics graphics=null; private Panel panel=null, sub_panel=null; private final ByteArrayOutputStream out=new ByteArrayOutputStream(); private DataOutputStream outstream; private DataInputStream instream; private final Random random=new Random(System.currentTimeMillis()); private Button clear_button, leave_button; private Label mbr_label; private final Font default_font=new Font("Helvetica",Font.PLAIN,12); private final String groupname="DrawGroup"; private Channel channel=null; private Thread receiver=null; private int member_size=1; private int red=0, green=0, blue=0; private Color default_color=null; private final ChannelFactory factory=new JChannelFactory(); private String props="TUNNEL(router_host=janet;router_port=12002):" + "PING(gossip_host=janet;gossip_port=12002):" + "FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:QUEUE"; private final Vector members=new Vector(); private boolean fl=true; Log log=LogFactory.getLog(getClass()); public void init() { System.out.println("INIT"); setLayout(new BorderLayout()); String tmp_props=getParameter("properties"); if(tmp_props != null) { System.out.println("Setting parameters " + tmp_props); props=tmp_props; } try { channel=factory.createChannel(props); showStatus("Connecting to group " + groupname); channel.connect(groupname); } catch(Exception e) { log.error(e); } receiver=new Thread(this, "DrawThread"); receiver.start(); go(); } public void start() { System.out.println("------- START"); } public void destroy() { System.out.println("------- DESTROY"); if(receiver != null && receiver.isAlive()) { fl=false; receiver.interrupt(); try {receiver.join(1000);} catch(Exception ex) {} } receiver=null; showStatus("Disconnecting from " + groupname); channel.disconnect(); showStatus("Disconnected"); } public void paint(Graphics g) { Rectangle bounds=panel.getBounds(); Color old=graphics.getColor(); if(bounds == null || graphics == null) return; graphics.setColor(Color.black); graphics.drawRect(0, 0, bounds.width-1, bounds.height-1); graphics.setColor(old); } private void selectColor() { red=(Math.abs(random.nextInt()) % 255); green=(Math.abs(random.nextInt()) % 255); blue=(Math.abs(random.nextInt()) % 255); default_color=new Color(red, green, blue); } public void go() { try { panel=new Panel(); sub_panel=new Panel(); resize(200, 200); add("Center", panel); clear_button=new Button("Clear"); clear_button.setFont(default_font); clear_button.addActionListener(this); leave_button=new Button("Exit"); leave_button.setFont(default_font); leave_button.addActionListener(this); mbr_label=new Label("0 mbr(s)"); mbr_label.setFont(default_font); sub_panel.add("South", clear_button); sub_panel.add("South", leave_button); sub_panel.add("South", mbr_label); add("South", sub_panel); panel.addMouseMotionListener(this); setVisible(true); mbr_label.setText(member_size + " mbrs"); graphics=panel.getGraphics(); selectColor(); graphics.setColor(default_color); panel.setBackground(Color.white); clear_button.setForeground(Color.blue); leave_button.setForeground(Color.blue); } catch(Exception e) { log.error(e); return; } } public void run() { Object tmp; Message msg=null; int my_x=10, my_y=10, r=0, g=0, b=0; while(fl) { my_x=10; my_y=10; try { tmp=channel.receive(0); if(tmp instanceof View) { viewAccepted((View)tmp); continue; } if(!(tmp instanceof Message)) continue; msg=(Message)tmp; if(msg == null || msg.getLength() == 0) { log.error("DrawApplet.run(): msg or msg.buffer is null !"); continue; } instream=new DataInputStream(new ByteArrayInputStream(msg.getRawBuffer(), msg.getOffset(), msg.getLength())); r=instream.readInt(); // red if(r == -13) { clearPanel(); continue; } g=instream.readInt(); // green b=instream.readInt(); // blue my_x=instream.readInt(); my_y=instream.readInt(); } catch(ChannelNotConnectedException conn) { break; } catch(Exception e) { log.error(e); } if(graphics != null) { graphics.setColor(new Color(r, g, b)); graphics.fillOval(my_x, my_y, 10, 10); graphics.setColor(default_color); } } } /* --------------- Callbacks --------------- */ public void mouseMoved(MouseEvent e) {} public void mouseDragged(MouseEvent e) { int tmp[]=new int[1], x, y; tmp[0]=0; x=e.getX(); y=e.getY(); graphics.fillOval(x, y, 10, 10); try { out.reset(); outstream=new DataOutputStream(out); outstream.writeInt(red); outstream.writeInt(green); outstream.writeInt(blue); outstream.writeInt(x); outstream.writeInt(y); channel.send(new Message(null, null, out.toByteArray())); out.reset(); } catch(Exception ex) { log.error(ex); } } public void clearPanel() { Rectangle bounds=null; if(panel == null || graphics == null) return; bounds=panel.getBounds(); graphics.clearRect(1, 1, bounds.width-2, bounds.height-2); } public void sendClearPanelMsg() { int tmp[]=new int[1]; tmp[0]=0; clearPanel(); try { out.reset(); outstream=new DataOutputStream(out); outstream.writeInt(-13); channel.send(new Message(null, null, out.toByteArray())); outstream.flush(); } catch(Exception ex) { log.error(ex); } } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if(command == "Clear") { System.out.println("Members are " + members); sendClearPanelMsg(); } else if(command == "Exit") { try { destroy(); setVisible(false); } catch(Exception ex) { log.error(ex); } } else System.out.println("Unknown action"); } public void viewAccepted(View v) { Vector mbrs=v.getMembers(); if(v != null) { System.out.println("View accepted: " +v); member_size=v.size(); if(mbr_label != null) mbr_label.setText(member_size + " mbr(s)"); members.removeAllElements(); for(int i=0; i < mbrs.size(); i++) members.addElement(mbrs.elementAt(i)); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/applets/package.html0000644000175000017500000000013511366547366026444 0ustar twernertwerner Provides an applet that demonstrates JGroups functionality. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/applets/draw.html0000644000175000017500000000101111366547366026000 0ustar twernertwerner A Simple Drawing Program Here is an example of a distributed Draw program:


libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/QuoteServer.java0000644000175000017500000000753211366547366025652 0ustar twernertwerner// $Id: QuoteServer.java,v 1.11 2007/07/05 08:42:04 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.util.Util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; /** * Example of a replicated quote server. The server maintains state which consists of a list * of quotes and their corresponding values. When it is started, it tries to reach other * quote servers to get its initial state. If it does not receive any response after 5 * seconds, it assumes it is the first server and starts processing requests.

* Any updates are multicast across the cluster * @author Bela Ban */ public class QuoteServer implements MembershipListener, MessageListener { final Hashtable stocks=new Hashtable(); Channel channel; RpcDispatcher disp; static final String channel_name="Quotes"; final int num_members=1; Log log=LogFactory.getLog(getClass()); final String props=null; // default stack from JChannel private void integrate(Hashtable state) { String key; if(state == null) return; for(Enumeration e=state.keys(); e.hasMoreElements();) { key=(String)e.nextElement(); stocks.put(key, state.get(key)); // just overwrite } } public void viewAccepted(View new_view) { System.out.println("Accepted view (" + new_view.size() + new_view.getMembers() + ')'); } public void suspect(Address suspected_mbr) { } public void block() { } public void start() { try { channel=new JChannel(props); disp=new RpcDispatcher(channel, this, this, this); channel.connect(channel_name); System.out.println("\nQuote Server started at " + new Date()); System.out.println("Joined channel '" + channel_name + "' (" + channel.getView().size() + " members)"); channel.getState(null, 0); System.out.println("Ready to serve requests"); } catch(Exception e) { log.error("QuoteServer.start() : " + e); System.exit(-1); } } /* Quote methods: */ public float getQuote(String stock_name) throws Exception { System.out.print("Getting quote for " + stock_name + ": "); Float retval=(Float)stocks.get(stock_name); if(retval == null) { System.out.println("not found"); throw new Exception("Stock " + stock_name + " not found"); } System.out.println(retval.floatValue()); return retval.floatValue(); } public void setQuote(String stock_name, Float value) { System.out.println("Setting quote for " + stock_name + ": " + value); stocks.put(stock_name, value); } public Hashtable getAllStocks() { System.out.print("getAllStocks: "); printAllStocks(); return stocks; } public void printAllStocks() { System.out.println(stocks); } public void receive(Message msg) { } public byte[] getState() { try { return Util.objectToByteBuffer(stocks.clone()); } catch(Exception ex) { ex.printStackTrace(); return null; } } public void setState(byte[] state) { try { integrate((Hashtable)Util.objectFromByteBuffer(state)); } catch(Exception ex) { ex.printStackTrace(); } } public static void main(String args[]) { try { QuoteServer server=new QuoteServer(); server.start(); while(true) { Util.sleep(10000); } } catch(Throwable t) { t.printStackTrace(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/KeyStoreGenerator.java0000644000175000017500000000720711366547366027001 0ustar twernertwerner//$Id: KeyStoreGenerator.java,v 1.2 2005/01/07 15:15:26 steview Exp $ package org.jgroups.demos; import java.io.FileOutputStream; import java.io.OutputStream; import java.security.KeyStore; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; /** * Generates a keystore file that has a SecretKey in it. It is not possible to * use the keytool JDk tool to achieve this. This is a simple way to generate * a JCEKS format keystore and SecretKey. * * Usage is --alg ALGNAME --size ALGSIZE --storeName FILENAME --storePass PASSWORD --alias KEYALIAS * * Any of args are optional and will default to *

    *
  • ALGNAME = Blowfish *
  • ALGSIZE = 56 *
  • FILENAME = defaultStore.keystore *
  • PASSWORD = changeit *
  • ALIAS = mykey *
* * @author S Woodcock * */ public class KeyStoreGenerator { static String symAlg = "Blowfish"; static int keySize =56; static String keyStoreName = "defaultStore.keystore"; static String storePass = "changeit"; static String alias = "myKey"; public static void main(String[] args) { int i = 0, j; String arg =null;; boolean specified =false; while (i < args.length && args[i].startsWith("-")) { arg = args[i++]; System.out.println("Found arg of " + arg); if (arg.equalsIgnoreCase("--alg")){ if (i"); else { sb.append('/' + name); if(userObject != null) sb.append(" --> " + userObject); } } sb.append('\n'); if(getChildCount() > 0) { if(isRoot()) indent=0; else indent+=4; for(int i=0; i < getChildCount(); i++) sb.append(((MyNode)getChildAt(i)).print(indent)); } return sb.toString(); } public String toString() { return name; } } /** * Demo showing the DistributedTree class. It displays a panel with the tree structure in the upper half, * and the properties of a chosen node on the bottom half. All updates are broadcast to all members. */ public class DistributedTreeDemo extends Frame implements WindowListener, DistributedTree.DistributedTreeListener, TreeSelectionListener, TableModelListener { DefaultTreeModel tree_model=null; JTree jtree=null; final DefaultTableModel table_model=new DefaultTableModel(); final JTable table=new JTable(table_model); JScrollPane scroll_pane=null; final MyNode root=new MyNode("/"); DistributedTree dt=null; String props=null; String selected_node=null; boolean create=false; public DistributedTreeDemo(boolean create) throws Exception { props="udp.xml"; this.create=create; dt=new DistributedTree("DistributedTreeDemo", props); dt.addDistributedTreeListener(this); setLayout(new BorderLayout()); addNotify(); setSize(getInsets().left + getInsets().right + 485, getInsets().top + getInsets().bottom + 367); setTitle("DistributedTree"); tree_model=new DefaultTreeModel(root); jtree=new JTree(tree_model); jtree.setDoubleBuffered(true); scroll_pane=new JScrollPane(); scroll_pane.getViewport().add(jtree); scroll_pane.setDoubleBuffered(true); add(scroll_pane, BorderLayout.CENTER); addWindowListener(this); table_model.setColumnIdentifiers(new String[]{"Name", "Value"}); table_model.addTableModelListener(this); add(table, BorderLayout.SOUTH); dt.start(); System.out.println("Constructing initial GUI tree"); populateTree(dt, ""); // creates initial GUI from model System.out.println("Constructing initial GUI tree -- done"); Properties props1=new Properties(); props1.setProperty("name", "EventService"); props1.setProperty("path", "/usr/local/Orbix2000/bin/es"); props1.setProperty("up", "true"); props1.setProperty("active", "false"); Properties props2=new Properties(); props2.setProperty("name", "NamingService"); props2.setProperty("path", "/usr/local/Orbix2000/bin/ns"); props2.setProperty("up", "true"); props2.setProperty("active", "true"); Properties props3=new Properties(); props3.setProperty("name", "ORBIX daemon"); props3.setProperty("path", "/usr/local/Orbix2000/bin/orbixd"); props3.setProperty("up", "true"); props3.setProperty("active", "true"); props3.setProperty("restart", "true"); props3.setProperty("restart_time", "3000"); props3.setProperty("restart_max", "10"); Properties props4=new Properties(); props4.setProperty("name", "Orbix2000 Version 1.1"); props4.setProperty("valid until", "11/12/2001"); props4.setProperty("up", "false"); props4.setProperty("active", "false"); Properties props5=new Properties(); props5.setProperty("name", "Orbix2000 Version 1.3b"); props5.setProperty("valid until", "12/31/2000"); props5.setProperty("up", "true"); props5.setProperty("active", "false"); if(create) { dt.add("/procs/NETSMART/es", props1); dt.add("/procs/NETSMART/ns", props2); dt.add("/procs/NETSMART/orbixd", props3); dt.add("/procs/NETSMART/orbixd/Version_1.1", props4); dt.add("/procs/NETSMART/orbixd/Version_1.2", props5); Properties props6=(Properties)props5.clone(); props6.setProperty("name", "osagent daemon"); props6.setProperty("path", "/usr/local/Visigenics/bin/osagent"); Properties props7=new Properties(); props7.setProperty("name", "Visigenics latest product"); props7.setProperty("license", "/vob/iem/Devp/etc/license.txt"); dt.set("/procs/NETSMART/orbixd/Version_1.2", props6); dt.add("/procs/NETSMART/orbixd/Version_2.0", props7); } jtree.addTreeSelectionListener(this); MouseListener ml=new MouseAdapter() { public void mouseClicked(MouseEvent e) { int selRow=jtree.getRowForLocation(e.getX(), e.getY()); TreePath selPath=jtree.getPathForLocation(e.getX(), e.getY()); if(selRow != -1) selected_node=makeFQN(selPath.getPath()); } }; jtree.addMouseListener(ml); } String makeFQN(Object[] path) { StringBuilder sb=new StringBuilder(""); String tmp_name; if(path == null) return null; for(int i=0; i < path.length; i++) { tmp_name=((MyNode)path[i]).name; if("/".equals(tmp_name)) continue; else sb.append('/' + tmp_name); } tmp_name=sb.toString(); if(tmp_name.length() == 0) return "/"; else return tmp_name; } void clearTable() { int num_rows=table.getRowCount(); if(num_rows > 0) { for(int i=0; i < num_rows; i++) table_model.removeRow(0); table_model.fireTableRowsDeleted(0, num_rows - 1); repaint(); } } void populateTable(Properties props) { String key, val; int num_rows=0; if(props == null) return; num_rows=props.size(); clearTable(); if(num_rows > 0) { for(Enumeration e=props.keys(); e.hasMoreElements();) { key=(String)e.nextElement(); val=(String)props.get(key); if(val == null) val=""; table_model.addRow(new Object[]{key, val}); } table_model.fireTableRowsInserted(0, num_rows - 1); validate(); } } void populateTree(DistributedTree tree, String tmp_fqn) { if(tree == null) return; Vector children=tree.getChildrenNames(tmp_fqn); String child_name, tmp_name; Serializable element; for(int i=0; i < children.size(); i++) { child_name=(String)children.elementAt(i); tmp_name=tmp_fqn + '/' + child_name; root.add(tmp_name, tree.get(tmp_name)); populateTree(tree, tmp_name); } } public synchronized void setVisible(boolean show) { setLocation(50, 50); super.setVisible(show); } public void windowClosed(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowActivated(WindowEvent event) { } public void windowDeactivated(WindowEvent event) { } public void windowOpened(WindowEvent event) { } public void windowClosing(WindowEvent event) { dt.stop(); System.exit(0); } public void tableChanged(TableModelEvent evt) { int row, col; String key, val; if(evt.getType() == TableModelEvent.UPDATE) { row=evt.getFirstRow(); col=evt.getColumn(); Properties props=(Properties)dt.get(selected_node); if(col == 0) { // set() key=(String)table_model.getValueAt(row, col); val=(String)table_model.getValueAt(row, col + 1); if(props != null && key != null && val != null) { props.setProperty(key, val); dt.set(selected_node, props); } } else { // add() key=(String)table_model.getValueAt(row, col - 1); val=(String)table.getValueAt(row, col); if(props != null && key != null && val != null) { props.setProperty(key, val); dt.add(selected_node, props); } } System.out.println("key=" + key + ", val=" + val); } } public void valueChanged(TreeSelectionEvent evt) { TreePath path=evt.getPath(); String fqn="/"; String component_name; Properties props=null; for(int i=0; i < path.getPathCount(); i++) { component_name=((MyNode)path.getPathComponent(i)).name; if("/".equals(component_name)) continue; if("/".equals(fqn)) fqn+=component_name; else fqn=fqn + '/' + component_name; } props=(Properties)dt.get(fqn); if(props != null) populateTable(props); else clearTable(); } /* ------------------ DistributedTree.DistributedTreeListener interface ------------ */ public void nodeAdded(String fqn, Serializable element) { MyNode n; System.out.println("** nodeCreated(" + fqn + ')'); root.add(fqn, element); n=root.findNode(fqn); if(n != null) tree_model.reload(n.getParent()); } public void nodeRemoved(String fqn) { MyNode n; TreeNode par; System.out.println("** nodeRemoved(" + fqn + ')'); n=root.findNode(fqn); if(n != null) { n.removeAllChildren(); par=n.getParent(); n.removeFromParent(); tree_model.reload(par); } } public void nodeModified(String fqn, Serializable old_element, Serializable new_element) { System.out.println("** nodeModified(" + fqn + ')'); root.modify(fqn, new_element); populateTable((Properties)new_element); } /* ---------------- End of DistributedTree.DistributedTreeListener interface -------- */ public static void main(String args[]) { DistributedTreeDemo demo; boolean create=false; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { System.out.println("DistributedTreeDemo [-create] [-help]"); return; } if("-create".equals(args[i])) { create=true; continue; } } try { demo=new DistributedTreeDemo(create); demo.setVisible(true); } catch(Exception ex) { System.err.println(ex); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/Gossip.java0000644000175000017500000003654211366547366024635 0ustar twernertwerner// $Id: Gossip.java,v 1.9.14.1 2008/01/22 10:00:55 belaban Exp $ package org.jgroups.demos; import org.jgroups.*; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Random; import java.util.Vector; /** * Demos that tries to graphically illustrating the gossip (or pbcast) protocol: every sender periodically sends * a DRAW command to a random subset of the group members. Each member checks whether it already received the * message and applies it if not yet received. Otherwise it discards it. If not yet received, the message will * be forwarded to 10% of the group members. This demo is probably only interesting when we have a larger * number of members: a gossip will gradually reach all members, coloring their whiteboards. */ public class Gossip implements Runnable, WindowListener, ActionListener, ChannelListener { private Graphics graphics=null; private Frame mainFrame=null; private JPanel panel=null, sub_panel=null; private final ByteArrayOutputStream out=new ByteArrayOutputStream(); private final Random random=new Random(System.currentTimeMillis()); private Button gossip_button, clear_button, leave_button; private final Font default_font=new Font("Helvetica", Font.PLAIN, 12); private final String groupname="GossipGroupDemo"; private Channel channel=null; private Thread receiver=null; private int member_size=1; private final Vector members=new Vector(); private int red=0, green=0, blue=0; private Color default_color=null; boolean first=true; final double subset=0.1; Address local_addr=null; TrafficGenerator gen=null; long traffic_interval=0; public Gossip(String props, long traffic) throws Exception { channel=new JChannel(props); channel.addChannelListener(this); channel.setOpt(Channel.AUTO_RECONNECT, Boolean.TRUE); traffic_interval=traffic; if(traffic_interval > 0) { gen=new TrafficGenerator(); gen.start(); } } public static void main(String[] args) { Gossip gossip=null; String props=null; long traffic=0; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { System.out.println("Gossip [-traffic_interval ] [-help]"); return; } if("-traffic_interval".equals(args[i])) { traffic=Long.parseLong(args[++i]); continue; } } // props="UDP:PING:FD:STABLE:NAKACK:UNICAST:FRAG:FLUSH:GMS:VIEW_ENFORCER:PERF(trace=;details=true)"; /** props="TCP(start_port=8000):" + "TCPPING(num_initial_members=1;timeout=3000;port_range=2;"+ "initial_hosts=daddy[8000],terrapin[8000],sindhu[8000]):" + "FD:" + "pbcast.PBCAST(gossip_interval=5000;gc_lag=50):" + "UNICAST:" + "FRAG:" + "pbcast.GMS"; // "PERF(trace=true;details=true)"; **/ props="UDP(mcast_addr=224.10.10.100;mcast_port=5678;ip_ttl=32):" + "PING:" + // "FD(shun=true;timeout=5000):" + "pbcast.FD(timeout=3000):" + "VERIFY_SUSPECT(timeout=2000;num_msgs=2):" + "pbcast.PBCAST(desired_avg_gossip=8000;mcast_gossip=true;gc_lag=30;max_queue=20):" + "UNICAST:" + "FRAG:" + "pbcast.GMS"; // :" + // ;join_timeout=20):" + // "PERF(trace=true;details=true)"; try { gossip=new Gossip(props, traffic); gossip.go(); } catch(Exception e) { System.err.println(e); System.exit(0); } } private void selectColor() { red=(Math.abs(random.nextInt()) % 255); green=(Math.abs(random.nextInt()) % 255); blue=(Math.abs(random.nextInt()) % 255); default_color=new Color(red, green, blue); } public void go() { try { channel.connect(groupname); local_addr=channel.getLocalAddress(); startThread(); mainFrame=new Frame(); panel=new MyPanel(); sub_panel=new JPanel(); mainFrame.setSize(250, 250); mainFrame.add("Center", panel); clear_button=new Button("Clear"); clear_button.setFont(default_font); clear_button.addActionListener(this); gossip_button=new Button("Gossip"); gossip_button.setFont(default_font); gossip_button.addActionListener(this); leave_button=new Button("Leave & Exit"); leave_button.setFont(default_font); leave_button.addActionListener(this); sub_panel.add("South", gossip_button); sub_panel.add("South", clear_button); sub_panel.add("South", leave_button); mainFrame.add("South", sub_panel); mainFrame.addWindowListener(this); mainFrame.setVisible(true); setTitle(); graphics=panel.getGraphics(); graphics.setColor(default_color); mainFrame.setBackground(Color.white); mainFrame.pack(); gossip_button.setForeground(Color.blue); clear_button.setForeground(Color.blue); leave_button.setForeground(Color.blue); } catch(Exception e) { System.err.println(e); return; } } void startThread() { receiver=new Thread(this, "GossipThread"); receiver.setPriority(Thread.MAX_PRIORITY); receiver.start(); } void setTitle() { String title=""; if(local_addr != null) title+=local_addr; title+=" (" + member_size + ") mbrs"; mainFrame.setTitle(title); } public void run() { Object tmp; Message msg=null; Command comm; boolean fl=true; Vector mbrs; ObjectOutputStream os; while(fl) { try { tmp=channel.receive(0); // System.out.println("Gossip.run(): received " + tmp); if(tmp == null) continue; if(tmp instanceof View) { View v=(View)tmp; member_size=v.size(); mbrs=v.getMembers(); members.removeAllElements(); for(int i=0; i < mbrs.size(); i++) members.addElement(mbrs.elementAt(i)); if(mainFrame != null) setTitle(); continue; } if(tmp instanceof ExitEvent) { // System.out.println("-- Gossip.main(): received EXIT, waiting for ChannelReconnected callback"); break; } if(!(tmp instanceof Message)) continue; msg=(Message)tmp; comm=null; Object obj=msg.getObject(); // System.out.println("obj is " + obj); if(obj instanceof Command) comm=(Command)obj; else if(obj instanceof Message) { System.out.println("*** Message is " + Util.printMessage((Message)obj)); Util.dumpStack(true); } else { if(obj != null) System.out.println("obj is " + obj.getClass() + ", hdrs are" + msg.printObjectHeaders()); else System.out.println("hdrs are" + msg.printObjectHeaders()); Util.dumpStack(true); } switch(comm.mode) { case Command.GOSSIP: if(graphics != null) { colorPanel(comm.r, comm.g, comm.b); comm.not_seen.removeElement(local_addr); if(comm.not_seen.size() > 0) { // forward gossip Vector v=Util.pickSubset(comm.not_seen, subset); out.reset(); os=new ObjectOutputStream(out); os.writeObject(comm); os.flush(); for(int i=0; i < v.size(); i++) { channel.send(new Message((Address)v.elementAt(i), null, out.toByteArray())); } } } break; case Command.CLEAR: clearPanel(); continue; default: System.err.println("***** Gossip.run(): received invalid draw command " + comm.mode); break; } } catch(ChannelNotConnectedException not) { System.err.println("Gossip: " + not); break; } catch(ChannelClosedException closed) { System.err.println("Gossip: channel was closed"); break; } catch(Exception e) { System.err.println(e); continue; // break; } } } /* --------------- Callbacks --------------- */ public void mouseMoved(MouseEvent e) { } public void clearPanel() { Rectangle bounds=null; if(panel == null || graphics == null) return; bounds=panel.getBounds(); graphics.clearRect(0, 0, bounds.width, bounds.height); } public void colorPanel(int r, int g, int b) { if(graphics != null) { red=r; green=g; blue=b; graphics.setColor(new Color(red, green, blue)); Rectangle bounds=panel.getBounds(); graphics.fillRect(0, 0, bounds.width, bounds.height); graphics.setColor(default_color); } } void sendGossip() { int tmp[]=new int[1]; tmp[0]=0; Command comm; ObjectOutputStream os; Vector dests=(Vector)members.clone(); try { selectColor(); // set a new randomly chosen color dests.removeElement(local_addr); dests=Util.pickSubset(dests, subset); if(dests == null || dests.size() == 0) { // only apply new color locally // System.out.println("-- local"); colorPanel(red, green, blue); return; } colorPanel(red, green, blue); comm=new Command(Command.GOSSIP, red, green, blue); comm.not_seen=(Vector)members.clone(); comm.not_seen.removeElement(local_addr); out.reset(); os=new ObjectOutputStream(out); os.writeObject(comm); os.flush(); for(int i=0; i < dests.size(); i++) { channel.send(new Message((Address)dests.elementAt(i), null, out.toByteArray())); } } catch(Exception ex) { System.err.println(ex); } } public void sendClearPanelMsg() { int tmp[]=new int[1]; tmp[0]=0; Command comm=new Command(Command.CLEAR); ObjectOutputStream os; try { out.reset(); os=new ObjectOutputStream(out); os.writeObject(comm); os.flush(); channel.send(new Message(null, null, out.toByteArray())); } catch(Exception ex) { System.err.println(ex); } } public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowClosing(WindowEvent e) { System.exit(0); // exit the dirty way ... } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if("Gossip".equals(command)) { sendGossip(); } else if("Clear".equals(command)) sendClearPanelMsg(); else if("Leave & Exit".equals(command)) { try { channel.disconnect(); channel.close(); } catch(Exception ex) { System.err.println(ex); } mainFrame.setVisible(false); System.exit(0); } else System.out.println("Unknown action"); } public void channelConnected(Channel channel) { if(first) first=false; else startThread(); } public void channelDisconnected(Channel channel) { // System.out.println("----> channelDisconnected()"); } public void channelClosed(Channel channel) { // System.out.println("----> channelClosed()"); } public void channelShunned() { System.out.println("----> channelShunned()"); } public void channelReconnected(Address new_addr) { System.out.println("----> channelReconnected(" + new_addr + ')'); local_addr=new_addr; } private static class Command implements Serializable { static final int GOSSIP=1; static final int CLEAR=2; final int mode; int r=0; int g=0; int b=0; Vector not_seen=new Vector(); Command(int mode) { this.mode=mode; } Command(int mode, int r, int g, int b) { this.mode=mode; this.r=r; this.g=g; this.b=b; } public String toString() { StringBuilder ret=new StringBuilder(); switch(mode) { case GOSSIP: ret.append("GOSSIP(" + r + '|' + g + '|' + b); break; case CLEAR: ret.append("CLEAR"); break; default: return ""; } ret.append(", not_seen=" + not_seen); return ret.toString(); } } private class TrafficGenerator implements Runnable { Thread generator=null; public void start() { if(generator == null) { generator=new Thread(this, "TrafficGeneratorThread"); generator.start(); } } public void stop() { if(generator != null) generator=null; generator=null; } public void run() { while(generator != null) { Util.sleep(traffic_interval); if(generator != null) sendGossip(); } } } private class MyPanel extends JPanel { final Dimension preferred_size=new Dimension(200, 200); public Dimension getPreferredSize() { return preferred_size; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/demos/DistributedQueueDemo.java0000644000175000017500000001723511366547366027463 0ustar twernertwerner// $Id: DistributedQueueDemo.java,v 1.7 2007/07/23 08:28:35 belaban Exp $ package org.jgroups.demos; import org.jgroups.ChannelException; import org.jgroups.ChannelFactory; import org.jgroups.JChannelFactory; import org.jgroups.blocks.DistributedQueue; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Collection; import java.util.Vector; /** * Uses the DistributedQueue building block. The latter subclasses org.jgroups.util.Queue and overrides * the methods that modify the queue (e.g. add()). Those methods are multicast to the group, whereas * read-only methods such as peek() use the local copy. A DistributedQueue is created given the name * of a group; all queues with the same name find each other and form a group. * @author Romuald du Song */ public class DistributedQueueDemo extends Frame implements WindowListener, ActionListener, DistributedQueue.Notification { DistributedQueue h = null; final JButton add = new JButton("Add"); final JButton quit = new JButton("Quit"); final JButton get_all = new JButton("All"); final JButton remove = new JButton("Remove"); final JLabel value = new JLabel("Value"); final JLabel err_msg = new JLabel("Error"); final JTextField value_field = new JTextField(); final java.awt.List listbox = new java.awt.List(); final Font default_font = new Font("Helvetica", Font.PLAIN, 12); public DistributedQueueDemo() { super(); addWindowListener(this); } private void showMsg(String msg) { err_msg.setText(msg); err_msg.setVisible(true); } private void clearMsg() { err_msg.setVisible(false); } private void removeItem() { h.remove(); } private void showAll() { if (listbox.getItemCount() > 0) { listbox.removeAll(); } if (h.size() == 0) { return; } clearMsg(); String key; Vector v = h.getContents(); for (int i = 0; i < v.size(); i++) { listbox.add((String)v.elementAt(i)); } } public void start(String groupname, ChannelFactory factory, String props) throws ChannelException { h = new DistributedQueue(groupname, factory, props, 10000); h.addNotifier(this); setLayout(null); setSize(400, 300); setFont(default_font); value.setBounds(new Rectangle(10, 60, 60, 30)); value_field.setBounds(new Rectangle(100, 60, 100, 30)); listbox.setBounds(new Rectangle(210, 30, 150, 160)); err_msg.setBounds(new Rectangle(10, 200, 350, 30)); err_msg.setFont(new Font("Helvetica", Font.ITALIC, 12)); err_msg.setForeground(Color.red); err_msg.setVisible(false); add.setBounds(new Rectangle(60, 250, 60, 30)); quit.setBounds(new Rectangle(130, 250, 70, 30)); get_all.setBounds(new Rectangle(210, 250, 60, 30)); remove.setBounds(new Rectangle(280, 250, 90, 30)); add.addActionListener(this); quit.addActionListener(this); get_all.addActionListener(this); remove.addActionListener(this); add(value); add(value_field); add(err_msg); add(add); add(quit); add(get_all); add(remove); add(listbox); setTitle("DistributedQueue Demo"); showAll(); // pack(); setVisible(true); /* new Thread() { public void run() { System.out.println("-- sleeping"); Util.sleep(10000); for(int i=0; i < 10; i++) { System.out.println("-- add()"); h.add("Bela#" + i); } while(true) { Util.sleep(500); try { System.out.println(h.remove()); } catch (QueueClosedException e) { e.printStackTrace(); } } } }.start(); */ } public void windowActivated(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowClosing(WindowEvent e) { System.exit(0); } public void windowDeactivated(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); try { if (command == "Add") { String value_name = value_field.getText(); if ((value_name == null) || (value_name.length() == 0)) { showMsg("Value is empty !"); return; } showMsg("Adding value " + value_name + ':'); h.add(value_name); } else if (command == "All") { showAll(); } else if (command == "Quit") { setVisible(false); System.exit(0); } else if (command == "Remove") { removeItem(); } else { System.out.println("Unknown action"); } } catch (Exception ex) { value_field.setText(""); showMsg(ex.toString()); } } public void entryAdd(Object value) { showAll(); } public void entryRemoved(Object key) { showAll(); } public void viewChange(Vector joined, Vector left) { System.out.println("New members: " + joined + ", left members: " + left); } public void contentsSet(Collection new_entries) { System.out.println("Contents Set:" + new_entries); } public void contentsCleared() { System.out.println("Contents cleared()"); } public static void main(String[] args) { String groupname = "QueueDemo"; DistributedQueueDemo client = new DistributedQueueDemo(); ChannelFactory factory = new JChannelFactory(); String arg; String next_arg; boolean trace = false; boolean persist = false; String props ="udp.xml"; try { for (int i = 0; i < args.length; i++) { arg = args[i]; if ("-trace".equals(arg)) { trace = true; continue; } if ("-groupname".equals(args[i])) { groupname = args[++i]; continue; } help(); return; } } catch (Exception e) { help(); return; } try { client.start(groupname, factory, props); } catch (Throwable t) { t.printStackTrace(); } } static void help() { System.out.println("DistributedQueueDemo [-help]"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ChannelListenerAdapter.java0000644000175000017500000000103411366547366026625 0ustar twernertwernerpackage org.jgroups; /** * Class which implements {@link org.jgroups.ChannelListener} * @author Bela Ban * @version $Id: ChannelListenerAdapter.java,v 1.1 2007/08/21 09:23:08 belaban Exp $ */ public class ChannelListenerAdapter implements ChannelListener { public void channelConnected(Channel channel) { } public void channelDisconnected(Channel channel) { } public void channelClosed(Channel channel) { } public void channelShunned() { } public void channelReconnected(Address addr) { } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ExtendedMessageListener.java0000644000175000017500000000546611366547366027036 0ustar twernertwernerpackage org.jgroups; import java.io.InputStream; import java.io.OutputStream; /** * * ExtendedMessageListener has additional callbacks for: *
    *
  • partial state transfer - http://jira.jboss.com/jira/browse/JGRP-118 *
  • streaming state transfer - http://jira.jboss.com/jira/browse/JGRP-89 *
*

* Application channels interested in using streaming state transfer, beside * implementing this interface, have to be configured with * STREAMING_STATE_TRANSFER protocol rather than the default * STATE_TRANSFER protocol. * *

* Note: *

* This interface will be merged with MessageListener in 3.0 (API changes) * * @author Bela Ban * @author Vladimir Blagojevic * @see org.jgroups.JChannel#getState(Address, long) * @see org.jgroups.JChannel#getState(Address, String, long) * @since 2.3 * * @version $Id: ExtendedMessageListener.java,v 1.4 2006/07/28 07:14:33 belaban Exp $ */ public interface ExtendedMessageListener extends MessageListener { /** * Allows an application to provide a partial state as a byte array * * @param state_id id of the partial state requested * @return partial state for the given state_id */ public byte[] getState(String state_id); /** * Allows an application to read a partial state indicated by state_id from * a given state byte array parameter. * * @param state_id id of the partial state requested * @param state partial state for the given state_id */ public void setState(String state_id, byte[] state); /** * Allows an application to write a state through a provided OutputStream. * An application is obligated to always close the given OutputStream reference. * * @param ostream the OutputStream * @see OutputStream#close() */ public void getState(OutputStream ostream); /** * Allows an application to write a partial state through a provided OutputStream. * An application is obligated to always close the given OutputStream reference. * * @param state_id id of the partial state requested * @param ostream the OutputStream * * @see OutputStream#close() */ public void getState(String state_id, OutputStream ostream); /** * Allows an application to read a state through a provided InputStream. * An application is obligated to always close the given InputStream reference. * * @param istream the InputStream * @see InputStream#close() */ public void setState(InputStream istream); /** * Allows an application to read a partial state through a provided InputStream. * An application is obligated to always close the given InputStream reference. * * @param state_id id of the partial state requested * @param istream the InputStream * * @see InputStream#close() */ public void setState(String state_id, InputStream istream); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Header.java0000644000175000017500000000250711366547366023444 0ustar twernertwerner// $Id: Header.java,v 1.10 2007/05/01 10:55:19 belaban Exp $ package org.jgroups; import java.io.Externalizable; /** Abstract base class for all headers to be added to a Message. @author Bela Ban */ public abstract class Header implements Externalizable { public static final int HDR_OVERHEAD=100; // estimated size of a header (used to estimate the size of the entire msg) public Header() { } /** * To be implemented by subclasses. Return the size of this object for the serialized version of it. * I.e. how many bytes this object takes when flattened into a buffer. This may be different for each instance, * or can be the same. This may also just be an estimation. E.g. FRAG uses it on Message to determine whether * or not to fragment the message. Fragmentation itself will be accurate, because the entire message will actually * be serialized into a byte buffer, so we can determine the exact size. */ public int size() { return HDR_OVERHEAD; } // public void writeTo(DataOutputStream out) throws IOException { // ; // } // // public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { // ; // } public String toString() { return '[' + getClass().getName() + " Header]"; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/UnblockEvent.java0000644000175000017500000000040211366547366024643 0ustar twernertwernerpackage org.jgroups; /** * Trivial object that represents a block event. * @author Bela Ban * @version $Id: UnblockEvent.java,v 1.1 2006/09/27 12:33:06 belaban Exp $ */ public class UnblockEvent { public String toString() {return "UnblockEvent";} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Message.java0000644000175000017500000006034111366547366023640 0ustar twernertwerner package org.jgroups; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.conf.ClassConfigurator; import org.jgroups.stack.IpAddress; import org.jgroups.util.*; import java.io.*; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * A Message encapsulates data sent to members of a group. It contains among other things the * address of the sender, the destination address, a payload (byte buffer) and a list of * headers. Headers are added by protocols on the sender side and removed by protocols * on the receiver's side. *

* The byte buffer can point to a reference, and we can subset it using index and length. However, * when the message is serialized, we only write the bytes between index and length. * @author Bela Ban * @version $Id: Message.java,v 1.76.2.11 2009/12/17 16:30:09 belaban Exp $ */ public class Message implements Externalizable, Streamable { protected Address dest_addr=null; protected Address src_addr=null; /** The payload */ private byte[] buf=null; /** The index into the payload (usually 0) */ protected transient int offset=0; /** The number of bytes in the buffer (usually buf.length is buf not equal to null). */ protected transient int length=0; /** All headers are placed here */ protected Headers headers; protected static final Log log=LogFactory.getLog(Message.class); private static final long serialVersionUID=7966206671974139740L; static final byte DEST_SET = 1; static final byte SRC_SET = 2; static final byte BUF_SET = 4; // static final byte HDRS_SET=8; // bela July 15 2005: not needed, we always create headers static final byte IPADDR_DEST =16; static final byte IPADDR_SRC =32; static final byte SRC_HOST_NULL =64; // =========================== Flags ============================== public static final byte OOB = 1; public static final byte LOW_PRIO = 2; // not yet sure if we want this flag... public static final byte HIGH_PRIO = 4; // not yet sure if we want this flag... private byte flags=0; static final Set nonStreamableHeaders=new HashSet(); /** Public constructor * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

*/ public Message(Address dest) { setDest(dest); headers=createHeaders(3); } /** Public constructor * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

* @param src Address of sender * @param buf Message to be sent. Note that this buffer must not be modified (e.g. buf[0]=0 is * not allowed), since we don't copy the contents on clopy() or clone(). */ public Message(Address dest, Address src, byte[] buf) { this(dest); setSrc(src); setBuffer(buf); } /** * Constructs a message. The index and length parameters allow to provide a reference to * a byte buffer, rather than a copy, and refer to a subset of the buffer. This is important when * we want to avoid copying. When the message is serialized, only the subset is serialized.
* * Note that the byte[] buffer passed as argument must not be modified. Reason: if we retransmit the * message, it would still have a ref to the original byte[] buffer passed in as argument, and so we would * retransmit a changed byte[] buffer ! * * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

* @param src Address of sender * @param buf A reference to a byte buffer * @param offset The index into the byte buffer * @param length The number of bytes to be used from buf. Both index and length are checked for * array index violations and an ArrayIndexOutOfBoundsException will be thrown if invalid */ public Message(Address dest, Address src, byte[] buf, int offset, int length) { this(dest); setSrc(src); setBuffer(buf, offset, length); } /** Public constructor * @param dest Address of receiver. If it is null then the message sent to the group. * Otherwise, it contains a single destination and is sent to that member.

* @param src Address of sender * @param obj The object will be serialized into the byte buffer. Object * has to be serializable ! The resulting buffer must not be modified * (e.g. buf[0]=0 is not allowed), since we don't copy the contents on clopy() or clone().

* Note that this is a convenience method and JGroups will use default Java serialization to * serialize obj into a byte buffer. */ public Message(Address dest, Address src, Serializable obj) { this(dest); setSrc(src); setObject(obj); } public Message() { headers=createHeaders(3); } public Message(boolean create_headers) { if(create_headers) headers=createHeaders(3); } public Address getDest() { return dest_addr; } public void setDest(Address new_dest) { dest_addr=new_dest; } public Address getSrc() { return src_addr; } public void setSrc(Address new_src) { src_addr=new_src; } /** * Returns a reference to the payload (byte buffer). Note that this buffer should not be modified as * we do not copy the buffer on copy() or clone(): the buffer of the copied message is simply a reference to * the old buffer.
* Even if offset and length are used: we return the entire buffer, not a subset. */ public byte[] getRawBuffer() { return buf; } /** * Returns a copy of the buffer if offset and length are used, otherwise a reference. * @return byte array with a copy of the buffer. */ final public byte[] getBuffer() { if(buf == null) return null; if(offset == 0 && length == buf.length) return buf; else { byte[] retval=new byte[length]; System.arraycopy(buf, offset, retval, 0, length); return retval; } } final public void setBuffer(byte[] b) { buf=b; if(buf != null) { offset=0; length=buf.length; } else { offset=length=0; } } /** * Set the internal buffer to point to a subset of a given buffer * @param b The reference to a given buffer. If null, we'll reset the buffer to null * @param offset The initial position * @param length The number of bytes */ final public void setBuffer(byte[] b, int offset, int length) { buf=b; if(buf != null) { if(offset < 0 || offset > buf.length) throw new ArrayIndexOutOfBoundsException(offset); if((offset + length) > buf.length) throw new ArrayIndexOutOfBoundsException((offset+length)); this.offset=offset; this.length=length; } else { this.offset=this.length=0; } } /** * Note that the byte[] buffer passed as argument must not be modified. Reason: if we retransmit the * message, it would still have a ref to the original byte[] buffer passed in as argument, and so we would * retransmit a changed byte[] buffer ! * */ public final void setBuffer(Buffer buf) { if(buf != null) { this.buf=buf.getBuf(); this.offset=buf.getOffset(); this.length=buf.getLength(); } } /** Returns the offset into the buffer at which the data starts */ public int getOffset() { return offset; } /** Returns the number of bytes in the buffer */ public int getLength() { return length; } /** Returns a reference to the headers hashmap, which is immutable. Any attempt to * modify the returned map will cause a runtime exception */ public Map getHeaders() { return headers.getHeaders(); } public String printHeaders() { return headers.printHeaders(); } public int getNumHeaders() { return headers.size(); } /** * Takes an object and uses Java serialization to generate the byte[] buffer which is set in the message. */ final public void setObject(Serializable obj) { if(obj == null) return; try { byte[] tmp=Util.objectToByteBuffer(obj); setBuffer(tmp); } catch(Exception ex) { throw new IllegalArgumentException(ex); } } /** * Uses Java serialization to create an object from the buffer of the message. Note that this is dangerous when * using your own classloader, e.g. inside of an application server ! Most likely, JGroups will use the system * classloader to deserialize the buffer into an object, whereas (for example) a web application will want to * use the webapp's classloader, resulting in a ClassCastException. The recommended way is for the application to * use their own serialization and only pass byte[] buffer to JGroups. * @return */ final public Object getObject() { try { return Util.objectFromByteBuffer(buf, offset, length); } catch(Exception ex) { throw new IllegalArgumentException(ex); } } public void setFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); flags |= flag; } public void clearFlag(byte flag) { if(flag > Byte.MAX_VALUE || flag < 0) throw new IllegalArgumentException("flag has to be >= 0 and <= " + Byte.MAX_VALUE); // if(isFlagSet(flag)) { // flags ^= flag; // } flags &= ~flag; } public boolean isFlagSet(byte flag) { return (flags & flag) == flag; } public byte getFlags() { return flags; } /*---------------------- Used by protocol layers ----------------------*/ /** Puts a header given a key into the hashmap. Overwrites potential existing entry. */ public void putHeader(String key, Header hdr) { headers.putHeader(key, hdr); } /** * Puts a header given a key into the map, only if the key doesn't exist yet * @param key * @param hdr * @return the previous value associated with the specified key, or * null if there was no mapping for the key. * (A null return can also indicate that the map * previously associated null with the key, * if the implementation supports null values.) */ public Header putHeaderIfAbsent(String key, Header hdr) { return headers.putHeaderIfAbsent(key, hdr); } /** * * @param key * @return the header assoaicted with key * @deprecated Use getHeader() instead. The issue with removing a header is described in * http://jira.jboss.com/jira/browse/JGRP-393 */ public Header removeHeader(String key) { return getHeader(key); } public Header getHeader(String key) { return headers.getHeader(key); } /*---------------------------------------------------------------------*/ public Message copy() { return copy(true); } /** * Create a copy of the message. If offset and length are used (to refer to another buffer), the copy will * contain only the subset offset and length point to, copying the subset into the new copy. * @param copy_buffer * @return Message with specified data */ public Message copy(boolean copy_buffer) { Message retval=new Message(false); retval.dest_addr=dest_addr; retval.src_addr=src_addr; retval.flags=flags; if(copy_buffer && buf != null) { // change bela Feb 26 2004: we don't resolve the reference retval.setBuffer(buf, offset, length); } retval.headers=createHeaders(headers); return retval; } protected Object clone() throws CloneNotSupportedException { return copy(); } public Message makeReply() { return new Message(src_addr); } public String toString() { StringBuilder ret=new StringBuilder(64); ret.append("[dst: "); if(dest_addr == null) ret.append(""); else ret.append(dest_addr); ret.append(", src: "); if(src_addr == null) ret.append(""); else ret.append(src_addr); int size; if((size=getNumHeaders()) > 0) ret.append(" (").append(size).append(" headers)"); ret.append(", size="); if(buf != null && length > 0) ret.append(length); else ret.append('0'); ret.append(" bytes"); if(flags > 0) ret.append(", flags=").append(flagsToString()); ret.append(']'); return ret.toString(); } /** Tries to read an object from the message's buffer and prints it */ public String toStringAsObject() { if(buf == null) return null; try { Object obj=getObject(); return obj != null ? obj.toString() : ""; } catch(Exception e) { // it is not an object return ""; } } /** * Returns size of buffer, plus some constant overhead for src and dest, plus number of headers time * some estimated size/header. The latter is needed because we don't want to marshal all headers just * to find out their size requirements. If a header implements Sizeable, the we can get the correct * size.

Size estimations don't have to be very accurate since this is mainly used by FRAG to * determine whether to fragment a message or not. Fragmentation will then serialize the message, * therefore getting the correct value. */ /** * Returns the exact size of the marshalled message. Uses method size() of each header to compute the size, so if * a Header subclass doesn't implement size() we will use an approximation. However, most relevant header subclasses * have size() implemented correctly. (See org.jgroups.tests.SizeTest). * @return The number of bytes for the marshalled message */ public long size() { long retval=Global.BYTE_SIZE // leading byte + Global.BYTE_SIZE // flags + length // buffer + (buf != null? Global.INT_SIZE : 0); // if buf != null 4 bytes for length // if(dest_addr != null) // retval+=dest_addr.size(); if(src_addr != null) retval+=(src_addr).size(); retval+=Global.SHORT_SIZE; // size (short) retval+=headers.marshalledSize(); return retval; } public String printObjectHeaders() { return headers.printObjectHeaders(); } /* ----------------------------------- Interface Externalizable ------------------------------- */ public void writeExternal(ObjectOutput out) throws IOException { int len; Externalizable hdr; if(dest_addr != null) { out.writeBoolean(true); Marshaller.write(dest_addr, out); } else { out.writeBoolean(false); } if(src_addr != null) { out.writeBoolean(true); Marshaller.write(src_addr, out); } else { out.writeBoolean(false); } out.write(flags); if(buf == null) out.writeInt(0); else { out.writeInt(length); out.write(buf, offset, length); } len=headers.size(); out.writeInt(len); final Object[] data=headers.getRawData(); for(int i=0; i < data.length; i+=2) { if(data[i] != null) { out.writeUTF((String)data[i]); hdr=(Externalizable)data[i+1]; Marshaller.write(hdr, out); } } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { boolean destAddressExist=in.readBoolean(); if(destAddressExist) { dest_addr=(Address)Marshaller.read(in); } boolean srcAddressExist=in.readBoolean(); if(srcAddressExist) { src_addr=(Address)Marshaller.read(in); } flags=in.readByte(); int i=in.readInt(); if(i != 0) { buf=new byte[i]; in.readFully(buf); offset=0; length=buf.length; } int len=in.readInt(); while(len-- > 0) { String key=in.readUTF(); Header value=(Header)Marshaller.read(in); headers.putHeader(key, value); } } /* --------------------------------- End of Interface Externalizable ----------------------------- */ /* ----------------------------------- Interface Streamable ------------------------------- */ /** * Streams all members (dest and src addresses, buffer and headers) to the output stream. * @param out * @throws IOException */ public void writeTo(DataOutputStream out) throws IOException { byte leading=0; if(src_addr != null) { leading+=SRC_SET; if(src_addr instanceof IpAddress) { leading+=IPADDR_SRC; if(((IpAddress)src_addr).getIpAddress() == null) { leading+=SRC_HOST_NULL; } } } if(buf != null) leading+=BUF_SET; // 1. write the leading byte first out.write(leading); // the flags (e.g. OOB, LOW_PRIO) out.write(flags); // 3. src_addr if(src_addr != null) { if(src_addr instanceof IpAddress) { src_addr.writeTo(out); } else { Util.writeAddress(src_addr, out); } } // 4. buf if(buf != null) { out.writeInt(length); out.write(buf, offset, length); } // 5. headers int size=headers.size(); out.writeShort(size); final Object[] data=headers.getRawData(); for(int i=0; i < data.length; i+=2) { if(data[i] != null) { out.writeUTF((String)data[i]); writeHeader((Header)data[i+1], out); } } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { int len, leading; String hdr_name; Header hdr; // 1. read the leading byte first leading=in.readByte(); flags=in.readByte(); // 2. src_addr if((leading & SRC_SET) == SRC_SET) { if((leading & IPADDR_SRC) == IPADDR_SRC) { src_addr=new IpAddress(); src_addr.readFrom(in); } else { src_addr=Util.readAddress(in); } } // 3. buf if((leading & BUF_SET) == BUF_SET) { len=in.readInt(); buf=new byte[len]; in.readFully(buf, 0, len); length=len; } // 4. headers len=in.readShort(); headers=createHeaders(len); Object[] data=headers.getRawData(); int index=0; for(int i=0; i < len; i++) { hdr_name=in.readUTF(); data[index++]=hdr_name; hdr=readHeader(in); data[index++]=hdr; // headers.putHeader(hdr_name, hdr); } } /* --------------------------------- End of Interface Streamable ----------------------------- */ /* ----------------------------------- Private methods ------------------------------- */ private String flagsToString() { StringBuilder sb=new StringBuilder(); boolean first=true; if(isFlagSet(OOB)) { first=false; sb.append("OOB"); } if(isFlagSet(LOW_PRIO)) { if(!first) sb.append("|"); else first=false; sb.append("LOW_PRIO"); } if(isFlagSet(HIGH_PRIO)) { if(!first) sb.append("|"); else first=false; sb.append("HIGH_PRIO"); } return sb.toString(); } private static void writeHeader(Header value, DataOutputStream out) throws IOException { short magic_number; String classname; ObjectOutputStream oos=null; int size=value.size(); try { magic_number=ClassConfigurator.getInstance(false).getMagicNumber(value.getClass()); // write the magic number or the class name out.writeShort(magic_number); if(magic_number == -1) { classname=value.getClass().getName(); out.writeUTF(classname); if(log.isWarnEnabled()) log.warn("magic number for " + classname + " not found, make sure you add your header to " + "jg-magic-map.xml, or register it programmatically with the ClassConfigurator"); } out.writeShort(size); // write the contents if(value instanceof Streamable) { ((Streamable)value).writeTo(out); } else { oos=new ObjectOutputStream(out); value.writeExternal(oos); if(!nonStreamableHeaders.contains(value.getClass())) { nonStreamableHeaders.add(value.getClass()); if(log.isTraceEnabled()) log.trace("encountered non-Streamable header: " + value.getClass()); } } } catch(ChannelException e) { IOException io_ex=new IOException("failed writing header"); io_ex.initCause(e); throw io_ex; } finally { if(oos != null) oos.close(); // this is a no-op on ByteArrayOutputStream } } private static Header readHeader(DataInputStream in) throws IOException { Header hdr; short magic_number; String classname; Class clazz; ObjectInputStream ois=null; try { magic_number=in.readShort(); if(magic_number != -1) { clazz=ClassConfigurator.getInstance(false).get(magic_number); if(clazz == null) throw new IllegalArgumentException("magic number " + magic_number + " is not available in magic map"); } else { classname=in.readUTF(); clazz=ClassConfigurator.getInstance(false).get(classname); } in.readShort(); // we discard the size since we don't use it hdr=(Header)clazz.newInstance(); if(hdr instanceof Streamable) { ((Streamable)hdr).readFrom(in); } else { ois=new ObjectInputStream(in); hdr.readExternal(ois); } } catch(Exception ex) { IOException io_ex=new IOException("failed reading header"); io_ex.initCause(ex); throw io_ex; } return hdr; } private static Headers createHeaders(int size) { return size > 0? new Headers(size) : new Headers(3); } private static Headers createHeaders(Headers m) { return new Headers(m); } /* ------------------------------- End of Private methods ---------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Channel.java0000644000175000017500000005633411366547366023633 0ustar twernertwerner// $Id: Channel.java,v 1.37.2.3 2008/02/07 09:15:26 belaban Exp $ package org.jgroups; import org.apache.commons.logging.Log; import java.io.Serializable; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; /** A channel represents a group communication endpoint (like BSD datagram sockets). A client joins a group by connecting the channel to a group address and leaves it by disconnecting. Messages sent over the channel are received by all group members that are connected to the same group (that is, all members that have the same group address).

The FSM for a channel is roughly as follows: a channel is created (unconnected). The channel is connected to a group (connected). Messages can now be sent and received. The channel is disconnected from the group (unconnected). The channel could now be connected to a different group again. The channel is closed (closed).

Only a single sender is allowed to be connected to a channel at a time, but there can be more than one channel in an application.

Messages can be sent to the group members using the send method and messages can be received using receive (pull approach).

A channel instance is created using either a ChannelFactory or the public constructor. Each implementation of a channel must provide a subclass of Channel and an implementation of ChannelFactory.

Various degrees of sophistication in message exchange can be achieved using building blocks on top of channels; e.g., light-weight groups, synchronous message invocation, or remote method calls. Channels are on the same abstraction level as sockets, and should really be simple to use. Higher-level abstractions are all built on top of channels. @author Bela Ban @see java.net.DatagramPacket @see java.net.MulticastSocket */ public abstract class Channel implements Transport { public static final int BLOCK=0; public static final int VIEW=1; public static final int SUSPECT=2; public static final int LOCAL=3; public static final int GET_STATE_EVENTS=4; public static final int AUTO_RECONNECT=5; public static final int AUTO_GETSTATE=6; protected UpHandler up_handler=null; // when set, all events are passed to it ! protected Set channel_listeners=null; protected Receiver receiver=null; protected abstract Log getLog(); /** Connects the channel to a group. The client is now able to receive group messages, views and block events (depending on the options set) and to send messages to (all or single) group members. This is a null operation if already connected.

All channels with the same name form a group, that means all messages sent to the group will be received by all channels connected to the same channel name.

@param cluster_name The name of the chanel to connect to. @exception ChannelException The protocol stack cannot be started @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. A new channel has to be created first. @see Channel#disconnect */ abstract public void connect(String cluster_name) throws ChannelException; /** * Connects the channel to a group and fetches the state * * @param cluster_name * The name of the cluster to connect to. * @param target * The address of the member from which the state is to be * retrieved. If it is null, the state is retrieved from coordinator is contacted. * @param state_id * The ID of a substate. If the full state is to be fetched, set * this parameter to null * @param timeout * Milliseconds to wait for the state response (0 = wait indefinitely). * * @throws ChannelException thrown if connecting to cluster was not successful * @throws StateTransferException thrown if state transfer was not successful * */ abstract public void connect(String cluster_name, Address target, String state_id, long timeout) throws ChannelException; /** Disconnects the channel from the current group (if connected), leaving the group. It is a null operation if not connected. It is a null operation if the channel is closed. @see #connect(String) */ abstract public void disconnect(); /** Destroys the channel and its associated resources (e.g., the protocol stack). After a channel has been closed, invoking methods on it throws the ChannelClosed exception (or results in a null operation). It is a null operation if the channel is already closed.

If the channel is connected to a group, disconnec()t will be called first. */ abstract public void close(); /** Shuts down the channel without disconnecting if connected, stops all the threads */ abstract public void shutdown(); /** Re-opens a closed channel. Throws an exception if the channel is already open. After this method returns, connect() may be called to join a group. The address of this member will be different from the previous incarnation. */ public void open() throws ChannelException { ; } /** Determines whether the channel is open; i.e., the protocol stack has been created (may not be connected though). */ abstract public boolean isOpen(); /** Determines whether the channel is connected to a group. This implies it is open. If true is returned, then the channel can be used to send and receive messages. */ abstract public boolean isConnected(); /** * Returns the number of messages that are waiting. Those messages can be * removed by {@link #receive(long)}. Note that this number could change after * calling this method and before calling receive() (e.g. the latter * method might be called by a different thread). * @return The number of messages on the queue, or -1 if the queue/channel * is closed/disconnected. */ public int getNumMessages() { return -1; } public String dumpQueue() { return ""; } /** * Returns a map of statistics of the various protocols and of the channel itself. * @return Map. A map where the keys are the protocols ("channel" pseudo key is * used for the channel itself") and the values are property maps. */ public abstract Map dumpStats(); /** Sends a message to a (unicast) destination. The message contains

  1. a destination address (Address). A null address sends the message to all group members.
  2. a source address. Can be left empty. Will be filled in by the protocol stack.
  3. a byte buffer. The message contents.
  4. several additional fields. They can be used by application programs (or patterns). E.g. a message ID, a oneway field which determines whether a response is expected etc.
@param msg The message to be sent. Destination and buffer should be set. A null destination means to send to all group members. @exception ChannelNotConnectedException The channel must be connected to send messages. @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. A new channel has to be created first. */ abstract public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException; /** Helper method. Will create a Message(dst, src, obj) and use send(Message). @param dst Destination address for message. If null, message will be sent to all current group members @param src Source (sender's) address. If null, it will be set by the protocol's transport layer before being put on the wire. Can usually be set to null. @param obj Serializable object. Will be serialized into the byte buffer of the Message. If it is not serializable, the byte buffer will be null. */ abstract public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException; /** Access to event mechanism of channels. Enables to send and receive events, used by building blocks to communicate with (building block) specific protocol layers. Currently useful only with JChannel. */ public void down(Event evt) { } /** * Can be used instead of down() when a return value is expected. This will be removed in 3.0 when we change * the signature of down() to return Object rather than void * @param evt * @return */ public Object downcall(Event evt) { return null; } /** Receives a message, a view change or a block event. By using setOpt, the type of objects to be received can be determined (e.g., not views and blocks, just messages). The possible types returned can be:
  1. Message. Normal message
  2. Event. All other events (used by JChannel)
  3. View. A view change.
  4. BlockEvent. A block event indicating that a flush protocol has been started, and we should not send any more messages. This event should be ack'ed by calling {@link org.jgroups.Channel#blockOk()} . Any messages sent after blockOk() returns might get blocked until the flush protocol has completed.
  5. UnblockEvent. An unblock event indicating that the flush protocol has completed and we can resume sending messages
  6. SuspectEvent. A notification of a suspected member.
  7. GetStateEvent. The current state of the application should be returned using ReturnState.
  8. SetStateEvent. The state of a single/all members as requested previously by having called Channel.getState(s).
  9. ExitEvent. Signals that this member was forced to leave the group (e.g., caused by the member being suspected.) The member can rejoin the group by calling open(). If the AUTO_RECONNECT is set (see setOpt()), the reconnect will be done automatically.
The instanceof operator can be used to discriminate between different types returned. @param timeout Value in milliseconds. Value <= 0 means wait forever @return A Message, View, BlockEvent, SuspectEvent, GetStateEvent, SetStateEvent or ExitEvent, depending on what is on top of the internal queue. @exception ChannelNotConnectedException The channel must be connected to receive messages. @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. A new channel has to be created first. @exception TimeoutException Thrown when a timeout has occurred. @deprecated Use a {@link Receiver} instead */ abstract public Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; /** Returns the next message, view, block, suspect or other event without removing it from the queue. @param timeout Value in milliseconds. Value <= 0 means wait forever @return A Message, View, BlockEvent, SuspectEvent, GetStateEvent or SetStateEvent object, depending on what is on top of the internal queue. @exception ChannelNotConnectedException The channel must be connected to receive messages. @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. A new channel has to be created first. @exception TimeoutException Thrown when a timeout has occurred. @see #receive(long) @deprecated Use a {@link Receiver} instead, this method will not be available in JGroups 3.0 */ abstract public Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; /** * Gets the current view. This does not retrieve a new view, use receive() to do so. The view may only be available after a successful connect(). The result of calling this method on an unconnected channel is implementation defined (may return null). Calling it on a channel that is not enabled to receive view events (via setOpt) returns null. Calling this method on a closed channel returns a null view. @return The current view. */ abstract public View getView(); /** Returns the channel's own address. The result of calling this method on an unconnected channel is implementation defined (may return null). Calling this method on a closed channel returns null. @return The channel's address. Generated by the underlying transport, and opaque. Addresses can be used as destination in the Send operation. */ abstract public Address getLocalAddress(); /** Returns the group address of the group of which the channel is a member. This is the object that was the argument to connect(). Calling this method on a closed channel returns null. @return The group address @deprecated Use {@link #getClusterName()} instead */ abstract public String getChannelName(); /** Returns the cluster name of the group of which the channel is a member. This is the object that was the argument to connect(). Calling this method on a closed channel returns null. @return The cluster name */ abstract public String getClusterName(); /** When up_handler is set, all events will be passed to it directly. These will not be received by the channel (except connect/disconnect, state retrieval and the like). This can be used by building blocks on top of a channel; thus the channel is used as a pass-through medium, and the building blocks take over some of the channel's tasks. However, tasks such as connection management and state transfer is still handled by the channel. */ public void setUpHandler(UpHandler up_handler) { this.up_handler=up_handler; } /** Allows to be notified when a channel event such as connect, disconnect or close occurs. E.g. a PullPushAdapter may choose to stop when the channel is closed, or to start when it is opened. @deprecated Use addChannelListener() instead */ public void setChannelListener(ChannelListener channel_listener) { addChannelListener(channel_listener); } /** Allows to be notified when a channel event such as connect, disconnect or close occurs. E.g. a PullPushAdapter may choose to stop when the channel is closed, or to start when it is opened. */ public synchronized void addChannelListener(ChannelListener listener) { if(listener == null) return; if(channel_listeners == null) channel_listeners=new CopyOnWriteArraySet(); channel_listeners.add(listener); } public synchronized void removeChannelListener(ChannelListener listener) { if(channel_listeners != null && listener != null) channel_listeners.remove(listener); } public synchronized void clearChannelListeners() { if(channel_listeners != null) channel_listeners.clear(); } /** Sets the receiver, which will handle all messages, view changes etc */ public void setReceiver(Receiver r) { receiver=r; } /** Sets an option. The following options are currently recognized:
  1. BLOCK. Turn the reception of BLOCK events on/off (value is Boolean). Default is off
  2. LOCAL. Receive its own broadcast messages to the group (value is Boolean). Default is on.
  3. AUTO_RECONNECT. Turn auto-reconnection on/off. If on, when a member if forced out of a group (EXIT event), then we will reconnect.
  4. AUTO_GETSTATE. Turn automatic fetching of state after an auto-reconnect on/off. This also sets AUTO_RECONNECT to true (if not yet set).
This method can be called on an unconnected channel. Calling this method on a closed channel has no effect. */ abstract public void setOpt(int option, Object value); /** Gets an option. This method can be called on an unconnected channel. Calling this method on a closed channel returns null. @param option The option to be returned. @return The object associated with an option. */ abstract public Object getOpt(int option); abstract public boolean flushSupported(); abstract public boolean startFlush(List
flushParticipants,boolean automatic_resume); abstract public boolean startFlush(boolean automatic_resume); abstract public boolean startFlush(long timeout, boolean automatic_resume); abstract public void stopFlush(); abstract public void stopFlush(List
flushParticipants); /** Called to acknowledge a block() (callback in MembershipListener or BlockEvent received from call to Receive). After sending BlockOk, no messages should be sent until a new view has been received. Calling this method on a closed channel has no effect. */ abstract public void blockOk(); /** Retrieve the state of the group. Will usually contact the oldest group member to get the state. When the method returns true, a SetStateEvent will have been added to the channel's queue, causing receive() to return the state in one of the next invocations. If false, no state will be retrieved by receive(). @param target The address of the member from which the state is to be retrieved. If it is null, the coordinator is contacted. @param timeout Milliseconds to wait for the response (0 = wait indefinitely). @return boolean True if the state was retrieved successfully, otherwise false. @exception ChannelNotConnectedException The channel must be connected to receive messages. @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. A new channel has to be created first. */ abstract public boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException; /** * Fetches a partial state identified by state_id. * @param target * @param state_id * @param timeout * @return * @throws ChannelNotConnectedException * @throws ChannelClosedException */ abstract public boolean getState(Address target, String state_id, long timeout) throws ChannelNotConnectedException, ChannelClosedException; /** Retrieve all states of the group members. Will contact all group members to get the states. When the method returns true, a SetStateEvent will have been added to the channel's queue, causing Receive to return the states in one of the next invocations. If false, no states will be retrieved by Receive. @param targets A list of members which are contacted for states. If the list is null, all the current members of the group will be contacted. @param timeout Milliseconds to wait for the response (0 = wait indefinitely). @return boolean True if the state was retrieved successfully, otherwise false. @exception ChannelNotConnectedException The channel must be connected to receive messages. @exception ChannelClosedException The channel is closed and therefore cannot be used any longer. A new channel has to be created first. @deprecated Not really needed - we always want to get the state from a single member */ abstract public boolean getAllStates(Vector targets, long timeout) throws ChannelNotConnectedException, ChannelClosedException; /** * Called by the application is response to receiving a * getState() object when calling receive(). * @param state The state of the application as a byte buffer * (to send over the network). */ public abstract void returnState(byte[] state); /** Returns a given substate (state_id of null means return entire state) */ public abstract void returnState(byte[] state, String state_id); public abstract Map getInfo(); public abstract void setInfo(String key, Object value); public static String option2String(int option) { switch(option) { case BLOCK: return "BLOCK"; case VIEW: return "VIEW"; case SUSPECT: return "SUSPECT"; case LOCAL: return "LOCAL"; case GET_STATE_EVENTS: return "GET_STATE_EVENTS"; case AUTO_RECONNECT: return "AUTO_RECONNECT"; case AUTO_GETSTATE: return "AUTO_GETSTATE"; default: return "unknown (" + option + ')'; } } protected void notifyChannelConnected(Channel c) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelConnected(c); } catch(Throwable t) { getLog().error("exception in channelConnected() callback", t); } } } protected void notifyChannelDisconnected(Channel c) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelDisconnected(c); } catch(Throwable t) { getLog().error("exception in channelDisonnected() callback", t); } } } protected void notifyChannelClosed(Channel c) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelClosed(c); } catch(Throwable t) { getLog().error("exception in channelClosed() callback", t); } } } protected void notifyChannelShunned() { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelShunned(); } catch(Throwable t) { getLog().error("exception in channelShunned() callback", t); } } } protected void notifyChannelReconnected(Address addr) { if(channel_listeners == null) return; for(ChannelListener channelListener: channel_listeners) { try { channelListener.channelReconnected(addr); } catch(Throwable t) { getLog().error("exception in channelReconnected() callback", t); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/View.java0000644000175000017500000002057311366547366023171 0ustar twernertwerner package org.jgroups; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.HashMap; import java.util.Map; import java.util.Vector; /** * A view is a local representation of the current membership of a group. * Only one view is installed in a channel at a time. * Views contain the address of its creator, an ID and a list of member addresses. * These adresses are ordered, and the first address is always the coordinator of the view. * This way, each member of the group knows who the new coordinator will be if the current one * crashes or leaves the group. * The views are sent between members using the VIEW_CHANGE event * @author Bela Ban * @version $Id: View.java,v 1.18.2.1 2009/12/17 16:30:09 belaban Exp $ */ public class View implements Externalizable, Cloneable, Streamable { /* A view is uniquely identified by its ViewID * The view id contains the creator address and a Lamport time. * The Lamport time is the highest timestamp seen or sent from a view. * if a view change comes in with a lower Lamport time, the event is discarded. */ protected ViewId vid=null; /** * A list containing all the members of the view * This list is always ordered, with the coordinator being the first member. * the second member will be the new coordinator if the current one disappears * or leaves the group. */ protected Vector
members=null; protected Map payload=null; private static final long serialVersionUID=7027860705519930293L; /** * creates an empty view, should not be used */ public View() { } /** * Creates a new view * * @param vid The view id of this view (can not be null) * @param members Contains a list of all the members in the view, can be empty but not null. */ public View(ViewId vid, Vector
members) { this.vid=vid; this.members=members; } /** * Creates a new view * * @param creator The creator of this view (can not be null) * @param id The lamport timestamp of this view * @param members Contains a list of all the members in the view, can be empty but not null. */ public View(Address creator, long id, Vector
members) { this(new ViewId(creator, id), members); } /** * returns the view ID of this view * if this view was created with the empty constructur, null will be returned * * @return the view ID of this view */ public ViewId getVid() { return vid; } /** * returns the creator of this view * if this view was created with the empty constructur, null will be returned * * @return the creator of this view in form of an Address object */ public Address getCreator() { return vid != null ? vid.getCoordAddress() : null; } /** * Returns a reference to the List of members (ordered) * Do NOT change this list, hence your will invalidate the view * Make a copy if you have to modify it. * * @return a reference to the ordered list of members in this view */ public Vector
getMembers() { return Util.unmodifiableVector(members); } /** * returns true, if this view contains a certain member * * @param mbr - the address of the member, * @return true if this view contains the member, false if it doesn't * if the argument mbr is null, this operation returns false */ public boolean containsMember(Address mbr) { return !(mbr == null || members == null) && members.contains(mbr); } public boolean equals(Object obj) { if(!(obj instanceof View)) return false; if(vid != null) { int rc=vid.compareTo(((View)obj).vid); if(rc != 0) return false; if(members != null && ((View)obj).members != null) { return members.equals(((View)obj).members); } } else { if(((View)obj).vid == null) return true; } return false; } public int hashCode() { return vid != null? vid.hashCode() : 0; } /** * returns the number of members in this view * * @return the number of members in this view 0..n */ public int size() { return members == null ? 0 : members.size(); } /** * creates a copy of this view * * @return a copy of this view */ public Object clone() { ViewId vid2=vid != null ? (ViewId)vid.clone() : null; Vector
members2=members != null ? new Vector
(members) : null; return new View(vid2, members2); } /** * debug only */ public String printDetails() { StringBuilder ret=new StringBuilder(); ret.append(vid).append("\n\t"); if(members != null) { for(int i=0; i < members.size(); i++) { ret.append(members.elementAt(i)).append("\n\t"); } ret.append('\n'); } return ret.toString(); } /** * Adds a key and value to the view. Since the payloads will be shipped around *with* the view, so the keys and * values need to be serializable. Note that the total serialized size of all keys and values cannot * exceed 65000 bytes ! * @param key * @param value */ public void addPayload(String key, Object value) { if(payload == null) { payload=new HashMap(7); } payload.put(key, value); } public Object getPayload(String key) { if(payload != null) return payload.get(key); return null; } public String toString() { StringBuilder ret=new StringBuilder(64); ret.append(vid).append(" ").append(members); return ret.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(vid); out.writeObject(members); if(payload != null && !payload.isEmpty()) { out.writeBoolean(true); out.writeObject(payload); } else { out.writeBoolean(false); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { vid=(ViewId)in.readObject(); members=(Vector
)in.readObject(); if(in.readBoolean()) { payload=(Map)in.readObject(); } } public void writeTo(DataOutputStream out) throws IOException { // vid if(vid != null) { out.writeBoolean(true); vid.writeTo(out); } else out.writeBoolean(false); // members: Util.writeAddresses(members, out); if(payload != null && !payload.isEmpty()) { try { byte buffer[]=Util.objectToByteBuffer(payload); out.writeShort(buffer.length); out.write(buffer, 0, buffer.length); } catch(Exception e) { throw new IOException("could not write View payload"); } } else { out.writeShort(0); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { boolean b; // vid: b=in.readBoolean(); if(b) { vid=new ViewId(); vid.readFrom(in); } // members: members=(Vector
)Util.readAddresses(in, Vector.class); short payloadLength=in.readShort(); if(payloadLength > 0) { byte[] buffer=new byte[payloadLength]; in.readFully(buffer); try { payload=(Map)Util.objectFromByteBuffer(buffer); } catch(Exception e) { throw new IOException("Could not read View payload " + buffer.length); } } } public int serializedSize() { int retval=Global.BYTE_SIZE; // presence for vid if(vid != null) retval+=vid.serializedSize(); retval+=Util.size(members); retval+=Global.SHORT_SIZE; // presence for payload if(payload != null) { retval+=Util.sizeOf(payload); } return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Receiver.java0000644000175000017500000000044011366547366024012 0ustar twernertwernerpackage org.jgroups; /** * Defines the callbacks that are invoked when messages, views etc are received on a channel * @author Bela Ban * @version $Id: Receiver.java,v 1.1 2005/11/08 10:40:16 belaban Exp $ */ public interface Receiver extends MessageListener, MembershipListener { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/debug/0000755000175000017500000000000011621261110022441 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/debug/package.html0000644000175000017500000000020111366547366024745 0ustar twernertwerner Provides debug support, including testing, profiling, and a graphical view of a protocol stack. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/debug/Profiler.java0000644000175000017500000000722111366547366025122 0ustar twernertwerner// $Id: Profiler.java,v 1.6 2005/05/30 16:14:35 belaban Exp $ package org.jgroups.debug; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Enumeration; import java.util.Hashtable; /** * Allows to time execution of 'named' statements, counts number of times called and total * execution time. * * @author Bela Ban */ public class Profiler { static public class Entry { long num_calls=0; long tot_time=0; double avg=0.0; long start_time=0; long stop_time=0; synchronized void compute() { num_calls++; tot_time+=stop_time - start_time; avg=(double)tot_time / num_calls; } } private static OutputStream os=null; private static final Hashtable entries=new Hashtable(); private static Log log=LogFactory.getLog(Profiler.class); public Profiler() { try { os=new FileOutputStream("profiler.dat"); } catch(Exception e) { log.error(e); } } public static void setFilename(String filename) { try { if(os != null) { os.close(); } os=new FileOutputStream(filename); } catch(Exception e) { log.error(e); } } public static void start(String call_name) { Entry e=(Entry)entries.get(call_name); if(e == null) { e=new Entry(); entries.put(call_name, e); } e.start_time=System.currentTimeMillis(); } public static void stop(String call_name) { Entry e=(Entry)entries.get(call_name); if(e == null) { log.error("Profiler.stop(): entry for " + call_name + " not found"); return; } e.stop_time=System.currentTimeMillis(); e.compute(); } public static void dump() { // dump to file String key; Entry val; if(os == null) { log.error("Profiler.dump(): output file is null"); return; } try { os.write("Key: Number of calls: Total time (ms): Average time (ms):\n".getBytes()); os.write("-----------------------------------------------------------------\n\n".getBytes()); } catch(Exception e) { log.error(e); } for(Enumeration e=entries.keys(); e.hasMoreElements();) { key=(String)e.nextElement(); val=(Entry)entries.get(key); try { os.write((key + ": " + val.num_calls + ' ' + val.tot_time + ' ' + trim(val.avg) + '\n').getBytes()); } catch(Exception ex) { log.error(ex); } } } public static double trim(double inp) { double retval=0.0, rem=0.0; long l1, l2; l1=(long)inp; rem=inp - l1; rem=rem * 100.0; l2=(long)rem; rem=l2 / 100.0; retval=l1 + rem; return retval; } public static void main(String[] args) { Profiler.setFilename("bela.out"); try { Profiler.start("time1"); Thread.sleep(1500); Profiler.stop("time1"); Profiler.start("time1"); Thread.sleep(1500); Profiler.start("time2"); Thread.sleep(500); Profiler.stop("time2"); Thread.sleep(1500); Profiler.stop("time1"); Profiler.dump(); } catch(Exception e) { log.error(e); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/debug/Simulator.java0000644000175000017500000001704111366547366025320 0ustar twernertwernerpackage org.jgroups.debug; import org.jgroups.Address; import org.jgroups.ChannelException; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.TimeScheduler; import java.util.HashMap; import java.util.Iterator; /** * Tests one or more protocols independently. Look at org.jgroups.tests.FCTest for an example of how to use it. * @author Bela Ban * @version $Id: Simulator.java,v 1.11.2.1 2008/05/22 13:23:10 belaban Exp $ */ public class Simulator { private Protocol[] protStack=null; private ProtocolAdapter ad=new ProtocolAdapter(); ProtocolStack prot_stack=null; private Receiver r=null; private Protocol top=null, bottom=null; private Queue send_queue=new Queue(); private Thread send_thread; private Queue recv_queue=new Queue(); private Thread recv_thread; /** HashMap from Address to Simulator. */ private final HashMap addrTable=new HashMap(); private Address local_addr=null; private View view; public interface Receiver { void receive(Event evt); } public void setProtocolStack(Protocol[] stack) { this.protStack=stack; this.protStack[0].setUpProtocol(ad); this.protStack[this.protStack.length-1].setDownProtocol(ad); top=protStack[0]; bottom=this.protStack[this.protStack.length-1]; try { prot_stack=new ProtocolStack(); } catch (ChannelException e) { e.printStackTrace(); } if(protStack.length > 1) { for(int i=0; i < protStack.length; i++) { Protocol p1=protStack[i]; p1.setProtocolStack(prot_stack); Protocol p2=i+1 >= protStack.length? null : protStack[i+1]; if(p2 != null) { p1.setDownProtocol(p2); p2.setUpProtocol(p1); } } } } public String dumpStats() { StringBuilder sb=new StringBuilder(); for(int i=0; i < protStack.length; i++) { Protocol p1=protStack[i]; sb.append(p1.getName()).append(":\n").append(p1.dumpStats()).append("\n"); } return sb.toString(); } public void addMember(Address addr) { addMember(addr, this); } public void addMember(Address addr, Simulator s) { addrTable.put(addr, s); } public void setLocalAddress(Address addr) { this.local_addr=addr; } public void setView(View v) { this.view=v; } public void setReceiver(Receiver r) { this.r=r; } public Object send(Event evt) { return top.down(evt); } public void receive(Event evt) { try { Event copy; if(evt.getType() == Event.MSG && evt.getArg() != null) { copy=new Event(Event.MSG, ((Message)evt.getArg()).copy()); } else copy=evt; recv_queue.add(copy); } catch(QueueClosedException e) { } } public void start() throws Exception { if(local_addr == null) throw new Exception("local_addr has to be non-null"); if(protStack == null) throw new Exception("protocol stack is null"); bottom.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); if(view != null) { Event view_evt=new Event(Event.VIEW_CHANGE, view); bottom.up(view_evt); top.down(view_evt); } for(int i=0; i < protStack.length; i++) { Protocol p=protStack[i]; p.setProtocolStack(prot_stack); } for(int i=0; i < protStack.length; i++) { Protocol p=protStack[i]; p.init(); } for(int i=0; i < protStack.length; i++) { Protocol p=protStack[i]; p.start(); } send_thread=new Thread() { public void run() { Event evt; while(send_thread != null) { try { evt=(Event)send_queue.remove(); if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); Address dst=msg.getDest(); if(msg.getSrc() == null) ((Message)evt.getArg()).setSrc(local_addr); Simulator s; if(dst == null) { for(Iterator it=addrTable.values().iterator(); it.hasNext();) { s=(Simulator)it.next(); s.receive(evt); } } else { s=(Simulator)addrTable.get(dst); if(s != null) s.receive(evt); } } } catch(QueueClosedException e) { send_thread=null; break; } } } }; send_thread.start(); recv_thread=new Thread() { public void run() { Event evt; while(recv_thread != null) { try { evt=(Event)recv_queue.remove(); bottom.up(evt); } catch(QueueClosedException e) { recv_thread=null; break; } } } }; recv_thread.start(); } public void stop() { recv_thread=null; recv_queue.close(false); send_thread=null; send_queue.close(false); if(ad != null) { try { ad.getTimer().stop(); } catch(InterruptedException e) { e.printStackTrace(); } } } class ProtocolAdapter extends TP { ProtocolAdapter() { timer=new TimeScheduler(); } public TimeScheduler getTimer() { return timer; } public void setTimer(TimeScheduler timer) { this.timer=timer; } public String getName() { return "ProtocolAdapter"; } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { } public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { } public String getInfo() { return null; } public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { } public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { } public void init() throws Exception { super.init(); } public Object up(Event evt) { if(r != null) r.receive(evt); return null; } /** send to unicast or multicast destination */ public Object down(Event evt) { try { send_queue.add(evt); } catch(QueueClosedException e) { } return null; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/debug/ProtocolTester.java0000644000175000017500000001155111366547366026331 0ustar twernertwerner// $Id: ProtocolTester.java,v 1.12.4.2 2008/05/22 13:23:10 belaban Exp $ package org.jgroups.debug; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.Configurator; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import java.util.LinkedList; import java.util.List; import java.util.Vector; /** * Generic class to test one or more protocol layers directly. Sets up a protocol stack consisting of * the top layer (which is essentially given by the user and is the test harness), the specified protocol(s) and * a bottom layer (which is automatically added), which sends all received messages immediately back up the * stack (swapping sender and receiver address of the message). * * @author Bela Ban * @author March 23 2001 */ public class ProtocolTester { Protocol harness=null, top, bottom; String props=null; Configurator config=null; protected final Log log=LogFactory.getLog(this.getClass()); public ProtocolTester(String prot_spec, Protocol harness) throws Exception { if(prot_spec == null || harness == null) throw new Exception("ProtocolTester(): prot_spec or harness is null"); props=prot_spec; this.harness=harness; props="LOOPBACK:" + props; // add a loopback layer at the bottom of the stack config=new Configurator(); ProtocolStack stack=new ProtocolStack(); top=Configurator.setupProtocolStack(props, stack); harness.setDownProtocol(top); top.setUpProtocol(harness); // +++ Configurator.initProtocolStack(getProtocols()); bottom=getBottomProtocol(top); // has to be set after StartProtocolStack, otherwise the up and down handler threads in the harness // will be started as well (we don't want that) ! // top.setUpProtocol(harness); } public Vector getProtocols() { Vector retval=new Vector(); Protocol tmp=top; while(tmp != null) { retval.add(tmp); tmp=tmp.getDownProtocol(); } return retval; } public String getProtocolSpec() { return props; } public Protocol getBottom() { return bottom; } public Protocol getTop() { return top; } public void start() throws Exception { Protocol p; if(harness != null) { p=harness; while(p != null) { p.start(); p=p.getDownProtocol(); } } else if(top != null) { p=top; while(p != null) { p.start(); p=p.getDownProtocol(); } } } public void stop() { Protocol p; if(harness != null) { List protocols=new LinkedList(); p=harness; while(p != null) { protocols.add(p); p.stop(); p=p.getDownProtocol(); } Configurator.destroyProtocolStack(protocols); } else if(top != null) { p=top; List protocols=new LinkedList(); while(p != null) { protocols.add(p); p.stop(); p=p.getDownProtocol(); } Configurator.destroyProtocolStack(protocols); } } private final Protocol getBottomProtocol(Protocol top) { Protocol tmp; if(top == null) return null; tmp=top; while(tmp.getDownProtocol() != null) tmp=tmp.getDownProtocol(); return tmp; } public static void main(String[] args) { String props; ProtocolTester t; Harness h; if(args.length < 1 || args.length > 2) { System.out.println("ProtocolTester [-trace]"); return; } props=args[0]; try { h=new Harness(); t=new ProtocolTester(props, h); System.out.println("protocol specification is " + t.getProtocolSpec()); h.down(new Event(Event.BECOME_SERVER)); for(int i=0; i < 5; i++) { System.out.println("Sending msg #" + i); h.down(new Event(Event.MSG, new Message(null, null, "Hello world #" + i))); } Util.sleep(500); t.stop(); } catch(Exception ex) { System.err.println(ex); } } private static class Harness extends Protocol { public String getName() { return "Harness"; } public Object up(Event evt) { System.out.println("Harness.up(): " + evt); return null; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ChannelNotConnectedException.java0000644000175000017500000000104211366547366030000 0ustar twernertwerner// $Id: ChannelNotConnectedException.java,v 1.3 2006/11/13 17:42:11 bstansberry Exp $ package org.jgroups; /** * Thrown if an operation is attemped on an unconnected channel. */ public class ChannelNotConnectedException extends ChannelException { private static final long serialVersionUID = -6701630538465783064L; public ChannelNotConnectedException() { } public ChannelNotConnectedException(String reason) { super(reason); } public String toString() { return "ChannelNotConnectedException"; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/StreamingSetStateEvent.java0000644000175000017500000000412711366547366026664 0ustar twernertwernerpackage org.jgroups; import java.io.InputStream; /** * * Represents an event returned by channel.receive(), as requested by * channel.getState() previously. * *

* * Allows applications using a channel in a pull mode to receive a state from * another channel instance providing state. Channels have to be configured with * STREAMING_STATE_TRANSFER protocol rather than the default * STATE_TRANSFER protocol in order to receive this event. * *

* * The following code demonstrate how to pull events from a channel, processing * StreamingSetStateEvent and retrieving hypothetical state in the * form of LinkedList from event's InputStream reference. * *

 *  Object obj=channel.receive(0);
 *  if(obj instanceof StreamingSetStateEvent) {
 *   	StreamingSetStateEvent evt=(StreamingSetStateEvent)obj;
 *    	ObjectInputStream ois = null;   	
 *		try {			
 *			ois = new ObjectInputStream(evt.getArg());
 *			state = (LinkedList)ois.readObject();  
 *		} catch (Exception e) {} 
 *		finally
 *		{
 *			try {				
 *				ois.close();
 *			} catch (IOException e) {
 *				System.err.println(e);
 *			}
 *		}                
 *   }
 * 
* * * @author Vladimir Blagojevic * @see org.jgroups.JChannel#getState(Address, long) * @see org.jgroups.StreamingMessageListener#setState(InputStream) * @since 2.4 * */ public class StreamingSetStateEvent { InputStream is; String state_id; public StreamingSetStateEvent(InputStream is,String state_id) { super(); this.is=is; this.state_id=state_id; } /** * Returns InputStream used for reading of a state. * * @return the InputStream */ public InputStream getArg() { return is; } /** * Returns id of the partial state if partial state was requested. * If full state transfer was requested this method will return null. * * @see JChannel#getState(Address, long) * @see JChannel#getState(Address, String, long) * @return partial state id */ public String getStateId() { return state_id; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ChannelListener.java0000644000175000017500000000100411366547366025321 0ustar twernertwerner// $Id: ChannelListener.java,v 1.1.1.1 2003/09/09 01:24:07 belaban Exp $ package org.jgroups; /** * Allows a listener to be notified when important channel events occur. For example, when * a channel is closed, a PullPushAdapter can be notified, and stop accordingly. */ public interface ChannelListener { void channelConnected(Channel channel); void channelDisconnected(Channel channel); void channelClosed(Channel channel); void channelShunned(); void channelReconnected(Address addr); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/package.html0000644000175000017500000000015211366547366023664 0ustar twernertwerner Provides top-level public JGroups classes such as Channel, Message, etc. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/BlockEvent.java0000644000175000017500000000034211366547366024303 0ustar twernertwerner// $Id: BlockEvent.java,v 1.2 2005/07/17 11:38:05 chrislott Exp $ package org.jgroups; /** * Trivial object that represents a block event. */ public class BlockEvent { public String toString() {return "BlockEvent";} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/StateTransferException.java0000644000175000017500000000113111366547366026710 0ustar twernertwernerpackage org.jgroups; /** * StateTransferException is thrown to indicate failure of * state transfer between cluster members. *

* * @author Vladimir Blagojevic * @since 2.6 * */ public class StateTransferException extends ChannelException { private static final long serialVersionUID = -4070956583392020498L; public StateTransferException(){ } public StateTransferException(String reason){ super(reason); } public StateTransferException(String reason,Throwable cause){ super(reason, cause); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Version.java0000644000175000017500000000772411366547366023707 0ustar twernertwerner package org.jgroups; import org.jgroups.annotations.Immutable; /** * We're using the scheme described at http://www.jboss.com/index.html?module=bb&op=viewtopic&t=77231 * for major, minor and micro version numbers. We have 5 bits for major and minor version numbers each and * 6 bits for the micro version. * This gives: * X = 0-31 for major versions * Y = 0-31 for minor versions * Z = 0-63 for micro versions * * @author Bela Ban * @version $Id: Version.java,v 1.59.2.30 2010/04/30 12:15:07 belaban Exp $ * Holds version information for JGroups. */ @Immutable public class Version { public static final short major = 2; public static final short minor = 6; public static final short micro = 15; public static final String description="2.6.15.GA"; public static final short version=encode(major, minor, micro); public static final String string_version=print(version); public static final String cvs="$Id: Version.java,v 1.59.2.30 2010/04/30 12:15:07 belaban Exp $"; private static final int MAJOR_SHIFT = 11; private static final int MINOR_SHIFT = 6; private static final int MAJOR_MASK = 0x00f800; // 1111100000000000 bit mask private static final int MINOR_MASK = 0x0007c0; // 11111000000 bit mask private static final int MICRO_MASK = 0x00003f; // 111111 bit mask /** * Prints the value of the description and cvs fields to System.out. * @param args */ public static void main(String[] args) { System.out.println("\nVersion: " + description); System.out.println("CVS: " + cvs + "\n"); } /** * Returns the catenation of the description and cvs fields. * @return String with description */ public static String printDescription() { return "JGroups " + description + " [" + cvs + "]"; } /** * Returns the version field as a String. * @return String with version */ public static String printVersion() { return string_version; } /** * Compares the specified version number against the current version number. * @param v short * @return Result of == operator. */ public static boolean isSame(short v) { return version == v; } /** Method copied from http://www.jboss.com/index.html?module=bb&op=viewtopic&t=77231 */ public static short encode(int major, int minor, int micro) { return (short)((major << MAJOR_SHIFT) + (minor << MINOR_SHIFT) + micro); } /** Method copied from http://www.jboss.com/index.html?module=bb&op=viewtopic&t=77231 */ public static String print(short version) { int major=(version & MAJOR_MASK) >> MAJOR_SHIFT; int minor=(version & MINOR_MASK) >> MINOR_SHIFT; int micro=(version & MICRO_MASK); return major + "." + minor + "." + micro; } public static short[] decode(short version) { short major=(short)((version & MAJOR_MASK) >> MAJOR_SHIFT); short minor=(short)((version & MINOR_MASK) >> MINOR_SHIFT); short micro=(short)(version & MICRO_MASK); return new short[]{major, minor, micro}; } /** * Checks whether ver is binary compatible with the current version. The rule for binary compatibility is that * the major and minor versions have to match, whereas micro versions can differ. * @param ver * @return */ public static boolean isBinaryCompatible(short ver) { if(ver == version) return true; short[] tmp=decode(ver); short tmp_major=tmp[0], tmp_minor=tmp[1]; return major == tmp_major && minor == tmp_minor; } public static boolean isBinaryCompatible(short ver1, short ver2) { if(ver1 == ver2) return true; short[] tmp=decode(ver1); short tmp_major=tmp[0], tmp_minor=tmp[1]; tmp=decode(ver2); short tmp_major2=tmp[0], tmp_minor2=tmp[1]; return tmp_major == tmp_major2 && tmp_minor == tmp_minor2; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Address.java0000644000175000017500000000145511366547366023642 0ustar twernertwerner// $Id: Address.java,v 1.4 2005/07/17 11:38:05 chrislott Exp $ package org.jgroups; import org.jgroups.util.Streamable; import java.io.Externalizable; /** * Abstract address. Used to identify members on a group to send messages to. * Addresses are mostly generated by the bottom-most (transport) layers (e.g. UDP, TCP, LOOPBACK). * @author Bela Ban */ public interface Address extends Externalizable, Streamable, Comparable, Cloneable { // todo: remove Externalizable /** * Checks whether this is an address that represents multiple destinations; * e.g., a class D address in the Internet. * @return true if this is a multicast address, false if it is a unicast address */ boolean isMulticastAddress(); /** Returns serialized size of this address */ int size(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/SuspectEvent.java0000644000175000017500000000065111366547366024702 0ustar twernertwerner// $Id: SuspectEvent.java,v 1.3 2005/07/17 11:38:05 chrislott Exp $ package org.jgroups; /** * Represents a suspect event. * Gives access to the suspected member. */ public class SuspectEvent { final Object suspected_mbr; public SuspectEvent(Object suspected_mbr) {this.suspected_mbr=suspected_mbr;} public Object getMember() {return suspected_mbr;} public String toString() {return "SuspectEvent";} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ExtendedReceiverAdapter.java0000644000175000017500000000212511366547366026776 0ustar twernertwernerpackage org.jgroups; import java.io.InputStream; import java.io.OutputStream; import org.jgroups.util.Util; /** * @author Bela Ban * @version $Id: ExtendedReceiverAdapter.java,v 1.6 2006/10/11 14:34:36 belaban Exp $ */ public class ExtendedReceiverAdapter implements ExtendedReceiver { public byte[] getState(String state_id) { return null; } public void setState(String state_id, byte[] state) { } public void receive(Message msg) { } public byte[] getState() { return null; } public void setState(byte[] state) { } public void viewAccepted(View new_view) { } public void suspect(Address suspected_mbr) { } public void block() { } public void unblock() { } public void getState(OutputStream ostream) { Util.close(ostream); } public void getState(String state_id, OutputStream ostream) { Util.close(ostream); } public void setState(InputStream istream) { Util.close(istream); } public void setState(String state_id, InputStream istream) { Util.close(istream); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/SetStateEvent.java0000644000175000017500000000134611366547366025012 0ustar twernertwerner// $Id: SetStateEvent.java,v 1.6 2006/03/17 09:04:45 belaban Exp $ package org.jgroups; /** * Encapsulates a state returned by Channel.receive(), as requested by * Channel.getState(s) previously. * @author Bela Ban * @version $Id: SetStateEvent.java,v 1.6 2006/03/17 09:04:45 belaban Exp $ */ public class SetStateEvent { byte[] state=null; String state_id=null; public SetStateEvent(byte[] state, String state_id) { this.state=state; this.state_id=state_id; } public byte[] getArg() {return state;} public String getStateId() {return state_id;} public String toString() {return "SetStateEvent[state=" + state + ", state_id=" + state_id + ']';} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/TimeoutException.java0000644000175000017500000000074611366547366025564 0ustar twernertwerner// $Id: TimeoutException.java,v 1.4 2006/02/16 08:41:32 belaban Exp $ package org.jgroups; /** * Thrown if members fail to respond in time. */ public class TimeoutException extends Exception { private static final long serialVersionUID = -3555655828017487825L; public TimeoutException() { super("TimeoutException"); } public TimeoutException(String msg) { super(msg); } public String toString() { return super.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/0000755000175000017500000000000011621261110022460 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/Protocol.java0000644000175000017500000002243411366547366025163 0ustar twernertwerner package org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Event; import org.jgroups.util.ThreadFactory; import org.jgroups.protocols.TP; import java.util.Map; import java.util.Properties; import java.util.Vector; import java.util.HashMap; /** * The Protocol class provides a set of common services for protocol layers. Each layer has to * be a subclass of Protocol and override a number of methods (typically just up(), * down() and getName(). Layers are stacked in a certain order to form * a protocol stack. Events are passed from lower * layers to upper ones and vice versa. E.g. a Message received by the UDP layer at the bottom * will be passed to its higher layer as an Event. That layer will in turn pass the Event to * its layer and so on, until a layer handles the Message and sends a response or discards it, * the former resulting in another Event being passed down the stack. *

* The important thing to bear in mind is that Events have to passed on between layers in FIFO * order which is guaranteed by the Protocol implementation and must be guranteed by subclasses * implementing their on Event queuing.

* Note that each class implementing interface Protocol MUST provide an empty, public * constructor ! * * @author Bela Ban * @version $Id: Protocol.java,v 1.54.2.2 2009/09/07 12:52:16 belaban Exp $ */ public abstract class Protocol { protected final Properties props=new Properties(); protected Protocol up_prot=null, down_prot=null; protected ProtocolStack stack=null; protected boolean stats=true; // determines whether to collect statistics (and expose them via JMX) protected final Log log=LogFactory.getLog(this.getClass()); /** * Configures the protocol initially. A configuration string consists of name=value * items, separated by a ';' (semicolon), e.g.:

     * "loopback=false;unicast_inport=4444"
     * 
*/ public boolean setProperties(Properties props) { if(props != null) this.props.putAll(props); return true; } /** Called by Configurator. Removes 2 properties which are used by the Protocol directly and then * calls setProperties(), which might invoke the setProperties() method of the actual protocol instance. */ public boolean setPropertiesInternal(Properties props) { this.props.putAll(props); String str=props.getProperty("down_thread"); if(str != null) { if(log.isWarnEnabled()) log.warn("down_thread was deprecated and is ignored"); props.remove("down_thread"); } str=props.getProperty("down_thread_prio"); if(str != null) { if(log.isWarnEnabled()) log.warn("down_thread_prio was deprecated and is ignored"); props.remove("down_thread_prio"); } str=props.getProperty("up_thread"); if(str != null) { if(log.isWarnEnabled()) log.warn("up_thread was deprecated and is ignored"); props.remove("up_thread"); } str=props.getProperty("up_thread_prio"); if(str != null) { if(log.isWarnEnabled()) log.warn("up_thread_prio was deprecated and is ignored"); props.remove("up_thread_prio"); } str=props.getProperty("stats"); if(str != null) { stats=Boolean.valueOf(str).booleanValue(); props.remove("stats"); } return setProperties(props); } public Properties getProperties() { return props; } public ProtocolStack getProtocolStack(){ return stack; } protected TP getTransport() { Protocol retval=this; while(retval != null && retval.down_prot != null) { retval=retval.down_prot; } return (TP)retval; } /** Supposed to be overwritten by subclasses. Usually the transport returns a valid non-null thread factory, but * thread factories can also be created by individual protocols * @return */ public ThreadFactory getThreadFactory() { return down_prot != null? down_prot.getThreadFactory(): null; } /** @deprecated up_thread was removed * @return false by default */ public boolean upThreadEnabled() { return false; } /** * @deprecated down thread was removed * @return boolean False by default */ public boolean downThreadEnabled() { return false; } public boolean statsEnabled() { return stats; } public void enableStats(boolean flag) { stats=flag; } public void resetStats() { ; } public String printStats() { return null; } public Map dumpStats() { return new HashMap(); } /** * Called after instance has been created (null constructor) and before protocol is started. * Properties are already set. Other protocols are not yet connected and events cannot yet be sent. * @exception Exception Thrown if protocol cannot be initialized successfully. This will cause the * ProtocolStack to fail, so the channel constructor will throw an exception */ public void init() throws Exception { } /** * This method is called on a {@link org.jgroups.Channel#connect(String)}. Starts work. * Protocols are connected and queues are ready to receive events. * Will be called from bottom to top. This call will replace * the START and START_OK events. * @exception Exception Thrown if protocol cannot be started successfully. This will cause the ProtocolStack * to fail, so {@link org.jgroups.Channel#connect(String)} will throw an exception */ public void start() throws Exception { } /** * This method is called on a {@link org.jgroups.Channel#disconnect()}. Stops work (e.g. by closing multicast socket). * Will be called from top to bottom. This means that at the time of the method invocation the * neighbor protocol below is still working. This method will replace the * STOP, STOP_OK, CLEANUP and CLEANUP_OK events. The ProtocolStack guarantees that * when this method is called all messages in the down queue will have been flushed */ public void stop() { } /** * This method is called on a {@link org.jgroups.Channel#close()}. * Does some cleanup; after the call the VM will terminate */ public void destroy() { } /** List of events that are required to be answered by some layer above. @return Vector (of Integers) */ public Vector requiredUpServices() { return null; } /** List of events that are required to be answered by some layer below. @return Vector (of Integers) */ public Vector requiredDownServices() { return null; } /** List of events that are provided to layers above (they will be handled when sent down from above). @return Vector (of Integers) */ public Vector providedUpServices() { return null; } /** List of events that are provided to layers below (they will be handled when sent down from below). @return Vector providedDownServices() { return null; } public abstract String getName(); // all protocol names have to be unique ! public Protocol getUpProtocol() { return up_prot; } public Protocol getDownProtocol() { return down_prot; } public void setUpProtocol(Protocol up_prot) { this.up_prot=up_prot; } public void setDownProtocol(Protocol down_prot) { this.down_prot=down_prot; } public void setProtocolStack(ProtocolStack stack) { this.stack=stack; } /** * An event was received from the layer below. Usually the current layer will want to examine * the event type and - depending on its type - perform some computation * (e.g. removing headers from a MSG event type, or updating the internal membership list * when receiving a VIEW_CHANGE event). * Finally the event is either a) discarded, or b) an event is sent down * the stack using down_prot.down() or c) the event (or another event) is sent up * the stack using up_prot.up(). */ public Object up(Event evt) { return up_prot.up(evt); } /** * An event is to be sent down the stack. The layer may want to examine its type and perform * some action on it, depending on the event's type. If the event is a message MSG, then * the layer may need to add a header to it (or do nothing at all) before sending it down * the stack using down_prot.down(). In case of a GET_ADDRESS event (which tries to * retrieve the stack's address from one of the bottom layers), the layer may need to send * a new response event back up the stack using up_prot.up(). */ public Object down(Event evt) { return down_prot.down(evt); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/ExponentialInterval.java0000644000175000017500000000134311366547366027351 0ustar twernertwernerpackage org.jgroups.stack; /** * @author Bela Ban * @version $Id: ExponentialInterval.java,v 1.1 2007/08/14 07:51:12 belaban Exp $ */ public class ExponentialInterval implements Interval { private long value=30; private static final long MAX=15000; public ExponentialInterval() { } public ExponentialInterval(long value) { this.value=value; } public long next() { long retval=value; value=Math.min(MAX, value * 2); return retval; } /** We don't need to copy as we don't have any state */ public final Interval copy() { return new ExponentialInterval(value); } public String toString() { return String.valueOf(value); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/IpAddress.java0000644000175000017500000002605011366547366025236 0ustar twernertwerner// $Id: IpAddress.java,v 1.42.2.6 2008/08/20 12:16:37 belaban Exp $ package org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; /** * Network-dependent address (Internet). Generated by the bottommost layer of the protocol * stack (UDP). Contains an InetAddress and port. * @author Bela Ban */ public class IpAddress implements Address { private static final long serialVersionUID = 2592301708270771474L; private InetAddress ip_addr=null; private int port=0; private byte[] additional_data; protected static final Log log=LogFactory.getLog(IpAddress.class); static boolean resolve_dns=false; transient int size=-1; static { /* Trying to get value of resolve_dns. PropertyPermission not granted if * running in an untrusted environment with JNLP */ try { String tmp=Util.getProperty(new String[]{Global.RESOLVE_DNS, "resolve.dns"}, null, null, false, "false"); resolve_dns=Boolean.valueOf(tmp).booleanValue(); } catch (SecurityException ex){ resolve_dns=false; } } // Used only by Externalization public IpAddress() { } public IpAddress(String i, int p) throws UnknownHostException { port=p; ip_addr=InetAddress.getByName(i); } public IpAddress(InetAddress i, int p) { ip_addr=i; port=p; if(this.ip_addr == null) setAddressToLocalHost(); } private void setAddressToLocalHost() { try { ip_addr=InetAddress.getLocalHost(); // get first NIC found (on multi-homed systems) // size=size(); } catch(Exception e) { if(log.isWarnEnabled()) log.warn("exception: " + e); } } public IpAddress(int port) { this(port, true); } public IpAddress(int port, boolean set_default_host) { this.port=port; if(set_default_host) setAddressToLocalHost(); } public IpAddress(InetSocketAddress sock_addr) { port=sock_addr.getPort(); ip_addr=sock_addr.getAddress(); } public final InetAddress getIpAddress() {return ip_addr;} public final int getPort() {return port;} public final boolean isMulticastAddress() { return ip_addr != null && ip_addr.isMulticastAddress(); } /** * Returns the additional_data. * @return byte[] */ public final byte[] getAdditionalData() { return additional_data; } /** * Sets the additional_data. * @param additional_data The additional_data to set */ public final void setAdditionalData(byte[] additional_data) { this.additional_data=additional_data; size=-1; // changed May 13 2006 bela (suggested by Bruce Schuchardt) size=size(); } /** * Establishes an order between 2 addresses. Assumes other contains non-null IpAddress. * Excludes channel_name from comparison. * @return 0 for equality, value less than 0 if smaller, greater than 0 if greater. * @deprecated Use {@link #compareTo(org.jgroups.Address)} instead */ public final int compare(IpAddress other) { return compareTo(other); } /** * implements the java.lang.Comparable interface * @see java.lang.Comparable * @param o - the Object to be compared * @return a negative integer, zero, or a positive integer as this object is less than, * equal to, or greater than the specified object. * @exception java.lang.ClassCastException - if the specified object's type prevents it * from being compared to this Object. */ public final int compareTo(Object o) { int h1, h2, rc; // added Nov 7 2005, makes sense with canonical addresses if(this == o) return 0; if(!(o instanceof IpAddress)) throw new ClassCastException("comparison between different classes: the other object is " + (o != null? o.getClass() : o)); IpAddress other = (IpAddress) o; if(ip_addr == null) if (other.ip_addr == null) return port < other.port ? -1 : (port > other.port ? 1 : 0); else return -1; h1=ip_addr.hashCode(); h2=other.ip_addr.hashCode(); rc=h1 < h2? -1 : h1 > h2? 1 : 0; return rc != 0 ? rc : port < other.port ? -1 : (port > other.port ? 1 : 0); } /** * This method compares both addresses' dotted-decimal notation in string format if the hashcode and ports are * identical. Ca 30% slower than {@link #compareTo(Object)} if used excessively. * @param o * @return * @deprecated Use {@link #compareTo(org.jgroups.Address)} instead */ public final int compareToUnique(Object o) { int h1, h2, rc; // added Nov 7 2005, makes sense with canonical addresses if(this == o) return 0; if ((o == null) || !(o instanceof IpAddress)) throw new ClassCastException("comparison between different classes: the other object is " + (o != null? o.getClass() : o)); IpAddress other = (IpAddress) o; if(ip_addr == null) if (other.ip_addr == null) return port < other.port ? -1 : (port > other.port ? 1 : 0); else return -1; h1=ip_addr.hashCode(); h2=other.ip_addr.hashCode(); rc=h1 < h2? -1 : h1 > h2? 1 : 0; if(rc != 0) return rc; rc=port < other.port ? -1 : (port > other.port ? 1 : 0); if(rc != 0) return rc; // here we have the same addresses hash codes and ports, now let's compare the dotted-decimal addresses String addr1=ip_addr.getHostAddress(), addr2=other.ip_addr.getHostAddress(); return addr1.compareTo(addr2); } public final boolean equals(Object obj) { if(this == obj) return true; // added Nov 7 2005, makes sense with canonical addresses if(!(obj instanceof IpAddress)) return false; IpAddress other=(IpAddress)obj; boolean sameIP=false; if(this.ip_addr != null) sameIP=this.ip_addr.equals(other.ip_addr); else sameIP=(other.ip_addr == null); return sameIP && (this.port == other.port); } public final int hashCode() { return ip_addr != null ? ip_addr.hashCode() + port : port; } public String toString() { StringBuilder sb=new StringBuilder(); if(ip_addr == null) sb.append(""); else { if(ip_addr.isMulticastAddress()) sb.append(ip_addr.getHostAddress()); else { String host_name=null; if(resolve_dns) { host_name=ip_addr.getHostName(); // appendShortName(host_name, sb); } else { host_name=ip_addr.getHostAddress(); } sb.append(host_name); } } sb.append(":").append(port); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { if(ip_addr != null) { byte[] address=ip_addr.getAddress(); out.writeByte(address.length); // 1 byte out.write(address, 0, address.length); } else { out.writeByte(0); } out.writeShort(port); if(additional_data != null) { out.writeBoolean(true); out.writeShort(additional_data.length); out.write(additional_data, 0, additional_data.length); } else out.writeBoolean(false); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int len=in.readByte(); if(len > 0) { //read the four bytes byte[] a = new byte[len]; //in theory readFully(byte[]) should be faster //than read(byte[]) since latter reads // 4 bytes one at a time in.readFully(a); //look up an instance in the cache this.ip_addr=InetAddress.getByAddress(a); } //then read the port port=in.readUnsignedShort(); if(in.readBoolean() == false) return; len=in.readShort(); if(len > 0) { additional_data=new byte[len]; in.readFully(additional_data, 0, additional_data.length); } } public void writeTo(DataOutputStream out) throws IOException { if(ip_addr != null) { byte[] address=ip_addr.getAddress(); // 4 bytes (IPv4) or 16 bytes (IPv6) out.writeByte(address.length); // 1 byte out.write(address, 0, address.length); } else { out.writeByte(0); } out.writeShort(port); if(additional_data != null) { out.writeBoolean(true); // 1 byte out.writeShort(additional_data.length); out.write(additional_data, 0, additional_data.length); } else { out.writeBoolean(false); } } public void readFrom(DataInputStream in) throws IOException { int len=in.readByte(); if(len > 0 && (len != Global.IPV4_SIZE && len != Global.IPV6_SIZE)) throw new IOException("length has to be " + Global.IPV4_SIZE + " or " + Global.IPV6_SIZE + " bytes (was " + len + " bytes)"); byte[] a = new byte[len]; // 4 bytes (IPv4) or 16 bytes (IPv6) in.readFully(a); this.ip_addr=InetAddress.getByAddress(a); // changed from readShort(): we need the full 65535, with a short we'd only get up to 32K ! port=in.readUnsignedShort(); if(in.readBoolean() == false) return; len=in.readUnsignedShort(); if(len > 0) { additional_data=new byte[len]; in.readFully(additional_data, 0, additional_data.length); } } public int size() { if(size >= 0) return size; // length (1 bytes) + 4 bytes for port + 1 for additional_data available int tmp_size=Global.BYTE_SIZE+ Global.SHORT_SIZE + Global.BYTE_SIZE; if(ip_addr != null) tmp_size+=ip_addr.getAddress().length; // 4 bytes for IPv4 if(additional_data != null) tmp_size+=additional_data.length+Global.SHORT_SIZE; size=tmp_size; return tmp_size; } public Object clone() throws CloneNotSupportedException { IpAddress ret=new IpAddress(ip_addr, port); if(additional_data != null) { ret.additional_data=new byte[additional_data.length]; System.arraycopy(additional_data, 0, ret.additional_data, 0, additional_data.length); } return ret; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/AckReceiverWindow.java0000644000175000017500000001403211366547366026730 0ustar twernertwernerpackage org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Message; import java.util.*; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicBoolean; /** * Counterpart of AckSenderWindow. Simple FIFO buffer. * Every message received is ACK'ed (even duplicates) and added to a hashmap * keyed by seqno. The next seqno to be received is stored in next_to_remove. When a message with * a seqno less than next_to_remove is received, it will be discarded. The remove() method removes * and returns a message whose seqno is equal to next_to_remove, or null if not found.
* Change May 28 2002 (bela): replaced TreeSet with HashMap. Keys do not need to be sorted, and adding a key to * a sorted set incurs overhead. * * @author Bela Ban * @version $Id: AckReceiverWindow.java,v 1.25.2.8 2009/09/18 07:58:11 belaban Exp $ */ public class AckReceiverWindow { long next_to_remove=0; final Map msgs=new HashMap(); // keys: seqnos (Long), values: Messages static final Log log=LogFactory.getLog(AckReceiverWindow.class); final AtomicBoolean processing=new AtomicBoolean(false); public AckReceiverWindow(long initial_seqno) { this.next_to_remove=initial_seqno; } public AtomicBoolean getProcessing() { return processing; } /** Adds a new message. Message cannot be null * @return True if the message was added, false if not (e.g. duplicate, message was already present) */ public boolean add(long seqno, Message msg) { return add2(seqno, msg) == 1; } /** * * @param seqno * @param msg * @return -1 if not added because seqno < next_to_remove, 0 if not added because already present, * 1 if added successfully */ public byte add2(long seqno, Message msg) { if(msg == null) throw new IllegalArgumentException("msg must be non-null"); synchronized(msgs) { if(seqno < next_to_remove) return -1; if(!msgs.containsKey(seqno)) { msgs.put(seqno, msg); return 1; } else return 0; } } /** * Removes a message whose seqno is equal to next_to_remove, increments the latter. * Returns message that was removed, or null, if no message can be removed. Messages are thus * removed in order. */ public Message remove() { Message retval=null; synchronized(msgs) { long seqno=next_to_remove; try { retval=msgs.remove(seqno); } finally { if(retval != null) next_to_remove=++seqno; } } return retval; } /** * We need to have the lock on 'msgs' while we're setting processing to false (if no message is available), because * this prevents some other thread from adding a message. Use case: *
    *
  1. Thread 1 calls msgs.remove() --> returns null *
  2. Thread 2 calls add() *
  3. Thread 2 checks the CAS and returns because Thread1 hasn't yet released it *
  4. Thread 1 releases the CAS *
* The result here is that Thread 2 didn't go into the remove() processing and returned, and Thread 1 didn't see * the new message and therefore returned as well. Result: we have an unprocessed message in 'msgs' ! * @param processing * @return */ public Message remove(AtomicBoolean processing) { Message retval=null; synchronized(msgs) { long seqno=next_to_remove; try { retval=msgs.remove(seqno); } finally { if(retval != null) next_to_remove=++seqno; else processing.set(false); } } return retval; } /** * Removes as many messages as possible (in seqeuence, without gaps) * @return */ public List removeMany(AtomicBoolean processing) { List retval; Message msg; synchronized(msgs) { retval=new ArrayList(msgs.size()); // we remove msgs.size() messages *max* while((msg=msgs.remove(next_to_remove)) != null) { next_to_remove++; retval.add(msg); } if(retval.isEmpty()) processing.set(false); } return retval; } public Message removeOOBMessage() { Message retval; synchronized(msgs) { retval=msgs.get(next_to_remove); if(retval != null) { if(!retval.isFlagSet(Message.OOB)) { return null; } retval=msgs.remove(next_to_remove); next_to_remove++; } } return retval; } public boolean hasMessagesToRemove() { synchronized(msgs) { return msgs.containsKey(next_to_remove); } } public void reset() { synchronized(msgs) { msgs.clear(); } } public int size() { return msgs.size(); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(msgs.size()).append(" msgs (").append("next=").append(next_to_remove).append(")"); TreeSet s=new TreeSet(msgs.keySet()); if(!s.isEmpty()) { sb.append(" [").append(s.first()).append(" - ").append(s.last()).append("]"); sb.append(": ").append(s); } return sb.toString(); } public String printDetails() { StringBuilder sb=new StringBuilder(); sb.append(msgs.size()).append(" msgs (").append("next=").append(next_to_remove).append(")"). append(", msgs=" ).append(new TreeSet(msgs.keySet())); return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/StaticInterval.java0000644000175000017500000000247111366547366026315 0ustar twernertwerner package org.jgroups.stack; import org.jgroups.annotations.GuardedBy; /** * Manages retransmission timeouts. Always returns the next timeout, until the last timeout in the * array is reached. Returns the last timeout from then on. Note that this class is immutable, * so it shouldn't be shared between instances, as {@link #next()} will modify the state. * @author John Giorgiadis * @author Bela Ban * @version $Id: StaticInterval.java,v 1.3 2007/08/10 12:47:38 belaban Exp $ */ public class StaticInterval implements Interval { private int next=0; private final long[] values; public StaticInterval(long ... vals) { if (vals.length == 0) throw new IllegalArgumentException("zero length array passed as argument"); this.values=vals; } public Interval copy() { return new StaticInterval(values); } /** @return the next interval */ @GuardedBy("interval") public long next() { // we don't need to synchronize because this method won't be called concurrently; each entry has its own copy // of StaticInterval // synchronized(values) { if (next >= values.length) return(values[values.length-1]); else return(values[next++]); // } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/package.html0000644000175000017500000000010711366547366024771 0ustar twernertwerner Support for managing protocol stacks. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/ProtocolStack.java0000644000175000017500000003537611366547366026162 0ustar twernertwernerpackage org.jgroups.stack; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.TP; import org.jgroups.util.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.ReentrantLock; /** * A ProtocolStack manages a number of protocols layered above each other. It creates all * protocol classes, initializes them and, when ready, starts all of them, beginning with the * bottom most protocol. It also dispatches messages received from the stack to registered * objects (e.g. channel, GMP) and sends messages sent by those objects down the stack.

* The ProtocolStack makes use of the Configurator to setup and initialize stacks, and to * destroy them again when not needed anymore * @author Bela Ban * @version $Id: ProtocolStack.java,v 1.59.2.10 2009/09/11 12:12:08 belaban Exp $ */ public class ProtocolStack extends Protocol implements Transport { public static final int ABOVE = 1; // used by insertProtocol() public static final int BELOW = 2; // used by insertProtocol() private Protocol top_prot = null; private Protocol bottom_prot = null; private String setup_string; private JChannel channel = null; private volatile boolean stopped = true; /** Holds the shared transports, keyed by 'TP.singleton_name'. * The values are the transport and the use count for start() (decremented by stop() */ private static final ConcurrentMap> singleton_transports=new ConcurrentHashMap>(); public ProtocolStack(JChannel channel, String setup_string) throws ChannelException { this.setup_string=setup_string; this.channel=channel; ClassConfigurator.getInstance(true); // will create the singleton } /** Only used by Simulator; don't use */ public ProtocolStack() throws ChannelException { this(null,null); } /** * @deprecated Use {@link org.jgroups.stack.Protocol#getThreadFactory()} instead * @return */ public ThreadFactory getThreadFactory(){ getTransport().getThreadFactory(); TP transport=getTransport(); return transport != null? transport.getThreadFactory() : null; } @Deprecated public static ThreadFactory getTimerThreadFactory() { throw new UnsupportedOperationException("get the timer thread factory directly from the transport"); } /** * @deprecated Use {@link org.jgroups.stack.Protocol#getThreadFactory()} instead * @param f */ public void setThreadFactory(ThreadFactory f) { } /** * @deprecated Use {@link TP#setTimerThreadFactory(org.jgroups.util.ThreadFactory)} instead * @param f */ public static void setTimerThreadFactory(ThreadFactory f) { } public Channel getChannel() { return channel; } /** * @deprecated Use {@link org.jgroups.protocols.TP#getTimer()} to fetch the timer and call getCorePoolSize() directly * @return */ public int getTimerThreads() { TP transport=getTransport(); TimeScheduler timer; if(transport != null) { timer=transport.getTimer(); if(timer != null) return timer.getCorePoolSize(); } return -1; } /** Returns all protocols in a list, from top to bottom. These are not copies of protocols, so modifications will affect the actual instances ! */ public Vector getProtocols() { Protocol p; Vector v=new Vector(); p=top_prot; while(p != null) { v.addElement(p); p=p.getDownProtocol(); } return v; } /** Returns the bottom most protocol */ public TP getTransport() { Vector prots=getProtocols(); return (TP)(!prots.isEmpty()? prots.lastElement() : null); } public static ConcurrentMap> getSingletonTransports() { return singleton_transports; } /** * * @return Map> */ public Map dumpStats() { Protocol p; Map retval=new HashMap(), tmp; String prot_name; p=top_prot; while(p != null) { prot_name=p.getName(); tmp=p.dumpStats(); if(prot_name != null && tmp != null) retval.put(prot_name, tmp); p=p.getDownProtocol(); } return retval; } public Map dumpStats(String protocol_name) { Protocol p; Map retval=new HashMap(), tmp; String prot_name; p=top_prot; while(p != null) { prot_name=p.getName(); if(prot_name.equals(protocol_name)) { tmp=p.dumpStats(); if(tmp != null) retval.put(prot_name, tmp); } p=p.getDownProtocol(); } return retval; } /** * @deprecated Use {@link org.jgroups.protocols.TP#getTimer()} instead to fetch the timer from the * transport and then invoke the method on it * @return */ public String dumpTimerQueue() { TP transport=getTransport(); TimeScheduler timer; if(transport != null) { timer=transport.getTimer(); if(timer != null) return timer.dumpTaskQueue(); } return ""; } /** * Prints the names of the protocols, from the bottom to top. If include_properties is true, * the properties for each protocol will also be printed. */ public String printProtocolSpec(boolean include_properties) { StringBuilder sb=new StringBuilder(); Protocol prot=top_prot; Properties tmpProps; String name; Map.Entry entry; while(prot != null) { name=prot.getName(); if(name != null) { if("ProtocolStack".equals(name)) break; sb.append(name); if(include_properties) { tmpProps=prot.getProperties(); if(tmpProps != null) { sb.append('\n'); for(Iterator it=tmpProps.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); sb.append(entry).append("\n"); } } } sb.append('\n'); prot=prot.getDownProtocol(); } } return sb.toString(); } public String printProtocolSpecAsXML() { StringBuilder sb=new StringBuilder(); Protocol prot=bottom_prot; Properties tmpProps; String name; Map.Entry entry; int len, max_len=30; sb.append("\n"); while(prot != null) { name=prot.getName(); if(name != null) { if("ProtocolStack".equals(name)) break; sb.append(" <").append(name).append(" "); tmpProps=prot.getProperties(); if(tmpProps != null) { len=name.length(); String s; for(Iterator it=tmpProps.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); s=entry.getKey() + "=\"" + entry.getValue() + "\" "; if(len + s.length() > max_len) { sb.append("\n "); len=8; } sb.append(s); len+=s.length(); } } sb.append("/>\n"); prot=prot.getUpProtocol(); } } sb.append(""); return sb.toString(); } public String printProtocolSpecAsPlainString() { StringBuilder sb=new StringBuilder(); Protocol prot=bottom_prot; Properties tmpProps; boolean initialized=false; Class clazz; while(prot != null) { clazz=prot.getClass(); if(clazz.equals(ProtocolStack.class)) break; if(initialized) sb.append(":"); else initialized=true; sb.append(clazz.getName()); tmpProps=prot.getProperties(); if(tmpProps != null && !tmpProps.isEmpty()) { sb.append("("); boolean first=true; for(Map.Entry entry: tmpProps.entrySet()) { if(first) first=false; else sb.append(";"); sb.append(entry.getKey() + "=" + entry.getValue()); } sb.append(")"); } sb.append("\n"); prot=prot.getUpProtocol(); } return sb.toString(); } public void setup() throws Exception { if(top_prot == null) { top_prot=Configurator.setupProtocolStack(setup_string, this); top_prot.setUpProtocol(this); bottom_prot=Configurator.getBottommostProtocol(top_prot); List protocols=getProtocols(); Configurator.initProtocolStack(protocols); // calls init() on each protocol, from bottom to top } } /** * Creates a new protocol given the protocol specification. * @param prot_spec The specification of the protocol. Same convention as for specifying a protocol stack. * An exception will be thrown if the class cannot be created. Example: *

"VERIFY_SUSPECT(timeout=1500)"
Note that no colons (:) have to be * specified * @return Protocol The newly created protocol * @exception Exception Will be thrown when the new protocol cannot be created */ public Protocol createProtocol(String prot_spec) throws Exception { return Configurator.createProtocol(prot_spec, this); } /** * Inserts an already created (and initialized) protocol into the protocol list. Sets the links * to the protocols above and below correctly and adjusts the linked list of protocols accordingly. * Note that this method may change the value of top_prot or bottom_prot. * @param prot The protocol to be inserted. Before insertion, a sanity check will ensure that none * of the existing protocols have the same name as the new protocol. * @param position Where to place the protocol with respect to the neighbor_prot (ABOVE, BELOW) * @param neighbor_prot The name of the neighbor protocol. An exception will be thrown if this name * is not found * @exception Exception Will be thrown when the new protocol cannot be created, or inserted. */ public void insertProtocol(Protocol prot, int position, String neighbor_prot) throws Exception { Configurator.insertProtocol(prot, position, neighbor_prot, this); } public void insertProtocol(Protocol prot, int position, Class neighbor_prot) throws Exception { Configurator.insertProtocol(prot, position, neighbor_prot, this); } /** * Removes a protocol from the stack. Stops the protocol and readjusts the linked lists of * protocols. * @param prot_name The name of the protocol. Since all protocol names in a stack have to be unique * (otherwise the stack won't be created), the name refers to just 1 protocol. * @exception Exception Thrown if the protocol cannot be stopped correctly. */ public Protocol removeProtocol(String prot_name) throws Exception { return Configurator.removeProtocol(top_prot, prot_name); } /** Returns a given protocol or null if not found */ public Protocol findProtocol(String name) { Protocol tmp=top_prot; String prot_name; while(tmp != null) { prot_name=tmp.getName(); if(prot_name != null && prot_name.equals(name)) return tmp; tmp=tmp.getDownProtocol(); } return null; } public Protocol findProtocol(Class clazz) { Protocol tmp=top_prot; while(tmp != null) { Class protClass=tmp.getClass(); if(clazz.isAssignableFrom(protClass)){ return tmp; } tmp=tmp.getDownProtocol(); } return null; } public void destroy() { if(top_prot != null) { Configurator.destroyProtocolStack(getProtocols()); // destroys msg queues and threads top_prot=null; } } /** * Start all layers. The {@link Protocol#start()} method is called in each protocol, * from top to bottom. * Each layer can perform some initialization, e.g. create a multicast socket */ public void startStack(String cluster_name) throws Exception { if(stopped == false) return; Configurator.startProtocolStack(getProtocols(), cluster_name, singleton_transports); stopped=false; } /** * Iterates through all the protocols from top to bottom and does the following: *
    *
  1. Waits until all messages in the down queue have been flushed (ie., size is 0) *
  2. Calls stop() on the protocol *
*/ public void stopStack(String cluster_name) { if(stopped) return; Configurator.stopProtocolStack(getProtocols(), cluster_name, singleton_transports); stopped=true; } /** * Not needed anymore, just left in here for backwards compatibility with JBoss AS * @deprecated */ public void flushEvents() { } /*--------------------------- Transport interface ------------------------------*/ public void send(Message msg) throws Exception { down(new Event(Event.MSG, msg)); } public Object receive(long timeout) throws Exception { throw new Exception("ProtocolStack.receive(): not implemented !"); } /*------------------------- End of Transport interface ---------------------------*/ /*--------------------------- Protocol functionality ------------------------------*/ public String getName() {return "ProtocolStack";} public Object up(Event evt) { return channel.up(evt); } public Object down(Event evt) { if(top_prot != null) return top_prot.down(evt); return null; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/GossipClient.java0000644000175000017500000003761411366547366025773 0ustar twernertwernerpackage org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.InetSocketAddress; import java.util.*; /** * Local stub for clients to access one (or more) GossipRouters. Will use proprietary protocol * (using GossipData PDUs) based on TCP to connect to GossipRouter.

* Requires JDK >= 1.3 due to the use of Timer. * * @author Bela Ban Oct 4 2001 * @version $Id: GossipClient.java,v 1.18.2.4 2008/10/31 08:21:14 belaban Exp $ */ public class GossipClient { Timer timer=new Timer(true); /** Hashtable> */ final Map> groups=new Hashtable>(); // groups - List of Addresses private Refresher refresher_task=new Refresher(); final Vector

gossip_servers=new Vector
(); // a list of GossipRouters (IpAddress) boolean timer_running=false; boolean refresher_enabled=true; long refresh_interval=20000; // interval for re-registering; must be less than in GossipRouter int sock_conn_timeout=2000; // max number of ms to wait for socket establishment to GossipRouter int sock_read_timeout=0; // max number of ms to wait for socket reads (0 means block forever, or until the sock is closed) protected final Log log=LogFactory.getLog(this.getClass()); /** * Creates the GossipClient * @param gossip_host The address and port of the host on which the GossipRouter is running * @param expiry Interval (in msecs) for the refresher task */ public GossipClient(IpAddress gossip_host, long expiry) { init(gossip_host, expiry); } /** Creates the GossipClient @param gossip_hosts List of IpAddresses @param expiry Interval (in msecs) for the refresher task */ public GossipClient(Vector gossip_hosts, long expiry) { if(gossip_hosts == null) { if(log.isErrorEnabled()) log.error("empty set of GossipRouters given"); return; } for(IpAddress host: gossip_hosts) init(host, expiry); } public GossipClient(Vector gossip_hosts, long expiry, int sock_conn_timeout) { this(gossip_hosts, expiry); this.sock_conn_timeout=sock_conn_timeout; } public boolean isRefresherEnabled() { return refresher_enabled; } public void setRefresherEnabled(boolean refresher_enabled) { this.refresher_enabled=refresher_enabled; } public int getSocketConnectionTimeout() { return sock_conn_timeout; } public void setSocketConnectionTimeout(int sock_conn_timeout) { this.sock_conn_timeout=sock_conn_timeout; } public int getSocketReadTimeout() { return sock_read_timeout; } public void setSocketReadTimeout(int sock_read_timeout) { this.sock_read_timeout=sock_read_timeout; } public long getRefreshInterval() { return refresh_interval; } public void setRefreshInterval(long refresh_interval) { this.refresh_interval=refresh_interval; } public void stop() { timer_running=false; if(refresher_task != null) refresher_task.cancel(); timer.cancel(); groups.clear(); // provide another refresh tools in case the channel gets reconnected // timer=new Timer(); // refresher_task=new Refresher(); } public void destroy() { timer_running=false; timer.cancel(); groups.clear(); } /** * Adds a GossipRouter to be accessed. */ public void addGossipRouter(IpAddress gossip_host) { if(!gossip_servers.contains(gossip_host)) gossip_servers.addElement(gossip_host); } /** Adds the member to the given group. If the group already has an entry for the member, its timestamp will be updated, preventing the cache cleaner from removing the entry.

The entry will be registered with all GossipRouters that GossipClient is configured to access */ public void register(String group, Address mbr) { if(group == null || mbr == null) { if(log.isErrorEnabled()) log.error("group or mbr is null"); return; } List

mbrs=groups.get(group); if(mbrs == null) { mbrs=new LinkedList
(); mbrs.add(mbr); groups.put(group, mbrs); } else { if(!mbrs.contains(mbr)) mbrs.add(mbr); } _register(group, mbr); // update entry in GossipRouter if(refresher_enabled) { if(!timer_running) { timer=new Timer(true); refresher_task=new Refresher(); timer.schedule(refresher_task, refresh_interval, refresh_interval); timer_running=true; } } } public void unregister(String group, Address mbr) { if(group == null || mbr == null) { if(log.isErrorEnabled()) log.error("group or mbr is null"); return; } _unregister(group, mbr); // remove entry from GossipRouter } /** Returns all members of a given group @param group The group name @return List A list of Addresses */ public List
getMembers(String group) { if(group == null) { if(log.isErrorEnabled()) log.error("group is null"); return null; } List
result=_getMembers(group); if(log.isTraceEnabled()) log.trace("GET(" + group + ") --> " + result); return result; } /* ------------------------------------- Private methods ----------------------------------- */ final void init(IpAddress gossip_host, long refresh_interval) { this.refresh_interval=refresh_interval; addGossipRouter(gossip_host); } /** * Registers the group|mbr with *all* GossipRouters. */ void _register(String group, Address mbr) { Socket sock=null; DataOutputStream out=null; IpAddress entry; GossipData gossip_req; for(int i=0; i < gossip_servers.size(); i++) { entry=(IpAddress) gossip_servers.elementAt(i); if(entry.getIpAddress() == null || entry.getPort() == 0) { if(log.isErrorEnabled()) log.error("entry.host or entry.port is null"); continue; } try { if(log.isTraceEnabled()) log.trace("REGISTER(" + group + ", " + mbr + ") with GossipRouter at " + entry.getIpAddress() + ':' + entry.getPort()); sock=new Socket(); if(sock_read_timeout > 0) sock.setSoTimeout(sock_read_timeout); sock.connect(new InetSocketAddress(entry.getIpAddress(), entry.getPort()), sock_conn_timeout); out=new DataOutputStream(sock.getOutputStream()); gossip_req=new GossipData(GossipRouter.REGISTER, group, mbr, null); // must send GossipData as fast as possible, otherwise the request might be rejected gossip_req.writeTo(out); out.flush(); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception connecting to host " + entry); } finally { Util.close(out); Util.close(sock); } } } void _unregister(String group, Address mbr) { Socket sock=null; DataOutputStream out=null; IpAddress entry; GossipData gossip_req; for(int i=0; i < gossip_servers.size(); i++) { entry=(IpAddress) gossip_servers.elementAt(i); if(entry.getIpAddress() == null || entry.getPort() == 0) { if(log.isErrorEnabled()) log.error("entry.host or entry.port is null"); continue; } try { if(log.isTraceEnabled()) log.trace("UNREGISTER(" + group + ", " + mbr + ") with GossipRouter at " + entry.getIpAddress() + ':' + entry.getPort()); sock=new Socket(); if(sock_read_timeout > 0) sock.setSoTimeout(sock_read_timeout); sock.connect(new InetSocketAddress(entry.getIpAddress(), entry.getPort()), sock_conn_timeout); out=new DataOutputStream(sock.getOutputStream()); gossip_req=new GossipData(GossipRouter.UNREGISTER, group, mbr, null); // must send GossipData as fast as possible, otherwise the // request might be rejected gossip_req.writeTo(out); out.flush(); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception connecting to host " + entry); } finally { Util.close(out); if(sock != null) { try {sock.close();} catch(IOException e) {} } } } } /** * Sends a GET_MBR_REQ to *all* GossipRouters, merges responses. */ private List
_getMembers(String group) { List
ret=new LinkedList
(); Socket sock=null; SocketAddress destAddr; DataOutputStream out=null; DataInputStream in=null; IpAddress entry; GossipData gossip_req, gossip_rsp; Address mbr; for(int i=0; i < gossip_servers.size(); i++) { entry=(IpAddress) gossip_servers.elementAt(i); if(entry.getIpAddress() == null || entry.getPort() == 0) { if(log.isErrorEnabled()) log.error("entry.host or entry.port is null"); continue; } try { sock=new Socket(); if(sock_read_timeout > 0) sock.setSoTimeout(sock_read_timeout); destAddr=new InetSocketAddress(entry.getIpAddress(), entry.getPort()); sock.connect(destAddr, sock_conn_timeout); out=new DataOutputStream(sock.getOutputStream()); gossip_req=new GossipData(GossipRouter.GOSSIP_GET, group, null, null); // must send GossipData as fast as possible, otherwise the // request might be rejected gossip_req.writeTo(out); out.flush(); in=new DataInputStream(sock.getInputStream()); gossip_rsp=new GossipData(); gossip_rsp.readFrom(in); if(gossip_rsp.mbrs != null) { // merge with ret for(Iterator it=gossip_rsp.mbrs.iterator(); it.hasNext();) { mbr=(Address)it.next(); if(!ret.contains(mbr)) ret.add(mbr); } } } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception connecting to host " + entry); } finally { Util.close(out); Util.close(in); Util.close(sock); } } return ret; } /* ---------------------------------- End of Private methods ------------------------------- */ /** * Periodically iterates through groups and refreshes all registrations with GossipRouter */ private class Refresher extends TimerTask { public void run() { int num_items=0; String group; List
mbrs; if(log.isTraceEnabled()) log.trace("refresher task is run"); for(Map.Entry> entry: groups.entrySet()) { group=entry.getKey(); mbrs=entry.getValue(); if(mbrs != null) { for(Address mbr: mbrs) { if(log.isTraceEnabled()) log.trace("registering " + group + " : " + mbr); register(group, mbr); num_items++; } } } if(log.isTraceEnabled()) log.trace("refresher task done. Registered " + num_items + " items"); } } public static void main(String[] args) { Vector gossip_hosts=new Vector(); String host; InetAddress ip_addr; int port; boolean get=false, register=false, keep_running=false; String register_host=null; int register_port=0; String get_group=null, register_group=null; GossipClient gossip_client=null; List mbrs; long expiry=20000; int sock_conn_timeout=2000; int sock_read_timeout=3000; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { usage(); return; } if("-expiry".equals(args[i])) { expiry=Long.parseLong(args[++i]); continue; } if("-sock_read_timeout".equals(args[i])) { sock_read_timeout=Integer.parseInt(args[++i]); continue; } if("-sock_conn_timeout".equals(args[i])) { sock_conn_timeout=Integer.parseInt(args[++i]); continue; } if("-host".equals(args[i])) { host=args[++i]; port=Integer.parseInt(args[++i]); try { ip_addr=InetAddress.getByName(host); gossip_hosts.add(new IpAddress(ip_addr, port)); } catch(Exception ex) { System.err.println(ex); } continue; } if("-keep_running".equals(args[i])) { keep_running=true; continue; } if("-get".equals(args[i])) { get=true; get_group=args[++i]; continue; } if("-register".equals(args[i])) { register_group=args[++i]; register_host=args[++i]; register_port=Integer.parseInt(args[++i]); register=true; continue; } usage(); return; } if(gossip_hosts.isEmpty()) { System.err.println("At least 1 GossipRouter has to be given"); return; } if(!register && !get) { System.err.println("Neither get nor register command given, will not do anything"); return; } try { gossip_client=new GossipClient(gossip_hosts, expiry); gossip_client.setSocketConnectionTimeout(sock_conn_timeout); gossip_client.setSocketReadTimeout(sock_read_timeout); if(register) { System.out.println("Registering " + register_group + " --> " + register_host + ':' + register_port); gossip_client.register(register_group, new IpAddress(register_host, register_port)); } if(get) { System.out.println("Getting members for group " + get_group); mbrs=gossip_client.getMembers(get_group); System.out.println("Members for group " + get_group + " are " + mbrs); } } catch(Exception ex) { System.err.println(ex); } if(!keep_running) gossip_client.stop(); } static void usage() { System.out.println("GossipClient [-help] [-host ]+ " + "[-sock_conn_timeout ] [-sock_read_timeout ] " + " [-get ] [-register ] [-expiry ] " + "[-keep_running]]"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/AckMcastSenderWindow.java0000644000175000017500000004573611366547366027413 0ustar twernertwernerpackage org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.util.TimeScheduler; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; import java.util.concurrent.Future; /** * Keeps track of ACKs from receivers for each message. When a new message is * sent, it is tagged with a sequence number and the receiver set (set of * members to which the message is sent) and added to a hashtable * (key = sequence number, val = message + receiver set). Each incoming ACK * is noted and when all ACKs for a specific sequence number haven been * received, the corresponding entry is removed from the hashtable. A * retransmission thread periodically re-sends the message point-to-point to * all receivers from which no ACKs have been received yet. A view change or * suspect message causes the corresponding non-existing receivers to be * removed from the hashtable. *

* This class may need flow control in order to avoid needless * retransmissions because of timeouts. * @author Bela Ban June 9 1999, 2007 * @author John Georgiadis May 8 2001 * @version $Id: AckMcastSenderWindow.java,v 1.14.2.1 2008/01/22 10:01:01 belaban Exp $ */ public class AckMcastSenderWindow { /** * Called by retransmitter thread whenever a message needs to be re-sent * to a destination. dest has to be set in the * dst field of msg, as the latter was sent * multicast, but now we are sending a unicast message. Message has to be * copied before sending it (as headers will be appended and therefore * the message changed!). */ public interface RetransmitCommand { /** * Retranmit the given msg * @param seqno the sequence number associated with the message * @param msg the msg to retransmit (it should be a copy!) * @param dest the msg destination */ void retransmit(long seqno, Message msg, Address dest); } private static final long SEC=1000; /** * Default retransmit intervals (ms) - exponential approx. */ private static final Interval RETRANSMIT_TIMEOUTS=new StaticInterval(2 * SEC, 3 * SEC, 5 * SEC, 8 * SEC); /** * Default retransmit thread suspend timeout (ms) */ protected static final Log log=LogFactory.getLog(AckMcastSenderWindow.class); // Msg tables related /** * Table of pending msgs: seqno -> Entry */ private final Map msgs=new HashMap(); /** * List of recently suspected members. Used to cease retransmission to suspected members */ private final LinkedList suspects=new LinkedList(); /** * Max number in suspects list */ private static final int max_suspects=20; /** * List of acknowledged msgs since the last call to * getStableMessages() */ private final List stable_msgs=new LinkedList(); /** * Whether a call to waitUntilAcksReceived() is still active */ private boolean waiting=false; // Retransmission thread related /** * Whether retransmitter is externally provided or owned by this object */ private boolean retransmitter_owned; /** * The retransmission scheduler */ private TimeScheduler timer=null; /** * Retransmission intervals */ private Interval retransmit_intervals; /** * The callback object for retransmission */ private RetransmitCommand cmd=null; /** * Convert exception stack trace to string */ private static String _toString(Throwable ex) { StringWriter sw=new StringWriter(); PrintWriter pw=new PrintWriter(sw); ex.printStackTrace(pw); return (sw.toString()); } /** * @param entry the record associated with the msg to retransmit. It * contains the list of receivers that haven't yet ack reception */ private void _retransmit(Entry entry) { Address sender; boolean received; synchronized(entry) { for(Enumeration e=entry.senders.keys(); e.hasMoreElements();) { sender=(Address)e.nextElement(); received=((Boolean)entry.senders.get(sender)).booleanValue(); if(!received) { if(suspects.contains(sender)) { if(log.isWarnEnabled()) log.warn("removing " + sender + " from retransmit list as it is in the suspect list"); remove(sender); continue; } if(log.isInfoEnabled()) log.info("--> retransmitting msg #" + entry.seqno + " to " + sender); cmd.retransmit(entry.seqno, entry.msg.copy(), sender); } } } } /** * Setup this object's state * @param cmd the callback object for retranmissions * @param retransmit_intervals the interval between two consecutive * retransmission attempts * @param timer the external scheduler to use to schedule retransmissions * @param sched_owned if true, the scheduler is owned by this object and * can be started/stopped/destroyed. If false, the scheduler is shared * among multiple objects and start()/stop() should not be called from * within this object * @throws IllegalArgumentException if cmd is null */ private void init(RetransmitCommand cmd, Interval retransmit_intervals, TimeScheduler timer, boolean sched_owned) { if(cmd == null) { if(log.isErrorEnabled()) log.error("command is null. Cannot retransmit " + "messages !"); throw new IllegalArgumentException("cmd"); } retransmitter_owned=sched_owned; this.timer=timer; this.retransmit_intervals=retransmit_intervals; this.cmd=cmd; } /** * Create and start the retransmitter * @param cmd the callback object for retranmissions * @param retransmit_intervals the interval between two consecutive * retransmission attempts * @param sched the external scheduler to use to schedule retransmissions * @throws IllegalArgumentException if cmd is null */ public AckMcastSenderWindow(RetransmitCommand cmd, Interval retransmit_intervals, TimeScheduler sched) { init(cmd, retransmit_intervals, sched, false); } /** * Create and start the retransmitter * @param cmd the callback object for retranmissions * @param sched the external scheduler to use to schedule retransmissions * @throws IllegalArgumentException if cmd is null */ public AckMcastSenderWindow(RetransmitCommand cmd, TimeScheduler sched) { init(cmd, RETRANSMIT_TIMEOUTS, sched, false); } /** * Create and start the retransmitter * @param cmd the callback object for retranmissions * @param retransmit_intervals the interval between two consecutive * retransmission attempts * @throws IllegalArgumentException if cmd is null */ public AckMcastSenderWindow(RetransmitCommand cmd, Interval retransmit_intervals) { init(cmd, retransmit_intervals, new TimeScheduler(), true); } /** * Create and start the retransmitter * @param cmd the callback object for retranmissions * @throws IllegalArgumentException if cmd is null */ public AckMcastSenderWindow(RetransmitCommand cmd) { this(cmd, RETRANSMIT_TIMEOUTS); } /** * Adds a new message to the hash table. * @param seqno The sequence number associated with the message * @param msg The message (should be a copy!) * @param receivers The set of addresses to which the message was sent * and from which consequently an ACK is expected */ public void add(long seqno, Message msg, Vector receivers) { if(waiting) return; if(receivers.isEmpty()) return; synchronized(msgs) { if(msgs.get(new Long(seqno)) != null) return; // each entry needs its own retransmission interval, intervals are stateful *and* mutable, so we *need* to copy ! Entry e=new Entry(seqno, msg, receivers, retransmit_intervals.copy()); Future future=timer.scheduleWithDynamicInterval(e); e.setFuture(future); msgs.put(new Long(seqno), e); } } /** * An ACK has been received from sender. Tag the sender in * the hash table as 'received'. If all ACKs have been received, remove * the entry all together. * @param seqno The sequence number of the message for which an ACK has * been received. * @param sender The sender which sent the ACK */ public void ack(long seqno, Address sender) { Entry entry; Boolean received; synchronized(msgs) { entry=msgs.get(new Long(seqno)); if(entry == null) return; synchronized(entry) { received=(Boolean)entry.senders.get(sender); if(received == null || received.booleanValue()) return; // If not yet received entry.senders.put(sender, Boolean.TRUE); entry.num_received++; if(!entry.allReceived()) return; } synchronized(stable_msgs) { entry.cancel(); msgs.remove(new Long(seqno)); stable_msgs.add(new Long(seqno)); } // wake up waitUntilAllAcksReceived() method msgs.notifyAll(); } } /** * Remove obj from all receiver sets and wake up * retransmission thread. * @param obj the sender to remove */ public void remove(Address obj) { Long key; Entry entry; synchronized(msgs) { for(Iterator it=msgs.keySet().iterator(); it.hasNext();) { key=it.next(); entry=msgs.get(key); synchronized(entry) { //if (((Boolean)entry.senders.remove(obj)).booleanValue()) entry.num_received--; //if (!entry.allReceived()) continue; Boolean received=(Boolean)entry.senders.remove(obj); if(received == null) continue; // suspected member not in entry.senders ? if(received.booleanValue()) entry.num_received--; if(!entry.allReceived()) continue; } synchronized(stable_msgs) { entry.cancel(); msgs.remove(key); stable_msgs.add(key); } // wake up waitUntilAllAcksReceived() method msgs.notifyAll(); } } } /** * Process with address suspected is suspected: remove it * from all receiver sets. This means that no ACKs are expected from this * process anymore. * @param suspected The suspected process */ public void suspect(Address suspected) { if(log.isInfoEnabled()) log.info("suspect is " + suspected); remove(suspected); suspects.add(suspected); if(suspects.size() >= max_suspects) suspects.removeFirst(); } /** * @return a copy of stable messages, or null (if non available). Removes * all stable messages afterwards */ public List getStableMessages() { List retval; synchronized(stable_msgs) { retval=(!stable_msgs.isEmpty())? new LinkedList(stable_msgs) : null; if(!stable_msgs.isEmpty()) stable_msgs.clear(); } return retval; } public void clearStableMessages() { synchronized(stable_msgs) { stable_msgs.clear(); } } /** * @return the number of currently pending msgs */ public long size() { synchronized(msgs) { return (msgs.size()); } } /** * Returns the number of members for a given entry for which acks have to be received */ public long getNumberOfResponsesExpected(long seqno) { Entry entry=msgs.get(new Long(seqno)); if(entry != null) return entry.senders.size(); else return -1; } /** * Returns the number of members for a given entry for which acks have been received */ public long getNumberOfResponsesReceived(long seqno) { Entry entry=msgs.get(new Long(seqno)); if(entry != null) return entry.num_received; else return -1; } /** * Prints all members plus whether an ack has been received from those members for a given seqno */ public String printDetails(long seqno) { Entry entry=msgs.get(new Long(seqno)); if(entry != null) return entry.toString(); else return null; } /** * Waits until all outstanding messages have been ACKed by all receivers. * Takes into account suspicions and view changes. Returns when there are * no entries left in the hashtable. While waiting, no entries can be * added to the hashtable (they will be discarded). * @param timeout Miliseconds to wait. 0 means wait indefinitely. */ public void waitUntilAllAcksReceived(long timeout) { long time_to_wait, start_time, current_time; Address suspect; // remove all suspected members from retransmission for(Iterator it=suspects.iterator(); it.hasNext();) { suspect=(Address)it.next(); remove(suspect); } time_to_wait=timeout; waiting=true; if(timeout <= 0) { synchronized(msgs) { while(!msgs.isEmpty()) { try { msgs.wait(); } catch(InterruptedException ex) { } } } } else { start_time=System.currentTimeMillis(); synchronized(msgs) { while(!msgs.isEmpty()) { current_time=System.currentTimeMillis(); time_to_wait=timeout - (current_time - start_time); if(time_to_wait <= 0) break; try { msgs.wait(time_to_wait); } catch(InterruptedException ex) { if(log.isWarnEnabled()) log.warn(ex.toString()); } } } } waiting=false; } /** * Stop the rentransmition and clear all pending msgs. *

* If this retransmitter has been provided an externally managed * scheduler, then just clear all msgs and the associated tasks, else * stop the scheduler. In this case the method blocks until the * scheduler's thread is dead. Only the owner of the scheduler should * stop it. */ public void stop() { // i. If retransmitter is owned, stop it else cancel all tasks // ii. Clear all pending msgs and notify anyone waiting synchronized(msgs) { if(retransmitter_owned) { try { timer.stop(); } catch(InterruptedException ex) { if(log.isErrorEnabled()) log.error(_toString(ex)); } } else { for(Entry entry: msgs.values()) { entry.cancel(); } } msgs.clear(); msgs.notifyAll(); // wake up waitUntilAllAcksReceived() method } } /** * Remove all pending msgs from the hashtable. Cancel all associated * tasks in the retransmission scheduler */ public void reset() { if(waiting) return; synchronized(msgs) { for(Entry entry: msgs.values()) { entry.cancel(); } msgs.clear(); msgs.notifyAll(); } } public String toString() { StringBuilder ret; Entry entry; ret=new StringBuilder(); synchronized(msgs) { ret.append("msgs: (").append(msgs.size()).append(')'); for(Long key: msgs.keySet()) { entry=msgs.get(key); ret.append("key = ").append(key).append(", value = ").append(entry).append('\n'); } synchronized(stable_msgs) { ret.append("\nstable_msgs: ").append(stable_msgs); } } return(ret.toString()); } /** * The retransmit task executed by the scheduler in regular intervals */ private static abstract class Task implements TimeScheduler.Task { private final Interval intervals; private Future future; protected Task(Interval intervals) { this.intervals=intervals; } public void setFuture(Future future) { this.future=future; } public long nextInterval() { return intervals.next(); } public void cancel() { if(future != null) { future.cancel(false); future=null; } } } /** * The entry associated with a pending msg */ private class Entry extends Task { /** * The msg sequence number */ public final long seqno; /** * The msg to retransmit */ public Message msg=null; /** * destination addr -> boolean (true = received, false = not) */ public final Hashtable senders=new Hashtable(); /** * How many destinations have received the msg */ public int num_received=0; public Entry(long seqno, Message msg, Vector dests, Interval intervals) { super(intervals); this.seqno=seqno; this.msg=msg; for(int i=0; i < dests.size(); i++) senders.put(dests.elementAt(i), Boolean.FALSE); } boolean allReceived() { return (num_received >= senders.size()); } /** * Retransmit this entry */ public void run() { _retransmit(this); } public String toString() { StringBuilder buf=new StringBuilder(); buf.append("num_received = ").append(num_received).append(", received msgs = ").append(senders); return buf.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/Configurator.java0000644000175000017500000010026111366547366026017 0ustar twernertwernerpackage org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.protocols.TP; import org.jgroups.util.Tuple; import org.jgroups.util.Util; import java.io.IOException; import java.io.PushbackReader; import java.io.Reader; import java.io.StringReader; import java.util.*; import java.util.concurrent.ConcurrentMap; /** * The task if this class is to setup and configure the protocol stack. A string describing * the desired setup, which is both the layering and the configuration of each layer, is * given to the configurator which creates and configures the protocol stack and returns * a reference to the top layer (Protocol).

* Future functionality will include the capability to dynamically modify the layering * of the protocol stack and the properties of each layer. * @author Bela Ban * @version $Id: Configurator.java,v 1.28.4.9 2009/07/01 13:43:11 vlada Exp $ */ public class Configurator { protected static final Log log=LogFactory.getLog(Configurator.class); /** * The configuration string has a number of entries, separated by a ':' (colon). * Each entry consists of the name of the protocol, followed by an optional configuration * of that protocol. The configuration is enclosed in parentheses, and contains entries * which are name/value pairs connected with an assignment sign (=) and separated by * a semicolon. *

UDP(in_port=5555;out_port=4445):FRAG(frag_size=1024)

* The first entry defines the bottommost layer, the string is parsed * left to right and the protocol stack constructed bottom up. Example: the string * "UDP(in_port=5555):FRAG(frag_size=32000):DEBUG" results is the following stack:

     *
     *   -----------------------
     *  | DEBUG                 |
     *  |-----------------------|
     *  | FRAG frag_size=32000  |
     *  |-----------------------|
     *  | UDP in_port=32000     |
     *   -----------------------
     * 
*/ public static Protocol setupProtocolStack(String configuration, ProtocolStack st) throws Exception { Protocol protocol_stack=null; Vector protocol_configs; Vector protocols; protocol_configs=parseConfigurations(configuration); protocols=createProtocols(protocol_configs, st); if(protocols == null) return null; protocol_stack=connectProtocols(protocols); return protocol_stack; } public static void initProtocolStack(List protocols) throws Exception { Collections.reverse(protocols); for(Protocol prot: protocols) { prot.init(); } } public static void startProtocolStack(List protocols, String cluster_name, final Map> singletons) throws Exception { Protocol above_prot=null; for(final Protocol prot: protocols) { if(prot instanceof TP) { String singleton_name=((TP)prot).getSingletonName(); if(singleton_name != null && singleton_name.length() > 0 && cluster_name != null) { TP transport=(TP)prot; final Map up_prots=transport.getUpProtocols(); synchronized(singletons) { synchronized(up_prots) { Set keys=up_prots.keySet(); if(keys.contains(cluster_name)) throw new IllegalStateException("cluster '" + cluster_name + "' is already connected to singleton " + "transport: " + keys); for(Iterator> it=up_prots.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); Protocol tmp=entry.getValue(); if(tmp == above_prot) { it.remove(); } } if(above_prot != null) { TP.ProtocolAdapter ad=new TP.ProtocolAdapter(cluster_name, prot.getName(), above_prot, prot, transport.getThreadNamingPattern(), transport.getLocalAddress()); ad.setProtocolStack(above_prot.getProtocolStack()); above_prot.setDownProtocol(ad); up_prots.put(cluster_name, ad); } } Tuple val=singletons.get(singleton_name); if(val == null) { singletons.put(singleton_name, new Tuple(transport,(short)1)); } else { short num_starts=val.getVal2(); val.setVal2((short)(num_starts +1)); if(num_starts >= 1) { if(above_prot != null) above_prot.up(new Event(Event.SET_LOCAL_ADDRESS, transport.getLocalAddress())); continue; } else { prot.start(); above_prot=prot; continue; } } } } } prot.start(); above_prot=prot; } } public static void stopProtocolStack(List protocols, String cluster_name, final Map> singletons) { for(final Protocol prot: protocols) { if(prot instanceof TP) { String singleton_name=((TP)prot).getSingletonName(); if(singleton_name != null && singleton_name.length() > 0) { TP transport=(TP)prot; final Map up_prots=transport.getUpProtocols(); synchronized(up_prots) { up_prots.remove(cluster_name); } synchronized(singletons) { Tuple val=singletons.get(singleton_name); if(val != null) { short num_starts=(short)Math.max(val.getVal2() -1, 0); val.setVal2(num_starts); if(num_starts > 0) { continue; // don't call TP.stop() if we still have references to the transport } else singletons.remove(singleton_name); } } } } prot.stop(); } } public static void destroyProtocolStack(List protocols) { for(Protocol prot: protocols) { prot.destroy(); } } public static Protocol findProtocol(Protocol prot_stack, String name) { String s; Protocol curr_prot=prot_stack; while(true) { s=curr_prot.getName(); if(s == null) continue; if(s.equals(name)) return curr_prot; curr_prot=curr_prot.getDownProtocol(); if(curr_prot == null) break; } return null; } public static Protocol getBottommostProtocol(Protocol prot_stack) { Protocol tmp=null, curr_prot=prot_stack; while(true) { if((tmp=curr_prot.getDownProtocol()) == null) break; curr_prot=tmp; } return curr_prot; } /** * Creates a new protocol given the protocol specification. Initializes the properties and starts the * up and down handler threads. * @param prot_spec The specification of the protocol. Same convention as for specifying a protocol stack. * An exception will be thrown if the class cannot be created. Example: *
"VERIFY_SUSPECT(timeout=1500)"
Note that no colons (:) have to be * specified * @param stack The protocol stack * @return Protocol The newly created protocol * @exception Exception Will be thrown when the new protocol cannot be created */ public static Protocol createProtocol(String prot_spec, ProtocolStack stack) throws Exception { ProtocolConfiguration config; Protocol prot; if(prot_spec == null) throw new Exception("Configurator.createProtocol(): prot_spec is null"); // parse the configuration for this protocol config=new ProtocolConfiguration(prot_spec); // create an instance of the protocol class and configure it prot=config.createLayer(stack); prot.init(); return prot; } /** * Inserts an already created (and initialized) protocol into the protocol list. Sets the links * to the protocols above and below correctly and adjusts the linked list of protocols accordingly. * This should be done before starting the stack. * @param prot The protocol to be inserted. Before insertion, a sanity check will ensure that none * of the existing protocols have the same name as the new protocol. * @param position Where to place the protocol with respect to the neighbor_prot (ABOVE, BELOW) * @param neighbor_prot The name of the neighbor protocol. An exception will be thrown if this name * is not found * @param stack The protocol stack * @exception Exception Will be thrown when the new protocol cannot be created, or inserted. */ public static void insertProtocol(Protocol prot, int position, String neighbor_prot, ProtocolStack stack) throws Exception { if(neighbor_prot == null) throw new Exception("Configurator.insertProtocol(): neighbor_prot is null"); if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) throw new Exception("position has to be ABOVE or BELOW"); Protocol neighbor=stack.findProtocol(neighbor_prot); if(neighbor == null) throw new Exception("protocol \"" + neighbor_prot + "\" not found in " + stack.printProtocolSpec(false)); insertProtocol(prot, neighbor, position); } public static void insertProtocol(Protocol prot, int position, Class neighbor_prot, ProtocolStack stack) throws Exception { if(neighbor_prot == null) throw new Exception("Configurator.insertProtocol(): neighbor_prot is null"); if(position != ProtocolStack.ABOVE && position != ProtocolStack.BELOW) throw new Exception("position has to be ABOVE or BELOW"); Protocol neighbor=stack.findProtocol(neighbor_prot); if(neighbor == null) throw new Exception("protocol \"" + neighbor_prot + "\" not found in " + stack.printProtocolSpec(false)); insertProtocol(prot, neighbor, position); } protected static void insertProtocol(Protocol prot, Protocol neighbor, int position) { // connect to the protocol layer below and above if(position == ProtocolStack.BELOW) { prot.setUpProtocol(neighbor); Protocol below=neighbor.getDownProtocol(); prot.setDownProtocol(below); if(below != null) below.setUpProtocol(prot); neighbor.setDownProtocol(prot); } else { // ABOVE is default Protocol above=neighbor.getUpProtocol(); prot.setUpProtocol(above); if(above != null) above.setDownProtocol(prot); prot.setDownProtocol(neighbor); neighbor.setUpProtocol(prot); } } /** * Removes a protocol from the stack. Stops the protocol and readjusts the linked lists of * protocols. * @param prot_name The name of the protocol. Since all protocol names in a stack have to be unique * (otherwise the stack won't be created), the name refers to just 1 protocol. * @exception Exception Thrown if the protocol cannot be stopped correctly. */ public static Protocol removeProtocol(Protocol top_prot, String prot_name) throws Exception { if(prot_name == null) return null; Protocol prot=findProtocol(top_prot, prot_name); if(prot == null) return null; Protocol above=prot.getUpProtocol(), below=prot.getDownProtocol(); if(above != null) above.setDownProtocol(below); if(below != null) below.setUpProtocol(above); prot.setUpProtocol(null); prot.setDownProtocol(null); return prot; } /* ------------------------------- Private Methods ------------------------------------- */ /** * Creates a protocol stack by iterating through the protocol list and connecting * adjacent layers. The list starts with the topmost layer and has the bottommost * layer at the tail. * @param protocol_list List of Protocol elements (from top to bottom) * @return Protocol stack */ private static Protocol connectProtocols(Vector protocol_list) { Protocol current_layer=null, next_layer=null; for(int i=0; i < protocol_list.size(); i++) { current_layer=(Protocol)protocol_list.elementAt(i); if(i + 1 >= protocol_list.size()) break; next_layer=(Protocol)protocol_list.elementAt(i + 1); next_layer.setDownProtocol(current_layer); current_layer.setUpProtocol(next_layer); if(current_layer instanceof TP) { String singleton_name= ((TP)current_layer).getSingletonName(); if(singleton_name != null && singleton_name.length() > 0) { ConcurrentMap up_prots=((TP)current_layer).getUpProtocols(); String key; synchronized(up_prots) { while(true) { key=Global.DUMMY + System.currentTimeMillis(); if(up_prots.containsKey(key)) continue; up_prots.put(key, next_layer); break; } } current_layer.setUpProtocol(null); } } } return current_layer; } /** * Get a string of the form "P1(config_str1):P2:P3(config_str3)" and return * ProtocolConfigurations for it. That means, parse "P1(config_str1)", "P2" and * "P3(config_str3)" * @param config_str Configuration string * @return Vector of strings */ public static Vector parseProtocols(String config_str) throws IOException { Vector retval=new Vector(); PushbackReader reader=new PushbackReader(new StringReader(config_str)); int ch; StringBuilder sb; boolean running=true; while(running) { String protocol_name=readWord(reader); sb=new StringBuilder(); sb.append(protocol_name); ch=read(reader); if(ch == -1) { retval.add(sb.toString()); break; } if(ch == ':') { // no attrs defined retval.add(sb.toString()); continue; } if(ch == '(') { // more attrs defined reader.unread(ch); String attrs=readUntil(reader, ')'); sb.append(attrs); retval.add(sb.toString()); } else { retval.add(sb.toString()); } while(true) { ch=read(reader); if(ch == ':') { break; } if(ch == -1) { running=false; break; } } } reader.close(); return retval; } private static int read(Reader reader) throws IOException { int ch=-1; while((ch=reader.read()) != -1) { if(!Character.isWhitespace(ch)) return ch; } return ch; } /** * Return a number of ProtocolConfigurations in a vector * @param configuration protocol-stack configuration string * @return Vector of ProtocolConfigurations */ public static Vector parseConfigurations(String configuration) throws Exception { Vector retval=new Vector(); Vector protocol_string=parseProtocols(configuration); String component_string; ProtocolConfiguration protocol_config; if(protocol_string == null) return null; for(int i=0; i < protocol_string.size(); i++) { component_string=(String)protocol_string.elementAt(i); protocol_config=new ProtocolConfiguration(component_string); retval.addElement(protocol_config); } return retval; } private static String readUntil(Reader reader, char c) throws IOException { StringBuilder sb=new StringBuilder(); int ch; while((ch=read(reader)) != -1) { sb.append((char)ch); if(ch == c) break; } return sb.toString(); } private static String readWord(PushbackReader reader) throws IOException { StringBuilder sb=new StringBuilder(); int ch; while((ch=read(reader)) != -1) { if(Character.isLetterOrDigit(ch) || ch == '_' || ch == '.' || ch == '$') { sb.append((char)ch); } else { reader.unread(ch); break; } } return sb.toString(); } /** * Takes vector of ProtocolConfigurations, iterates through it, creates Protocol for * each ProtocolConfiguration and returns all Protocols in a vector. * @param protocol_configs Vector of ProtocolConfigurations * @param stack The protocol stack * @return Vector of Protocols */ private static Vector createProtocols(Vector protocol_configs, final ProtocolStack stack) throws Exception { Vector retval=new Vector(); ProtocolConfiguration protocol_config; Protocol layer; String singleton_name; for(int i=0; i < protocol_configs.size(); i++) { protocol_config=protocol_configs.elementAt(i); singleton_name=protocol_config.getProperties().getProperty(Global.SINGLETON_NAME); if(singleton_name != null && singleton_name.trim().length() > 0) { Map> singleton_transports=ProtocolStack.getSingletonTransports(); synchronized(singleton_transports) { if(i > 0) { // crude way to check whether protocol is a transport throw new IllegalArgumentException("Property 'singleton_name' can only be used in a transport" + " protocol (was used in " + protocol_config.getProtocolName() + ")"); } Tuple val=singleton_transports.get(singleton_name); layer=val != null? val.getVal1() : null; if(layer != null) { retval.add(layer); } else { layer=protocol_config.createLayer(stack); if(layer == null) return null; singleton_transports.put(singleton_name, new Tuple((TP)layer,(short)0)); retval.addElement(layer); } } continue; } layer=protocol_config.createLayer(stack); if(layer == null) return null; retval.addElement(layer); } sanityCheck(retval); return retval; } /** Throws an exception if sanity check fails. Possible sanity check is uniqueness of all protocol names */ public static void sanityCheck(Vector protocols) throws Exception { Vector names=new Vector(); Protocol prot; String name; ProtocolReq req; Vector req_list=new Vector(); int evt_type; // Checks for unique names for(int i=0; i < protocols.size(); i++) { prot=protocols.elementAt(i); name=prot.getName(); for(int j=0; j < names.size(); j++) { if(name.equals(names.elementAt(j))) { throw new Exception("Configurator.sanityCheck(): protocol name " + name + " has been used more than once; protocol names have to be unique !"); } } names.addElement(name); } // Checks whether all requirements of all layers are met for(int i=0; i < protocols.size(); i++) { prot=protocols.elementAt(i); req=new ProtocolReq(prot.getName()); req.up_reqs=prot.requiredUpServices(); req.down_reqs=prot.requiredDownServices(); req.up_provides=prot.providedUpServices(); req.down_provides=prot.providedDownServices(); req_list.addElement(req); } for(int i=0; i < req_list.size(); i++) { req=req_list.elementAt(i); // check whether layers above this one provide corresponding down services if(req.up_reqs != null) { for(int j=0; j < req.up_reqs.size(); j++) { evt_type=((Integer)req.up_reqs.elementAt(j)).intValue(); if(!providesDownServices(i, req_list, evt_type)) { throw new Exception("Configurator.sanityCheck(): event " + Event.type2String(evt_type) + " is required by " + req.name + ", but not provided by any of the layers above"); } } } // check whether layers below this one provide corresponding up services if(req.down_reqs != null) { // check whether layers above this one provide up_reqs for(int j=0; j < req.down_reqs.size(); j++) { evt_type=((Integer)req.down_reqs.elementAt(j)).intValue(); if(!providesUpServices(i, req_list, evt_type)) { throw new Exception("Configurator.sanityCheck(): event " + Event.type2String(evt_type) + " is required by " + req.name + ", but not provided by any of the layers below"); } } } } } /** Check whether any of the protocols 'below' end_index provide evt_type */ static boolean providesUpServices(int end_index, Vector req_list, int evt_type) { ProtocolReq req; for(int i=0; i < end_index; i++) { req=(ProtocolReq)req_list.elementAt(i); if(req.providesUpService(evt_type)) return true; } return false; } /** Checks whether any of the protocols 'above' start_index provide evt_type */ static boolean providesDownServices(int start_index, Vector req_list, int evt_type) { ProtocolReq req; for(int i=start_index; i < req_list.size(); i++) { req=(ProtocolReq)req_list.elementAt(i); if(req.providesDownService(evt_type)) return true; } return false; } /* --------------------------- End of Private Methods ---------------------------------- */ private static class ProtocolReq { Vector up_reqs=null; Vector down_reqs=null; Vector up_provides=null; Vector down_provides=null; String name=null; ProtocolReq(String name) { this.name=name; } boolean providesUpService(int evt_type) { int type; if(up_provides != null) { for(int i=0; i < up_provides.size(); i++) { type=((Integer)up_provides.elementAt(i)).intValue(); if(type == evt_type) return true; } } return false; } boolean providesDownService(int evt_type) { int type; if(down_provides != null) { for(int i=0; i < down_provides.size(); i++) { type=((Integer)down_provides.elementAt(i)).intValue(); if(type == evt_type) return true; } } return false; } public String toString() { StringBuilder ret=new StringBuilder(); ret.append('\n' + name + ':'); if(up_reqs != null) ret.append("\nRequires from above: " + printUpReqs()); if(down_reqs != null) ret.append("\nRequires from below: " + printDownReqs()); if(up_provides != null) ret.append("\nProvides to above: " + printUpProvides()); if(down_provides != null) ret.append("\nProvides to below: ").append(printDownProvides()); return ret.toString(); } String printUpReqs() { StringBuilder ret; ret=new StringBuilder("["); if(up_reqs != null) { for(int i=0; i < up_reqs.size(); i++) { ret.append(Event.type2String(((Integer)up_reqs.elementAt(i)).intValue()) + ' '); } } return ret.toString() + ']'; } String printDownReqs() { StringBuilder ret=new StringBuilder("["); if(down_reqs != null) { for(int i=0; i < down_reqs.size(); i++) { ret.append(Event.type2String(((Integer)down_reqs.elementAt(i)).intValue()) + ' '); } } return ret.toString() + ']'; } String printUpProvides() { StringBuilder ret=new StringBuilder("["); if(up_provides != null) { for(int i=0; i < up_provides.size(); i++) { ret.append(Event.type2String(((Integer)up_provides.elementAt(i)).intValue()) + ' '); } } return ret.toString() + ']'; } String printDownProvides() { StringBuilder ret=new StringBuilder("["); if(down_provides != null) { for(int i=0; i < down_provides.size(); i++) ret.append(Event.type2String(((Integer)down_provides.elementAt(i)).intValue()) + ' '); } return ret.toString() + ']'; } } /** * Parses and encapsulates the specification for 1 protocol of the protocol stack, e.g. * UNICAST(timeout=5000) */ public static class ProtocolConfiguration { private String protocol_name=null; private String properties_str=null; private final Properties properties=new Properties(); private static final String protocol_prefix="org.jgroups.protocols"; /** * Creates a new ProtocolConfiguration. * @param config_str The configuration specification for the protocol, e.g. *
VERIFY_SUSPECT(timeout=1500)
*/ public ProtocolConfiguration(String config_str) throws Exception { setContents(config_str); } public ProtocolConfiguration() { } public String getProtocolName() { return protocol_name; } public void setProtocolName(String name) { protocol_name=name; } public Properties getProperties() { return properties; } public void setPropertiesString(String props) { this.properties_str=props; } void setContents(String config_str) throws Exception { int index=config_str.indexOf('('); // e.g. "UDP(in_port=3333)" int end_index=config_str.lastIndexOf(')'); if(index == -1) { protocol_name=config_str; } else { if(end_index == -1) { throw new Exception("Configurator.ProtocolConfiguration.setContents(): closing ')' " + "not found in " + config_str + ": properties cannot be set !"); } else { properties_str=config_str.substring(index + 1, end_index); protocol_name=config_str.substring(0, index); } } /* "in_port=5555;out_port=6666" */ if(properties_str != null) { String[] components=properties_str.split(";"); for(int i=0; i < components.length; i++) { String name, value, comp=components[i]; index=comp.indexOf('='); if(index == -1) { throw new Exception("Configurator.ProtocolConfiguration.setContents(): '=' not found in " + comp); } name=comp.substring(0, index); value=comp.substring(index + 1, comp.length()); properties.put(name, value); } } } private Protocol createLayer(ProtocolStack prot_stack) throws Exception { Protocol retval=null; if(protocol_name == null) return null; String defaultProtocolName=protocol_prefix + '.' + protocol_name; Class clazz=null; try { clazz=Util.loadClass(defaultProtocolName, this.getClass()); } catch(ClassNotFoundException e) { } if(clazz == null) { try { clazz=Util.loadClass(protocol_name, this.getClass()); } catch(ClassNotFoundException e) { } if(clazz == null) { throw new Exception("unable to load class for protocol " + protocol_name + " (either as an absolute - " + protocol_name + " - or relative - " + defaultProtocolName + " - package name)!"); } } try { retval=(Protocol)clazz.newInstance(); if(retval == null) throw new Exception("creation of instance for protocol " + protocol_name + "failed !"); retval.setProtocolStack(prot_stack); if(properties != null) if(!retval.setPropertiesInternal(properties)) throw new IllegalArgumentException("the following properties in " + protocol_name + " are not recognized: " + properties); } catch(InstantiationException inst_ex) { log.error("an instance of " + protocol_name + " could not be created. Please check that it implements" + " interface Protocol and that is has a public empty constructor !"); throw inst_ex; } return retval; } public String toString() { StringBuilder retval=new StringBuilder(); retval.append("Protocol: "); if(protocol_name == null) retval.append(""); else retval.append(protocol_name); if(properties != null) retval.append("(" + properties + ')'); return retval.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/GossipData.java0000644000175000017500000000375111366547366025421 0ustar twernertwerner// $Id: GossipData.java,v 1.3.6.1 2008/01/22 10:01:10 belaban Exp $ package org.jgroups.stack; import org.jgroups.Address; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.Vector; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; /** * Encapsulates data sent between GossipRouter and GossipClient * @author Bela Ban Oct 4 2001 */ public class GossipData implements Streamable { byte type=0; // One of GossipRouter type, e.g. CONNECT, REGISTER etc String group=null; // CONNECT, GET_REQ and GET_RSP Address addr=null; // CONNECT List mbrs=null; // GET_RSP public GossipData() { // for streamable } public GossipData(byte type) { this.type=type; } public GossipData(byte type, String group, Address addr, List mbrs) { this.type=type; this.group=group; this.addr=addr; this.mbrs=mbrs; } public byte getType() {return type;} public String getGroup() {return group;} public Address getAddress() {return addr;} public List getMembers() {return mbrs;} public void setMembers(List mbrs) { this.mbrs=mbrs; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(GossipRouter.type2String(type)).append( "(").append("group=").append(group).append(", addr=").append(addr); sb.append(", mbrs=").append(mbrs); return sb.toString(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); Util.writeString(group, out); Util.writeAddress(addr, out); Util.writeAddresses(mbrs, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); group=Util.readString(in); addr=Util.readAddress(in); mbrs=(List)Util.readAddresses(in, LinkedList.class); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/Retransmitter.java0000644000175000017500000002036311366547366026224 0ustar twernertwerner// $Id: Retransmitter.java,v 1.23.2.4 2009/09/29 04:34:44 belaban Exp $ package org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.util.TimeScheduler; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Maintains a pool of sequence numbers of messages that need to be retransmitted. Messages * are aged and retransmission requests sent according to age (configurable backoff). If a * TimeScheduler instance is given to the constructor, it will be used, otherwise Reransmitter * will create its own. The retransmit timeouts have to be set first thing after creating an instance. * The add() method adds the sequence numbers of messages to be retransmitted. The * remove() method removes a sequence number again, cancelling retransmission requests for it. * Whenever a message needs to be retransmitted, the RetransmitCommand.retransmit() method is called. * It can be used e.g. by an ack-based scheme (e.g. AckSenderWindow) to retransmit a message to the receiver, or * by a nak-based scheme to send a retransmission request to the sender of the missing message.
* Changes Aug 2007 (bela): the retransmitter was completely rewritten. Entry was removed, instead all tasks * are directly placed into a hashmap, keyed by seqnos. When a message has been received, we simply remove * the task from the hashmap and cancel it. This simplifies the code and avoids having to iterate through * the (previous) message list linearly on removal. Performance is about the same, or slightly better in * informal tests. * @author Bela Ban * @version $Revision: 1.23.2.4 $ */ public class Retransmitter { private static final long SEC=1000; /** Default retransmit intervals (ms) - exponential approx. */ private Interval RETRANSMIT_TIMEOUTS=new StaticInterval(2 * SEC, 3 * SEC, 5 * SEC, 8 * SEC); private Address sender=null; private final ConcurrentMap msgs=new ConcurrentHashMap(11); private RetransmitCommand cmd=null; private TimeScheduler timer=null; protected static final Log log=LogFactory.getLog(Retransmitter.class); /** Retransmit command (see Gamma et al.) used to retrieve missing messages */ public interface RetransmitCommand { /** * Get the missing messages between sequence numbers * first_seqno and last_seqno. This can either be done by sending a * retransmit message to destination sender (nak-based scheme), or by * retransmitting the missing message(s) to sender (ack-based scheme). * @param first_seqno The sequence number of the first missing message * @param last_seqno The sequence number of the last missing message * @param sender The destination of the member to which the retransmit request will be sent * (nak-based scheme), or to which the message will be retransmitted (ack-based scheme). */ void retransmit(long first_seqno, long last_seqno, Address sender); } /** * Create a new Retransmitter associated with the given sender address * @param sender the address from which retransmissions are expected or to which retransmissions are sent * @param cmd the retransmission callback reference * @param sched retransmissions scheduler */ public Retransmitter(Address sender, RetransmitCommand cmd, TimeScheduler sched) { init(sender, cmd, sched); } public void setRetransmitTimeouts(Interval interval) { if(interval != null) RETRANSMIT_TIMEOUTS=interval; } /** * Add the given range [first_seqno, last_seqno] in the list of * entries eligible for retransmission. If first_seqno > last_seqno, * then the range [last_seqno, first_seqno] is added instead *

* If retransmitter thread is suspended, wake it up */ public void add(long first_seqno, long last_seqno) { if(first_seqno > last_seqno) { long tmp=first_seqno; first_seqno=last_seqno; last_seqno=tmp; } Task new_task; for(long seqno=first_seqno; seqno <= last_seqno; seqno++) { // each task needs its own retransmission interval, as they are stateful *and* mutable, so we *need* to copy ! new_task=new Task(seqno, RETRANSMIT_TIMEOUTS.copy(), cmd, sender); Task old_task=msgs.putIfAbsent(seqno, new_task); if(old_task == null) // only schedule if we actually *added* the new task ! new_task.doSchedule(); // Entry adds itself to the timer } } /** * Remove the given sequence number from the list of seqnos eligible * for retransmission. If there are no more seqno intervals in the * respective entry, cancel the entry from the retransmission * scheduler and remove it from the pending entries */ public int remove(long seqno) { Task task=msgs.remove(seqno); if(task != null) { task.cancel(); return task.getNumRetransmits(); } return -1; } /** * Reset the retransmitter: clear all msgs and cancel all the * respective tasks */ public void reset() { for(Task task: msgs.values()) task.cancel(); msgs.clear(); } public void stop() { reset(); } public String toString() { int size=size(); StringBuilder sb=new StringBuilder(); sb.append(size).append(" messages to retransmit: ").append(msgs.keySet()); return sb.toString(); } public int size() { return msgs.size(); } /* ------------------------------- Private Methods -------------------------------------- */ /** * Init this object * * @param sender the address from which retransmissions are expected * @param cmd the retransmission callback reference * @param sched retransmissions scheduler */ private void init(Address sender, RetransmitCommand cmd, TimeScheduler sched) { this.sender=sender; this.cmd=cmd; timer=sched; } /* ---------------------------- End of Private Methods ------------------------------------ */ /** * The retransmit task executed by the scheduler in regular intervals */ private class Task implements TimeScheduler.Task { private final Interval intervals; private long seqno=-1; private volatile Future future; private Address msg_sender=null; protected volatile int num_retransmits=0; private RetransmitCommand command; private volatile boolean cancelled=false; protected Task(long seqno, Interval intervals, RetransmitCommand cmd, Address msg_sender) { this.seqno=seqno; this.intervals=intervals; this.command=cmd; this.msg_sender=msg_sender; } public int getNumRetransmits() { return num_retransmits; } public long nextInterval() { return intervals.next(); } public void doSchedule() { if(cancelled) { return; } long delay=intervals.next(); future=timer.schedule(this, delay, TimeUnit.MILLISECONDS); } public void cancel() { if(!cancelled) { cancelled=true; } if(future != null) future.cancel(true); } public void run() { if(cancelled) { return; } try { command.retransmit(seqno, seqno, msg_sender); num_retransmits++; } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed retransmission task", t); } doSchedule(); } public String toString() { return String.valueOf(seqno); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/NakReceiverWindow.java0000644000175000017500000005533511366547366026756 0ustar twernertwernerpackage org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.annotations.GuardedBy; import org.jgroups.util.TimeScheduler; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Keeps track of messages according to their sequence numbers. Allows * messages to be added out of order, and with gaps between sequence numbers. * Method remove() removes the first message with a sequence * number that is 1 higher than next_to_remove (this variable is * then incremented), or it returns null if no message is present, or if no * message's sequence number is 1 higher. *

* When there is a gap upon adding a message, its seqno will be added to the * Retransmitter, which (using a timer) requests retransmissions of missing * messages and keeps on trying until the message has been received, or the * member who sent the message is suspected. * * There are 3 variables which keep track of messages: *

    *
  • low: lowest seqno, modified on stable(). On stable(), we purge msgs [low digest.highest_delivered] *
  • highest_delivered: the highest delivered seqno, updated on remove(). The next message to be removed is highest_delivered + 1 *
  • highest_received: the highest received message, updated on add (if a new message is added, not updated e.g. * if a missing msg was received) *
*

* Note that the first seqno expected is 1. This design is described in doc/design.NAKACK.txt *

* Example: * 1,2,3,5,6,8: low=1, highest_delivered=2 (or 3, depending on whether remove() was called !), highest_received=8 * * @author Bela Ban May 27 1999, May 2004, Jan 2007 * @author John Georgiadis May 8 2001 * @version $Id: NakReceiverWindow.java,v 1.52.2.3 2009/09/11 12:07:44 belaban Exp $ */ public class NakReceiverWindow { public interface Listener { void missingMessageReceived(long seqno, Address original_sender); void messageGapDetected(long from, long to, Address src); } private final ReadWriteLock lock=new ReentrantReadWriteLock(); Address local_addr=null; /** Lowest seqno, modified on stable(). On stable(), we purge msgs [low digest.highest_delivered] */ @GuardedBy("lock") private long low=0; /** The highest delivered seqno, updated on remove(). The next message to be removed is highest_delivered + 1 */ @GuardedBy("lock") private long highest_delivered=0; /** The highest received message, updated on add (if a new message is added, not updated e.g. if a missing msg * was received) */ @GuardedBy("lock") private long highest_received=0; /** ConcurrentMap. Maintains messages keyed by (sorted) sequence numbers */ private final ConcurrentMap xmit_table=new ConcurrentHashMap(); /** * Messages that have been received in order are sent up the stack (= delivered to the application). Delivered * messages are removed from NakReceiverWindow.xmit_table and moved to NakReceiverWindow.delivered_msgs, where * they are later garbage collected (by STABLE). Since we do retransmits only from sent messages, never * received or delivered messages, we can turn the moving to delivered_msgs off, so we don't keep the message * around, and don't need to wait for garbage collection to remove them. */ private boolean discard_delivered_msgs=false; private final AtomicBoolean processing=new AtomicBoolean(false); /** If value is > 0, the retransmit buffer is bounded: only the max_xmit_buf_size latest messages are kept, * older ones are discarded when the buffer size is exceeded. A value <= 0 means unbounded buffers */ private int max_xmit_buf_size=0; /** if not set, no retransmitter thread will be started. Useful if * protocols do their own retransmission (e.g PBCAST) */ private Retransmitter retransmitter=null; private Listener listener=null; protected static final Log log=LogFactory.getLog(NakReceiverWindow.class); /** The highest stable() seqno received */ long highest_stability_seqno=0; /** The loss rate (70% of the new value and 30% of the old value) */ private double smoothed_loss_rate=0.0; /** * Creates a new instance with the given retransmit command * * @param sender The sender associated with this instance * @param cmd The command used to retransmit a missing message, will * be invoked by the table. If null, the retransmit thread will not be started * @param highest_delivered_seqno The next seqno to remove is highest_delivered_seqno +1 * @param lowest_seqno The low seqno purged * @param sched the external scheduler to use for retransmission * requests of missing msgs. If it's not provided or is null, an internal */ public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, long lowest_seqno, TimeScheduler sched) { this(null, sender, cmd, highest_delivered_seqno, lowest_seqno, sched); } public NakReceiverWindow(Address local_addr, Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, long lowest_seqno, TimeScheduler sched) { this.local_addr=local_addr; highest_delivered=highest_delivered_seqno; highest_received=highest_delivered; low=Math.min(lowest_seqno, highest_delivered); if(sched == null) throw new IllegalStateException("timer has to be provided and cannot be null"); if(cmd != null) retransmitter=new Retransmitter(sender, cmd, sched); } /** * Creates a new instance with the given retransmit command * * @param sender The sender associated with this instance * @param cmd The command used to retransmit a missing message, will * be invoked by the table. If null, the retransmit thread will not be started * @param highest_delivered_seqno The next seqno to remove is highest_delivered_seqno +1 * @param sched the external scheduler to use for retransmission * requests of missing msgs. If it's not provided or is null, an internal */ public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno, TimeScheduler sched) { this(sender, cmd, highest_delivered_seqno, 0, sched); } /** * Creates a new instance with the given retransmit command * * @param sender The sender associated with this instance * @param cmd The command used to retransmit a missing message, will * be invoked by the table. If null, the retransmit thread will not be started * @param highest_delivered_seqno The next seqno to remove is highest_delivered_seqno +1 */ public NakReceiverWindow(Address sender, Retransmitter.RetransmitCommand cmd, long highest_delivered_seqno) { this(sender, cmd, highest_delivered_seqno, null); } public AtomicBoolean getProcessing() { return processing; } public void setRetransmitTimeouts(Interval timeouts) { retransmitter.setRetransmitTimeouts(timeouts); } public void setDiscardDeliveredMessages(boolean flag) { this.discard_delivered_msgs=flag; } public int getMaxXmitBufSize() { return max_xmit_buf_size; } public void setMaxXmitBufSize(int max_xmit_buf_size) { this.max_xmit_buf_size=max_xmit_buf_size; } public void setListener(Listener l) { this.listener=l; } public int getPendingXmits() { return retransmitter!= null? retransmitter.size() : 0; } /** * Returns the loss rate, which is defined as the number of pending retransmission requests / the total number of * messages in xmit_table * @return The loss rate */ public double getLossRate() { int total_msgs=size(); int pending_xmits=getPendingXmits(); if(pending_xmits == 0 || total_msgs == 0) return 0.0; return pending_xmits / (double)total_msgs; } public double getSmoothedLossRate() { return smoothed_loss_rate; } /** Set the new smoothed_loss_rate value to 70% of the new value and 30% of the old value */ private void setSmoothedLossRate() { double new_loss_rate=getLossRate(); if(smoothed_loss_rate == 0) { smoothed_loss_rate=new_loss_rate; } else { smoothed_loss_rate=smoothed_loss_rate * .3 + new_loss_rate * .7; } } /** * Adds a message according to its seqno (sequence number). *

* There are 4 cases where messages are added: *

    *
  1. seqno is the next to be expected seqno: added to map *
  2. seqno is <= highest_delivered: discard as we've already delivered it *
  3. seqno is smaller than the next expected seqno: missing message, add it *
  4. seqno is greater than the next expected seqno: add it to map and fill the gaps with null messages * for retransmission. Add the seqno to the retransmitter too *
* @return True if the message was added successfully, false otherwise (e.g. duplicate message) */ public boolean add(long seqno, Message msg) { long old_next, next_to_add; lock.writeLock().lock(); try { next_to_add=highest_received +1; old_next=next_to_add; // Case #1: we received the expected seqno: most common path if(seqno == next_to_add) { xmit_table.put(new Long(seqno), msg); return true; } // Case #2: we received a message that has already been delivered: discard it if(seqno <= highest_delivered) { if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("seqno "); sb.append(seqno).append(" is smaller than ").append(next_to_add).append("); discarding message"); log.trace(sb); } return false; } // Case #3: we finally received a missing message. // Case #2 handled seqno <= highest_delivered, so this // seqno *must* be between highest_delivered and next_to_add if(seqno < next_to_add) { Message tmp=xmit_table.putIfAbsent(seqno, msg); // only set message if not yet received (bela July 23 2003) if(tmp == null) { // key/value was not present int num_xmits=retransmitter.remove(seqno); if(log.isTraceEnabled()) log.trace(new StringBuilder("added missing msg ").append(msg.getSrc()).append('#').append(seqno)); if(listener != null && num_xmits > 0) { try {listener.missingMessageReceived(seqno, msg.getSrc());} catch(Throwable t) {} } return true; } else { // key/value was present return false; } } // Case #4: we received a seqno higher than expected: add NULL_MSG values for missing messages, add to Retransmitter if(seqno > next_to_add) { xmit_table.put(seqno, msg); retransmitter.add(old_next, seqno -1); // BUT: add only null messages to xmitter if(listener != null) { try {listener.messageGapDetected(next_to_add, seqno, msg.getSrc());} catch(Throwable t) {} } return true; } } finally { highest_received=Math.max(highest_received, seqno); // setSmoothedLossRate(); lock.writeLock().unlock(); } return true; } public Message remove() { Message retval=null; lock.writeLock().lock(); try { long next_to_remove=highest_delivered +1; retval=xmit_table.get(next_to_remove); if(retval != null) { // message exists and is ready for delivery if(discard_delivered_msgs) { Address sender=retval.getSrc(); if(!local_addr.equals(sender)) { // don't remove if we sent the message ! xmit_table.remove(next_to_remove); } } highest_delivered=next_to_remove; return retval; } // message has not yet been received (gap in the message sequence stream) // drop all messages that have not been received if(max_xmit_buf_size > 0 && xmit_table.size() > max_xmit_buf_size) { highest_delivered=next_to_remove; retransmitter.remove(next_to_remove); } return null; } finally { // setSmoothedLossRate(); lock.writeLock().unlock(); } } /** * Removes a message from the NakReceiverWindow. The message is in order. If the next message cannot be removed * (e.g. because there is no message available, or because the next message is not in order), null will * be returned. When null is returned, processing is set to false. This is needed to atomically remove a message * and set the atomic boolean variable to false. * @param processing * @return */ public Message remove(final AtomicBoolean processing) { Message retval=null; boolean found=false; lock.writeLock().lock(); try { long next_to_remove=highest_delivered +1; retval=xmit_table.get(next_to_remove); found=retval != null; if(retval != null) { // message exists and is ready for delivery if(discard_delivered_msgs) { Address sender=retval.getSrc(); if(!local_addr.equals(sender)) { // don't remove if we sent the message ! xmit_table.remove(next_to_remove); } } highest_delivered=next_to_remove; return retval; } // message has not yet been received (gap in the message sequence stream) // drop all messages that have not been received if(max_xmit_buf_size > 0 && xmit_table.size() > max_xmit_buf_size) { highest_delivered=next_to_remove; retransmitter.remove(next_to_remove); } return null; } finally { if(!found) processing.set(false); // setSmoothedLossRate(); lock.writeLock().unlock(); } } /** * Removes as many messages as possible * @return List A list of messages, or null if no available messages were found */ public List removeMany() { List retval=null; lock.writeLock().lock(); try { while(true) { long next_to_remove=highest_delivered +1; Message msg=xmit_table.get(next_to_remove); if(msg != null) { // message exists and is ready for delivery if(discard_delivered_msgs) { Address sender=msg.getSrc(); if(!local_addr.equals(sender)) { // don't remove if we sent the message ! xmit_table.remove(next_to_remove); } } highest_delivered=next_to_remove; if(retval == null) retval=new LinkedList(); retval.add(msg); continue; } // message has not yet been received (gap in the message sequence stream) // drop all messages that have not been received if(max_xmit_buf_size > 0 && xmit_table.size() > max_xmit_buf_size) { highest_delivered=next_to_remove; retransmitter.remove(next_to_remove); continue; } return retval; } } finally { lock.writeLock().unlock(); } } public Message removeOOBMessage() { lock.writeLock().lock(); try { Message retval=xmit_table.get(highest_delivered +1); if(retval != null && retval.isFlagSet(Message.OOB)) { return remove(); } return null; } finally { lock.writeLock().unlock(); } } public boolean hasMessagesToRemove() { lock.readLock().lock(); try { return xmit_table.get(highest_delivered + 1) != null; } finally { lock.readLock().unlock(); } } public boolean hasRegularMessageToRemove() { lock.readLock().lock(); try { Message msg=xmit_table.get(highest_delivered + 1); return msg != null && !msg.isFlagSet(Message.OOB); } finally { lock.readLock().unlock(); } } /** * Delete all messages <= seqno (they are stable, that is, have been received at all members). * Stop when a number > seqno is encountered (all messages are ordered on seqnos). */ public void stable(long seqno) { lock.writeLock().lock(); try { if(seqno > highest_delivered) { if(log.isErrorEnabled()) log.error("seqno " + seqno + " is > highest_delivered " + highest_delivered + "; ignoring stability message"); return; } // we need to remove all seqnos *including* seqno if(!xmit_table.isEmpty()) { for(long i=low; i <= seqno; i++) { xmit_table.remove(i); } } // remove all seqnos below seqno from retransmission for(long i=low; i <= seqno; i++) { retransmitter.remove(i); } highest_stability_seqno=Math.max(highest_stability_seqno, seqno); low=Math.max(low, seqno); } finally { lock.writeLock().unlock(); } } /** * Reset the retransmitter and the nak window
*/ public void reset() { lock.writeLock().lock(); try { retransmitter.reset(); _reset(); } finally { lock.writeLock().unlock(); } } /** * @return the lowest sequence number of a message that has been * delivered or is a candidate for delivery (by the next call to * remove()) */ public long getLowestSeen() { lock.readLock().lock(); try { return low; } finally { lock.readLock().unlock(); } } /** Returns the highest sequence number of a message consumed by the application (by remove()). * Note that this is different from the highest deliverable seqno. E.g. in 23,24,26,27,29, the highest * delivered message may be 22, whereas the highest deliverable message may be 24 ! * @return the highest sequence number of a message consumed by the * application (by remove()) */ public long getHighestDelivered() { lock.readLock().lock(); try { return highest_delivered; } finally { lock.readLock().unlock(); } } /** * Returns the highest sequence number received so far (which may be * higher than the highest seqno delivered so far; e.g., for * 1,2,3,5,6 it would be 6. * * @see NakReceiverWindow#getHighestDelivered */ public long getHighestReceived() { lock.readLock().lock(); try { return highest_received; } finally { lock.readLock().unlock(); } } /** * Returns the message from xmit_table * @param seqno * @return Message from xmit_table */ public Message get(long seqno) { lock.readLock().lock(); try { return xmit_table.get(seqno); } finally { lock.readLock().unlock(); } } public int size() { lock.readLock().lock(); try { return xmit_table.size(); } finally { lock.readLock().unlock(); } } public String toString() { lock.readLock().lock(); try { return printMessages(); } finally { lock.readLock().unlock(); } } /** * Prints xmit_table. Requires read lock to be present * @return String */ String printMessages() { StringBuilder sb=new StringBuilder(); sb.append('[').append(low).append(" : ").append(highest_delivered).append(" (").append(highest_received).append(")"); if(xmit_table != null && !xmit_table.isEmpty()) { int non_received=0; for(Map.Entry entry: xmit_table.entrySet()) { if(entry.getValue() == null) non_received++; } sb.append(" (size=").append(xmit_table.size()).append(", missing=").append(non_received). append(", highest stability=").append(highest_stability_seqno).append(')'); } sb.append(']'); return sb.toString(); } public String printLossRate() { StringBuilder sb=new StringBuilder(); int num_missing=getPendingXmits(); int num_received=size(); int total=num_missing + num_received; sb.append("total=").append(total).append(" (received=").append(num_received).append(", missing=") .append(num_missing).append("), loss rate=").append(getLossRate()) .append(", smoothed loss rate=").append(smoothed_loss_rate); return sb.toString(); } /* ------------------------------- Private Methods -------------------------------------- */ /** * Reset the Nak window. Should be called from within a writeLock() context. *

* i. Delete all received entries
* ii. Reset all indices
*/ private void _reset() { xmit_table.clear(); low=0; highest_delivered=0; // next (=first) to deliver will be 1 highest_received=0; highest_stability_seqno=0; } /* --------------------------- End of Private Methods ----------------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/AckMcastReceiverWindow.java0000644000175000017500000000737711366547366027736 0ustar twernertwerner// $Id: AckMcastReceiverWindow.java,v 1.8.10.1 2008/01/22 10:01:00 belaban Exp $ package org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.net.UnknownHostException; /** * Keeps track of messages received from various senders. Acks each message received and checks whether * it was already delivered. If yes, the message is discarded, otherwise it is delivered (passed up). * The messages contain sequence numbers of old messages to be deleted, those are removed from the * message table. * * @author Bela Ban June 17 1999 */ public class AckMcastReceiverWindow { final Hashtable msgs=new Hashtable(); // sender -- Vector (of seqnos) protected static final Log log=LogFactory.getLog(AckMcastReceiverWindow.class); /** Records the sender/seqno pair in the message table @param sender The sender of the message @param seqno The sequence number associated with the message @return boolean If false, message is already present. Otherwise true. */ public boolean add(Object sender, long seqno) { Vector seqnos=(Vector)msgs.get(sender); Long val=new Long(seqno); if(seqnos == null) { seqnos=new Vector(); seqnos.addElement(val); msgs.put(sender, seqnos); return true; } if(seqnos.contains(val)) return false; seqnos.addElement(val); return true; } public void remove(Object sender, Vector seqnos) { Vector v=(Vector)msgs.get(sender); Long seqno; if(v != null && seqnos != null) { for(int i=0; i < seqnos.size(); i++) { seqno=(Long)seqnos.elementAt(i); v.removeElement(seqno); } } } public long size() { long ret=0; for(Enumeration e=msgs.elements(); e.hasMoreElements();) { ret+=((Vector)e.nextElement()).size(); } return ret; } public void reset() { removeAll(); } public void removeAll() {msgs.clear();} public void suspect(Object sender) { if(log.isInfoEnabled()) log.info("suspect is " + sender); msgs.remove(sender); } public String toString() { StringBuilder ret=new StringBuilder(); Object sender; for(Enumeration e=msgs.keys(); e.hasMoreElements();) { sender=e.nextElement(); ret.append(sender).append(" --> ").append(msgs.get(sender)).append('\n'); } return ret.toString(); } public static void main(String[] args) throws UnknownHostException { AckMcastReceiverWindow win=new AckMcastReceiverWindow(); Address sender1=new IpAddress("janet", 1111); Address sender2=new IpAddress("janet", 4444); Address sender3=new IpAddress("janet", 6767); Address sender4=new IpAddress("janet", 3333); win.add(sender1, 1); win.add(sender1, 2); win.add(sender3, 2); win.add(sender2, 2); win.add(sender4, 2); win.add(sender1, 3); win.add(sender1, 2); System.out.println(win); win.suspect(sender1); System.out.println(win); win.add(sender1, 1); win.add(sender1, 2); win.add(sender1, 3); win.add(sender1, 4); win.add(sender1, 5); win.add(sender1, 6); win.add(sender1, 7); win.add(sender1, 8); System.out.println(win); Vector seqnos=new Vector(); seqnos.addElement(new Long(4)); seqnos.addElement(new Long(6)); seqnos.addElement(new Long(8)); win.remove(sender2, seqnos); System.out.println(win); win.remove(sender1, seqnos); System.out.println(win); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/Interval.java0000644000175000017500000000077311366547366025150 0ustar twernertwerner package org.jgroups.stack; import org.jgroups.annotations.Immutable; import org.jgroups.annotations.GuardedBy; /** * Interface which returns a time series, one value at a time calling next() * @author Bela Ban * @version $Id: Interval.java,v 1.3 2007/08/10 12:32:17 belaban Exp $ */ public interface Interval { /** @return the next interval */ public long next() ; /** Returns a copy of the state. If there is no state, this method may return a ref to itself */ Interval copy(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/RouterStub.java0000644000175000017500000001612511366547366025500 0ustar twernertwernerpackage org.jgroups.stack; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.util.Util; /** * Client stub that talks to a remote GossipRouter * * @author Bela Ban * @version $Id: RouterStub.java,v 1.30.4.3 2009/02/06 16:41:55 vlada Exp $ */ public class RouterStub { public final static int STATUS_CONNECTED = 0; public final static int STATUS_DISCONNECTED = 1; private String router_host = null; // name of the router host private int router_port = 0; // port on which router listens on // router_host private Socket sock = null; // socket connecting to the router private DataOutputStream output = null; // output stream associated with // sock private DataInputStream input = null; // input stream associated with sock private Address local_addr = null; // addr of group mbr. Once assigned, // remains the same private volatile int connectionState = STATUS_DISCONNECTED; private static final Log log = LogFactory.getLog(RouterStub.class); private ConnectionListener conn_listener; private String groupname = null; private InetAddress bind_addr = null; private DatagramSocket my_sock = null; public interface ConnectionListener { void connectionStatusChange(int state); } /** * Creates a stub for a remote Router object. * * @param routerHost * The name of the router's host * @param routerPort * The router's port */ public RouterStub(String routerHost,int routerPort,InetAddress bindAddress){ router_host = routerHost != null ? routerHost : "localhost"; router_port = routerPort; bind_addr = bindAddress; } public boolean isConnected() { return connectionState == STATUS_CONNECTED; } public void setConnectionListener(ConnectionListener conn_listener) { this.conn_listener = conn_listener; } public synchronized Address getLocalAddress() throws SocketException { if(local_addr == null){ my_sock = new DatagramSocket(0, bind_addr); local_addr = new IpAddress(bind_addr, my_sock.getLocalPort()); } return local_addr; } /** * Register this process with the router under groupname. * * @param groupname * The name of the group under which to register */ public synchronized void connect(String groupname) throws Exception { if(groupname == null || groupname.length() == 0) throw new Exception("groupname is null"); if(!isConnected()){ this.groupname = groupname; try{ sock = new Socket(router_host, router_port, bind_addr, 0); sock.setSoLinger(true, 5); // 5 secs ! output = new DataOutputStream(sock.getOutputStream()); GossipData req = new GossipData(GossipRouter.CONNECT, groupname, getLocalAddress(), null); req.writeTo(output); output.flush(); input = new DataInputStream(sock.getInputStream()); boolean connectedOk = input.readBoolean(); if(connectedOk) connectionStateChanged(STATUS_CONNECTED); else throw new Exception("Failed to get connection ack from gossip router"); }catch(Exception e){ if(log.isWarnEnabled()) log.warn(this + " failed connecting to " + router_host + ":" + router_port); Util.close(sock); Util.close(input); Util.close(output); connectionStateChanged(STATUS_DISCONNECTED); throw e; } } } public synchronized void disconnect() { try{ GossipData req = new GossipData(GossipRouter.DISCONNECT, groupname, local_addr, null); req.writeTo(output); output.flush(); }catch(Exception e){ }finally{ Util.close(output); Util.close(input); Util.close(sock); Util.close(my_sock); sock = null; connectionStateChanged(STATUS_DISCONNECTED); } } public String toString() { return "RouterStub[local_address=" + local_addr + ",router_host=" + router_host + ",router_port=" + router_port + ",connected=" + isConnected() + "]"; } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { // null destination represents mcast sendToSingleMember(null, data, offset, length); } public synchronized void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { if(isConnected()){ try{ // 1. Group name output.writeUTF(groupname); // 2. Destination address (null in case of mcast) Util.writeAddress(dest, output); // 3. Length of byte buffer output.writeInt(data.length); // 4. Byte buffer output.write(data, 0, data.length); output.flush(); }catch(SocketException se){ if(log.isWarnEnabled()) log.warn("Router stub " + this + " did not send message to " + (dest == null ? "mcast" : dest + " since underlying socket is closed")); connectionStateChanged(STATUS_DISCONNECTED); }catch(Exception e){ if(log.isErrorEnabled()) log.error("Router stub " + this + " failed sending message to router"); connectionStateChanged(STATUS_DISCONNECTED); throw new Exception("dest=" + dest + " (" + length + " bytes)", e); } } } public DataInputStream getInputStream() throws IOException { if(!isConnected()){ throw new IOException("InputStream is closed"); } return input; } private void connectionStateChanged(int newState) { boolean notify = connectionState != newState; connectionState = newState; if(notify && conn_listener != null){ try{ conn_listener.connectionStatusChange(newState); }catch(Throwable t){ log.error("failed notifying ConnectionListener " + conn_listener, t); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/AckSenderWindow.java0000644000175000017500000001231311366547366026404 0ustar twernertwerner// $Id: AckSenderWindow.java,v 1.27.2.7 2009/09/15 08:00:00 belaban Exp $ package org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.util.TimeScheduler; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * ACK-based sliding window for a sender. Messages are added to the window keyed by seqno * When an ACK is received, the corresponding message is removed. The Retransmitter * continously iterates over the entries in the hashmap, retransmitting messages based on their * creation time and an (increasing) timeout. When there are no more messages in the retransmission * table left, the thread terminates. It will be re-activated when a new entry is added to the * retransmission table. * @author Bela Ban */ public class AckSenderWindow implements Retransmitter.RetransmitCommand { RetransmitCommand retransmit_command = null; // called to request XMIT of msg final ConcurrentMap msgs=new ConcurrentHashMap(); Interval interval=new StaticInterval(400,800,1200,1600); final Retransmitter retransmitter; static final Log log=LogFactory.getLog(AckSenderWindow.class); long lowest=Global.DEFAULT_FIRST_UNICAST_SEQNO; // lowest seqno, used by ack() public interface RetransmitCommand { void retransmit(long seqno, Message msg); } public AckSenderWindow(RetransmitCommand com) { retransmit_command = com; retransmitter = new Retransmitter(null, this, null); retransmitter.setRetransmitTimeouts(interval); } public AckSenderWindow(RetransmitCommand com, Interval interval, TimeScheduler sched) { retransmit_command = com; this.interval = interval; retransmitter = new Retransmitter(null, this, sched); retransmitter.setRetransmitTimeouts(interval); } public AckSenderWindow(RetransmitCommand com, Interval interval, TimeScheduler sched, Address sender) { retransmit_command = com; this.interval = interval; retransmitter = new Retransmitter(sender, this, sched); retransmitter.setRetransmitTimeouts(interval); } /** Only to be used for testing purposes */ public synchronized long getLowest() { return lowest; } public void reset() { msgs.clear(); retransmitter.reset(); lowest=Global.DEFAULT_FIRST_UNICAST_SEQNO; } /** * Adds a new message to the retransmission table. The message will be retransmitted (based on timeouts passed into * AckSenderWindow until (1) an ACK is received or (2) the AckSenderWindow is stopped (@link{#reset}) */ public void add(long seqno, Message msg) { msgs.putIfAbsent(seqno, msg); retransmitter.add(seqno, seqno); } /** * Removes all messages less than or equal to seqno from msgs, and cancels their retransmission */ public void ack(long seqno) { long prev_lowest; synchronized(this) { if(seqno < lowest) return; // not really needed, but we can avoid the max() call altogether... prev_lowest=lowest; lowest=Math.max(lowest, seqno +1); } for(long i=prev_lowest; i <= seqno; i++) { msgs.remove(i); retransmitter.remove(i); } } /** Returns the message with the lowest seqno */ public synchronized Message getLowestMessage() { return msgs.get(lowest); } public int size() { return msgs.size(); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(msgs.size()).append(" msgs (").append(retransmitter.size()).append(" to retransmit): "); TreeSet keys=new TreeSet(msgs.keySet()); if(!keys.isEmpty()) sb.append(keys.first()).append(" - ").append(keys.last()); else sb.append("[]"); return sb.toString(); } public String printDetails() { StringBuilder sb=new StringBuilder(); sb.append(msgs.size()).append(" msgs (").append(retransmitter.size()).append(" to retransmit): "). append(new TreeSet(msgs.keySet())); return sb.toString(); } /* -------------------------------- Retransmitter.RetransmitCommand interface ------------------- */ public void retransmit(long first_seqno, long last_seqno, Address sender) { Message msg; if(retransmit_command != null) { if(log.isTraceEnabled()) log.trace(new StringBuilder("retransmitting messages ").append(first_seqno). append(" - ").append(last_seqno).append(" from ").append(sender)); for(long i = first_seqno; i <= last_seqno; i++) { if((msg=msgs.get(i)) != null) { // find the message to retransmit retransmit_command.retransmit(i, msg); } } } } /* ----------------------------- End of Retransmitter.RetransmitCommand interface ---------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/StateTransferInfo.java0000644000175000017500000000476511366547366026772 0ustar twernertwerner// $Id: StateTransferInfo.java,v 1.15.2.1 2008/01/22 10:01:00 belaban Exp $ package org.jgroups.stack; import java.io.InputStream; import java.io.OutputStream; import org.jgroups.Address; /** * Contains parameters for state transfer. Exchanged between channel and STATE_TRANSFER * layer. The state is retrieved from 'target'. If target is null, then the state will be retrieved from the oldest * member (usually the coordinator). * @author Bela Ban * @version $Id: StateTransferInfo.java,v 1.15.2.1 2008/01/22 10:01:00 belaban Exp $ */ public class StateTransferInfo { public Address target=null; public long timeout=0; public byte[] state=null; public String state_id=null; public InputStream inputStream = null; public OutputStream outputStream = null; public StateTransferInfo() { } public StateTransferInfo(Address target) { this.target=target; } public StateTransferInfo(Address target, long timeout) { this.target=target; this.timeout=timeout; } public StateTransferInfo(Address target, String state_id, long timeout) { this.target=target; this.state_id=state_id; this.timeout=timeout; } public StateTransferInfo(Address target, String state_id, long timeout, byte[] state) { this.target=target; this.state=state; this.state_id=state_id; this.timeout=timeout; } public StateTransferInfo(Address target, InputStream is, String state_id) { this.target=target; this.state_id=state_id; this.inputStream=is; } public StateTransferInfo(Address target, OutputStream os, String state_id) { this.target=target; this.state_id=state_id; this.outputStream=os; } public StateTransferInfo copy() { if(inputStream!=null){ return new StateTransferInfo(target,inputStream,state_id); } else if(outputStream!=null){ return new StateTransferInfo(target,outputStream,state_id); } else{ return new StateTransferInfo(target, state_id, timeout, state); } } public String toString() { StringBuilder ret=new StringBuilder(); ret.append("target=" + target); if(state != null) ret.append(", state=" + state.length + " bytes"); if(state_id != null) ret.append(", state_id=" + state_id); ret.append(", timeout=" + timeout); return ret.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/stack/GossipRouter.java0000644000175000017500000011402511366547366026025 0ustar twernertwerner package org.jgroups.stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Version; import org.jgroups.util.*; import org.jgroups.util.ThreadFactory; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.*; /** * Router for TCP based group comunication (using layer TCP instead of UDP). * Instead of the TCP layer sending packets point-to-point to each other * member, it sends the packet to the router which - depending on the target * address - multicasts or unicasts it to the group / or single member.

* This class is especially interesting for applets which cannot directly make * connections (neither UDP nor TCP) to a host different from the one they were * loaded from. Therefore, an applet would create a normal channel plus * protocol stack, but the bottom layer would have to be the TCP layer which * sends all packets point-to-point (over a TCP connection) to the router, * which in turn forwards them to their end location(s) (also over TCP). A * centralized router would therefore have to be running on the host the applet * was loaded from.

* An alternative for running JGroups in an applet (IP multicast is not allows * in applets as of 1.2), is to use point-to-point UDP communication via the * gossip server. However, then the appplet has to be signed which involves * additional administrative effort on the part of the user.

* @author Bela Ban * @author Ovidiu Feodorov * @version $Id: GossipRouter.java,v 1.26.2.13 2009/09/29 04:36:31 belaban Exp $ * @since 2.1.1 */ public class GossipRouter { public static final byte CONNECT=1; // CONNECT(group, addr) --> local address public static final byte DISCONNECT=2; // DISCONNECT(group, addr) public static final byte REGISTER=3; // REGISTER(group, addr) public static final byte GOSSIP_GET=4; // GET(group) --> List (members) public static final byte ROUTER_GET=5; // GET(group) --> List (members) public static final byte GET_RSP=6; // GET_RSP(List) public static final byte UNREGISTER=7; // UNREGISTER(group, addr) public static final byte DUMP=8; // DUMP public static final byte SHUTDOWN=9; public static final int PORT=12001; public static final long EXPIRY_TIME=30000; public static final long GOSSIP_REQUEST_TIMEOUT=1000; public static final long ROUTING_CLIENT_REPLY_TIMEOUT=120000; private int port; private String bindAddressString; // time (in msecs) until a cached 'gossip' member entry expires private long expiryTime; // number of millisecs the main thread waits to receive a gossip request // after connection was established; upon expiration, the router initiates // the routing protocol on the connection. Don't set the interval too big, // otherwise the router will appear slow in answering routing requests. @Deprecated private long gossipRequestTimeout; // time (in ms) main thread waits for a router client to send the routing // request type and the group afiliation before it declares the request // failed. @Deprecated private long routingClientReplyTimeout; // HashMap >. Maintains associations between groups and their members. Keys=group // names, values = maps of logical address / AddressEntry associations private final ConcurrentMap> routingTable=new ConcurrentHashMap>(); private ServerSocket srvSock=null; private InetAddress bindAddress=null; /** Time (in millis) for setting SO_LINGER on sockets returned from accept(). 0 means don't set SO_LINGER */ private long linger_timeout=2000L; /** Time (in millis) for SO_TIMEOUT on sockets returned from accept(). 0 means don't set SO_TIMEOUT */ private long sock_read_timeout=3000L; /** The max queue size of backlogged connections */ private int backlog=1000; private boolean up=true; private boolean discard_loopbacks=false; protected int thread_pool_min_threads=2; protected int thread_pool_max_threads=10; protected long thread_pool_keep_alive_time=30000; protected boolean thread_pool_enabled=false; protected boolean thread_pool_queue_enabled=true; protected int thread_pool_queue_max_size=50; protected String thread_pool_rejection_policy="Run"; protected ExecutorService thread_pool; protected BlockingQueue thread_pool_queue=null; protected ThreadFactory default_thread_factory = new DefaultThreadFactory(Util.getGlobalThreadGroup(), "gossip-handlers", true, true); // the cache sweeper protected Timer timer=null; protected final Log log=LogFactory.getLog(this.getClass()); // // JMX INSTRUMENTATION - MANAGEMENT INTERFACE // public GossipRouter() { this(PORT); } public GossipRouter(int port) { this(port, null); } public GossipRouter(int port, String bindAddressString) { this(port, bindAddressString, EXPIRY_TIME); } public GossipRouter(int port, String bindAddressString, long expiryTime) { this(port, bindAddressString, expiryTime, GOSSIP_REQUEST_TIMEOUT, ROUTING_CLIENT_REPLY_TIMEOUT); } /** * * Creates a gossip router on a given port bound to a specified interface * and an expiry time (in msecs) until a cached 'gossip' member entry * expires. * *

* Remaining two parameters are deprecated and not used. * * @param port * @param bindAddressString * @param expiryTime * @param gossipRequestTimeout * @param routingClientReplyTimeout * */ public GossipRouter(int port, String bindAddressString, long expiryTime, long gossipRequestTimeout, long routingClientReplyTimeout) { this.port=port; this.bindAddressString=bindAddressString; this.expiryTime=expiryTime; this.gossipRequestTimeout=gossipRequestTimeout; this.routingClientReplyTimeout=routingClientReplyTimeout; } // // MANAGED ATTRIBUTES // public void setPort(int port) { this.port=port; } public int getPort() { return port; } public void setBindAddress(String bindAddress) { bindAddressString=bindAddress; } public String getBindAddress() { return bindAddressString; } public int getBacklog() { return backlog; } public void setBacklog(int backlog) { this.backlog=backlog; } public void setExpiryTime(long expiryTime) { this.expiryTime=expiryTime; } public long getExpiryTime() { return expiryTime; } @Deprecated public void setGossipRequestTimeout(long gossipRequestTimeout) { this.gossipRequestTimeout=gossipRequestTimeout; } @Deprecated public long getGossipRequestTimeout() { return gossipRequestTimeout; } @Deprecated public void setRoutingClientReplyTimeout(long routingClientReplyTimeout) { this.routingClientReplyTimeout=routingClientReplyTimeout; } @Deprecated public long getRoutingClientReplyTimeout() { return routingClientReplyTimeout; } public boolean isStarted() { return srvSock != null; } public boolean isDiscardLoopbacks() { return discard_loopbacks; } public void setDiscardLoopbacks(boolean discard_loopbacks) { this.discard_loopbacks=discard_loopbacks; } public long getLingerTimeout() { return linger_timeout; } public void setLingerTimeout(long linger_timeout) { this.linger_timeout=linger_timeout; } public long getSocketReadTimeout() { return sock_read_timeout; } public void setSocketReadTimeout(long sock_read_timeout) { this.sock_read_timeout=sock_read_timeout; } public ThreadFactory getDefaultThreadPoolThreadFactory() { return default_thread_factory; } public void setDefaultThreadPoolThreadFactory(ThreadFactory factory) { default_thread_factory=factory; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setThreadFactory(factory); } public int getThreadPoolMinThreads() { return thread_pool_min_threads; } public void setThreadPoolMinThreads(int thread_pool_min_threads) { this.thread_pool_min_threads = thread_pool_min_threads; } public int getThreadPoolMaxThreads() { return thread_pool_max_threads; } public void setThreadPoolMaxThreads(int thread_pool_max_threads) { this.thread_pool_max_threads = thread_pool_max_threads; } public long getThreadPoolKeepAliveTime() { return thread_pool_keep_alive_time; } public void setThreadPoolKeepAliveTime(long thread_pool_keep_alive_time) { this.thread_pool_keep_alive_time = thread_pool_keep_alive_time; } public boolean isThreadPoolEnabled() { return thread_pool_enabled; } public void setThreadPoolEnabled(boolean thread_pool_enabled) { this.thread_pool_enabled = thread_pool_enabled; } public boolean isThreadPoolQueueEnabled() { return thread_pool_queue_enabled; } public void setThreadPoolQueueEnabled(boolean thread_pool_queue_enabled) { this.thread_pool_queue_enabled = thread_pool_queue_enabled; } public int getThreadPoolQueueMaxSize() { return thread_pool_queue_max_size; } public void setThreadPoolQueueMaxSize(int thread_pool_queue_max_size) { this.thread_pool_queue_max_size = thread_pool_queue_max_size; } public String getThreadPoolRejectionPolicy() { return thread_pool_rejection_policy; } public void setThreadPoolRejectionPolicy(String thread_pool_rejection_policy) { this.thread_pool_rejection_policy = thread_pool_rejection_policy; } public static String type2String(int type) { switch(type) { case CONNECT: return "CONNECT"; case DISCONNECT: return "DISCONNECT"; case REGISTER: return "REGISTER"; case GOSSIP_GET: return "GOSSIP_GET"; case ROUTER_GET: return "ROUTER_GET"; case GET_RSP: return "GET_RSP"; case UNREGISTER: return "UNREGISTER"; case DUMP: return "DUMP"; case SHUTDOWN: return "SHUTDOWN"; default: return "unknown"; } } // // JBoss MBean LIFECYCLE OPERATIONS // /** * JBoss MBean lifecycle operation. */ public void create() throws Exception { // not used } /** * JBoss MBean lifecycle operation. Called after create(). When this method * is called, the managed attributes have already been set.
* Brings the Router in fully functional state. */ public void start() throws Exception { if(srvSock != null) { throw new Exception("Router already started."); } if(bindAddressString != null) { bindAddress=InetAddress.getByName(bindAddressString); srvSock=new ServerSocket(port, backlog, bindAddress); } else { srvSock=new ServerSocket(port, backlog); } up=true; if (thread_pool_enabled) { if (thread_pool_queue_enabled) { thread_pool_queue = new LinkedBlockingQueue( thread_pool_queue_max_size); } else { thread_pool_queue = new SynchronousQueue(); } thread_pool = createThreadPool(thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time, thread_pool_rejection_policy, thread_pool_queue, default_thread_factory); } else { thread_pool = Executors.newSingleThreadExecutor(default_thread_factory); } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { cleanup(); GossipRouter.this.stop(); } }); // start the main server thread new Thread(new Runnable() { public void run() { try{ mainLoop(); } finally { cleanup(); } } }, "GossipRouter").start(); // starts the cache sweeper as daemon thread, so we won't block on it // upon termination timer=new Timer(true); timer.schedule(new TimerTask() { public void run() { sweep(); } }, expiryTime, expiryTime); } /** * JBoss MBean lifecycle operation. The JMX agent allways calls this method * before destroy(). Close connections and frees resources. */ public void stop() { up=false; thread_pool.shutdownNow(); timer.cancel(); if (srvSock != null) { shutdown(); try { srvSock.close(); } catch (Exception e) { if (log.isErrorEnabled()) log.error("Failed to close server socket: " + e); } // exiting the mainLoop will clean the tables srvSock = null; } if(log.isInfoEnabled()) log.info("router stopped"); } /** * JBoss MBean lifecycle operation. */ public void destroy() { // not used } // // ORDINARY OPERATIONS // public String dumpRoutingTable() { String label="routing"; StringBuilder sb=new StringBuilder(); if(routingTable.isEmpty()) { sb.append("empty ").append(label).append(" table"); } else { for(Iterator i=routingTable.keySet().iterator(); i.hasNext();) { String gname=i.next(); sb.append("GROUP: '" + gname + "'\n"); Map map=routingTable.get(gname); if(map == null) { sb.append("\tnull list of addresses\n"); } else if(map.isEmpty()) { sb.append("\tempty list of addresses\n"); } else { for(Iterator j=map.values().iterator(); j.hasNext();) { sb.append('\t').append(i.next()).append('\n'); } } } } return sb.toString(); } /** * The main server loop. Runs on the JGroups Router Main Thread. */ private void mainLoop() { if (bindAddress == null) { bindAddress = srvSock.getInetAddress(); } printStartupInfo(); while (up && srvSock != null) { try { final Socket sock = srvSock.accept(); if (linger_timeout > 0) { int linger = Math.max(1, (int) (linger_timeout / 1000)); sock.setSoLinger(true, linger); } if (sock_read_timeout > 0) { sock.setSoTimeout((int) sock_read_timeout); } final DataInputStream input = new DataInputStream(sock.getInputStream()); Runnable task = new Runnable(){ public void run() { DataOutputStream output =null; Address peer_addr = null, mbr, logical_addr; String group; GossipData req = new GossipData(); try { req.readFrom(input); switch (req.getType()) { case GossipRouter.REGISTER: mbr = req.getAddress(); group = req.getGroup(); if (log.isTraceEnabled()) log.trace("REGISTER(" + group + ", " + mbr + ")"); if (group == null || mbr == null) { if (log.isErrorEnabled()) log.error("group or member is null, cannot register member"); } else addGossipEntry(group, mbr, new AddressEntry(mbr)); Util.close(input); Util.close(sock); break; case GossipRouter.UNREGISTER: mbr = req.getAddress(); group = req.getGroup(); if (log.isTraceEnabled()) log.trace("UNREGISTER(" + group + ", " + mbr + ")"); if (group == null || mbr == null) { if (log.isErrorEnabled()) log.error("group or member is null, cannot unregister member"); } else removeGossipEntry(group, mbr); Util.close(input); Util.close(output); Util.close(sock); break; case GossipRouter.GOSSIP_GET: group = req.getGroup(); List

mbrs = null; Map map; map = routingTable.get(group); if (map != null) { mbrs = new LinkedList
(map.keySet()); } if (log.isTraceEnabled()) log.trace("GOSSIP_GET(" + group + ") --> " + mbrs); output = new DataOutputStream(sock.getOutputStream()); GossipData rsp = new GossipData(GossipRouter.GET_RSP,group, null, mbrs); rsp.writeTo(output); Util.close(input); Util.close(output); Util.close(sock); break; case GossipRouter.ROUTER_GET: group = req.getGroup(); output = new DataOutputStream(sock.getOutputStream()); List
ret = null; map = routingTable.get(group); if (map != null) { ret = new LinkedList
(map.keySet()); } else ret = new LinkedList
(); if (log.isTraceEnabled()) log.trace("ROUTER_GET(" + group + ") --> " + ret); rsp = new GossipData(GossipRouter.GET_RSP, group, null, ret); rsp.writeTo(output); Util.close(input); Util.close(output); Util.close(sock); break; case GossipRouter.DUMP: output = new DataOutputStream(sock.getOutputStream()); output.writeUTF(dumpRoutingTable()); Util.close(input); Util.close(output); Util.close(sock); break; case GossipRouter.CONNECT: sock.setSoTimeout(0); // we have to disable this here output = new DataOutputStream(sock.getOutputStream()); peer_addr = new IpAddress(sock.getInetAddress(), sock.getPort()); logical_addr = req.getAddress(); String group_name = req.getGroup(); if (log.isTraceEnabled()) log.trace("CONNECT(" + group_name + ", "+ logical_addr + ")"); SocketThread st = new SocketThread(sock, input,group_name, logical_addr); addEntry(group_name, logical_addr, new AddressEntry(logical_addr, peer_addr, sock, st, output)); st.start(); break; case GossipRouter.DISCONNECT: Address addr = req.getAddress(); group_name = req.getGroup(); removeEntry(group_name, addr); if (log.isTraceEnabled()) log.trace("DISCONNECT(" + group_name + ", " + addr+ ")"); Util.close(input); Util.close(output); Util.close(sock); break; case GossipRouter.SHUTDOWN: if (log.isInfoEnabled()) log.info("router shutting down"); Util.close(input); Util.close(output); Util.close(sock); up = false; break; default: if (log.isWarnEnabled()) log.warn("received unkown gossip request (gossip="+ req + ')'); break; } } catch (Exception e) { if (up) if (log.isErrorEnabled()) log.error("failure handling a client request", e); Util.close(input); Util.close(output); Util.close(sock); } }}; if(!thread_pool.isShutdown()) thread_pool.execute(task); else task.run(); } catch (Exception exc) { if (log.isErrorEnabled()) log.error("failure receiving and setting up a client request", exc); } } } protected ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time, String rejection_policy, BlockingQueue queue, final ThreadFactory factory) { ThreadPoolExecutor pool = new ThreadManagerThreadPoolExecutor( min_threads, max_threads, keep_alive_time, TimeUnit.MILLISECONDS, queue); pool.setThreadFactory(factory); // default RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); if (rejection_policy != null) { if (rejection_policy.equals("abort")) handler = new ThreadPoolExecutor.AbortPolicy(); else if (rejection_policy.equals("discard")) handler = new ThreadPoolExecutor.DiscardPolicy(); else if (rejection_policy.equals("discardoldest")) handler = new ThreadPoolExecutor.DiscardOldestPolicy(); } pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler( handler)); return pool; } /** * Cleans the routing tables while the Router is going down. */ private void cleanup() { // shutdown the routing threads and cleanup the tables for(Map map: routingTable.values()) { if(map != null) { for(AddressEntry entry: map.values()) { entry.destroy(); } } } routingTable.clear(); } /** * Connects to the ServerSocket and sends the shutdown header. */ private void shutdown() { Socket s=null; DataOutputStream dos=null; try { s=new Socket(srvSock.getInetAddress(),srvSock.getLocalPort()); dos=new DataOutputStream(s.getOutputStream()); dos.writeInt(SHUTDOWN); dos.writeUTF(""); } catch(Exception e) { if(log.isErrorEnabled()) log.error("shutdown failed: " + e); } finally { Util.close(s); Util.close(dos); } } /** * Removes expired gossip entries (entries older than EXPIRY_TIME msec). * @since 2.2.1 */ private void sweep() { long diff, currentTime=System.currentTimeMillis(); int num_entries_removed=0; for(Iterator>> it=routingTable.entrySet().iterator(); it.hasNext();) { Entry > entry=it.next(); Map map=entry.getValue(); if(map == null || map.isEmpty()) { it.remove(); continue; } for(Iterator> it2=map.entrySet().iterator(); it2.hasNext();) { Entryentry2=it2.next(); AddressEntry ae=entry2.getValue(); diff=currentTime - ae.timestamp; if(diff > expiryTime) { it2.remove(); if(log.isTraceEnabled()) log.trace("removed " + ae.logical_addr + " (" + diff + " msecs old)"); num_entries_removed++; } } } if(num_entries_removed > 0) { if(log.isTraceEnabled()) log.trace("done (removed " + num_entries_removed + " entries)"); } } private void route(Address dest, String dest_group, byte[] msg, Address sender) { //if(log.isTraceEnabled()) { // int len=msg != null? msg.length : 0; //log.trace("routing request from " + sender + " for " + dest_group + " to " + // (dest == null? "ALL" : dest.toString()) + ", " + len + " bytes"); //} if(dest == null) { // send to all members in group dest.getChannelName() if(dest_group == null) { if(log.isErrorEnabled()) log.error("both dest address and group are null"); } else { sendToAllMembersInGroup(dest_group, msg, sender); } } else { // send to destination address AddressEntry ae=findAddressEntry(dest_group, dest); if(ae == null) { if(log.isTraceEnabled()) log.trace("cannot find " + dest + " in the routing table, \nrouting table=\n" + dumpRoutingTable()); return; } if(ae.output == null) { if(log.isErrorEnabled()) log.error(dest + " is associated with a null output stream"); return; } try { sendToMember(dest, ae.output, msg, sender); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending message to " + dest + ": " + e.getMessage()); removeEntry(dest_group, dest); // will close socket } } } private void addEntry(String groupname, Address logical_addr, AddressEntry entry) { addEntry(groupname, logical_addr, entry, false); } /** * Adds a new member to the routing group. */ private void addEntry(String groupname, Address logical_addr, AddressEntry entry, boolean update_only) { if(groupname == null || logical_addr == null) { if(log.isErrorEnabled()) log.error("groupname or logical_addr was null, entry was not added"); return; } ConcurrentMap mbrs=routingTable.get(groupname); if(mbrs == null) { mbrs=new ConcurrentHashMap(); mbrs.put(logical_addr, entry); routingTable.putIfAbsent(groupname, mbrs); } else { AddressEntry tmp=mbrs.get(logical_addr); if(tmp != null) { // already present if(update_only) { tmp.update(); return; } tmp.destroy(); } mbrs.put(logical_addr, entry); } } private void removeEntry(String groupname, Address logical_addr) { Mapval=routingTable.get(groupname); if(val == null) return; synchronized(val) { AddressEntry entry=val.get(logical_addr); if(entry != null) { entry.destroy(); val.remove(logical_addr); } } } /** * @return null if not found */ private AddressEntry findAddressEntry(String group_name, Address logical_addr) { if(group_name == null || logical_addr == null) return null; Map val=routingTable.get(group_name); if(val == null) return null; return (AddressEntry)val.get(logical_addr); } /** * Adds a new member to the group in the gossip table or renews the * membership where is the case. * @since 2.2.1 */ private void addGossipEntry(String groupname, Address logical_addr, AddressEntry e) { addEntry(groupname, logical_addr, e, true); } private void removeGossipEntry(String groupname, Address mbr) { removeEntry(groupname, mbr); } private void sendToAllMembersInGroup(String groupname, byte[] msg, Address sender) { Map val = routingTable.get(groupname); if(val == null || val.isEmpty()) return; synchronized(val) { for(Iterator> i=val.entrySet().iterator(); i.hasNext();) { Entry tmp=i.next(); AddressEntry entry=tmp.getValue(); DataOutputStream dos=entry.output; if(dos != null) { // send only to 'connected' members try { sendToMember(null, dos, msg, sender); } catch(Exception e) { if(log.isWarnEnabled()) log.warn("cannot send to " + entry.logical_addr + ": " + e.getMessage()); entry.destroy(); // this closes the socket i.remove(); } } } } } /** * @throws IOException */ private void sendToMember(Address dest, DataOutputStream out, byte[] msg, Address sender) throws IOException { if(out == null) return; if(discard_loopbacks && dest != null && dest.equals(sender)) { return; } synchronized(out) { Util.writeAddress(dest, out); out.writeInt(msg.length); out.write(msg, 0, msg.length); } } /** * Prints startup information. */ private void printStartupInfo() { System.out.println("GossipRouter started at " + new Date()); System.out.print("Listening on port " + port); System.out.println(" bound on address " + bindAddress); System.out.print("Backlog is " + backlog); System.out.print(", linger timeout is " + linger_timeout); System.out.println(", and read timeout is " + sock_read_timeout); } /** * Class used to store Addresses in both routing and gossip tables. * If it is used for routing, sock and output have valid values, otherwise * they're null and only the timestamp counts. */ class AddressEntry { Address logical_addr=null, physical_addr=null; Socket sock=null; DataOutputStream output=null; long timestamp=0; final SocketThread thread; /** * AddressEntry for a 'gossip' membership. */ public AddressEntry(Address addr) { this(addr, null, null, null, null); } public AddressEntry(Address logical_addr, Address physical_addr, Socket sock, SocketThread thread, DataOutputStream output) { this.logical_addr=logical_addr; this.physical_addr=physical_addr; this.sock=sock; this.thread=thread; this.output=output; this.timestamp=System.currentTimeMillis(); } void destroy() { if(thread != null) { thread.finish(); } Util.close(output); output=null; Util.close(sock); sock=null; timestamp=0; } public void update() { timestamp=System.currentTimeMillis(); } public boolean equals(Object other) { return other instanceof AddressEntry && logical_addr.equals(((AddressEntry)other).logical_addr); } public String toString() { StringBuilder sb=new StringBuilder("logical addr="); sb.append(logical_addr).append(" (").append(physical_addr).append(")"); //if(sock != null) { // sb.append(", sock="); //sb.append(sock); //} if(timestamp > 0) { long diff=System.currentTimeMillis() - timestamp; sb.append(", ").append(diff).append(" ms old"); } return sb.toString(); } } private static int threadCounter=0; /** * A SocketThread manages one connection to a client. Its main task is message routing. */ class SocketThread extends Thread { private volatile boolean active=true; Socket sock=null; DataInputStream input=null; Address logical_addr=null; String group_name=null; public SocketThread(Socket sock, DataInputStream ois, String group_name, Address logical_addr) { super(Util.getGlobalThreadGroup(), "SocketThread " + (threadCounter++)); this.sock=sock; input=ois; this.group_name=group_name; this.logical_addr=logical_addr; } void closeSocket() { Util.close(input); Util.close(sock); } void finish() { active=false; } public void run() { byte[] buf; int len; Address dst_addr=null; String gname; DataOutputStream output = null; try { output=new DataOutputStream(sock.getOutputStream()); output.writeBoolean(true); } catch(IOException e1) { try { output.writeBoolean(false); } catch(IOException e) {} } while(active) { try { // 1. Group name is first gname=input.readUTF(); // 2. Second is the destination address dst_addr=Util.readAddress(input); // 3. Then the length of the byte buffer representing the message len=input.readInt(); if(len == 0) { if(log.isWarnEnabled()) log.warn("received null message"); continue; } // 4. Finally the message itself, as a byte buffer buf=new byte[len]; input.readFully(buf, 0, buf.length); // message } catch(Exception io_ex) { if(log.isTraceEnabled()) log.trace(sock.getInetAddress().getHostName() + ':' + sock.getPort() + " closed connection; removing it from routing table"); removeEntry(group_name, logical_addr); // will close socket return; } try { route(dst_addr, gname, buf, logical_addr); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed routing request to " + dst_addr, e); break; } } closeSocket(); } } public static void main(String[] args) throws Exception { String arg; int port=12001; long expiry=GossipRouter.EXPIRY_TIME; long timeout=GossipRouter.GOSSIP_REQUEST_TIMEOUT; long routingTimeout=GossipRouter.ROUTING_CLIENT_REPLY_TIMEOUT; int backlog = 0; long soLinger = -1; long soTimeout = -1; GossipRouter router=null; String bind_addr=null; for(int i=0; i < args.length; i++) { arg=args[i]; if("-port".equals(arg)) { port=Integer.parseInt(args[++i]); continue; } if("-bindaddress".equals(arg) || "-bind_addr".equals(arg)) { bind_addr=args[++i]; continue; } if ("-backlog".equals(arg)) { backlog=Integer.parseInt(args[++i]); continue; } if("-expiry".equals(arg)) { expiry=Long.parseLong(args[++i]); continue; } // this option is not used and should be deprecated/removed // in a future release if("-timeout".equals(arg)) { System.out.println(" -timeout is depracted and will be ignored"); ++i; continue; } // this option is not used and should be deprecated/removed // in a future release if("-rtimeout".equals(arg)) { System.out.println(" -rtimeout is depracted and will be ignored"); ++i; continue; } if ("-solinger".equals(arg)) { soLinger=Long.parseLong(args[++i]); continue; } if ("-sotimeout".equals(arg)) { soTimeout=Long.parseLong(args[++i]); continue; } help(); return; } System.out.println("GossipRouter is starting (JGroups version: " + Version.printVersion() + ")"); try { router=new GossipRouter(port, bind_addr, expiry); if (backlog > 0) router.setBacklog(backlog); if (soTimeout >= 0) router.setSocketReadTimeout(soTimeout); if (soLinger >= 0) router.setLingerTimeout(soLinger); router.start(); } catch(Exception e) { System.err.println(e); } } static void help() { System.out.println(); System.out.println("GossipRouter [-port ] [-bind_addr
] [options]"); System.out.println(); System.out.println("Options:"); System.out.println(); // -timeout isn't used and should be deprecated/removed // in a future release. It's left in this code for backwards // compatability, but it is no longer advertized. //System.out.println(" -timeout - Number of millisecs the router waits to receive"); //System.out.println(" a gossip request after connection was established;"); //System.out.println(" upon expiration, the router initiates the routing"); //System.out.println(" protocol on the connection."); System.out.println(" -backlog - Max queue size of backlogged connections. Must be"); System.out.println(" greater than zero or the default of 1000 will be"); System.out.println(" used."); System.out.println(); System.out.println(" -expiry - Time until a gossip cache entry expires. 30000 is"); System.out.println(" the default value."); System.out.println(); System.out.println(" -solinger - Time for setting SO_LINGER on connections. 0"); System.out.println(" means do not set SO_LINGER. Must be greater than"); System.out.println(" or equal to zero or the default of 2000 will be"); System.out.println(" used."); System.out.println(); System.out.println(" -sotimeout - Time for setting SO_TIMEOUT on connections. 0"); System.out.println(" means don't set SO_TIMEOUT. Must be greater than"); System.out.println(" or equal to zero or the default of 3000 will be"); System.out.println(" used."); System.out.println(); } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/UpHandler.java0000644000175000017500000000047111366547366024134 0ustar twernertwerner// $Id: UpHandler.java,v 1.4 2007/01/11 12:57:28 belaban Exp $ package org.jgroups; /** * Provides a way of taking over a channel's tasks. */ public interface UpHandler { /** * Invoked for all channel events except connection management and state transfer. * @param evt */ Object up(Event evt); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/annotations/0000755000175000017500000000000011621261110023710 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/annotations/GuardedBy.java0000644000175000017500000000041711621261110026423 0ustar twernertwerner/* simple DFSG free reimplementation of the CC licensed original */ package org.jgroups.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.SOURCE) public @interface GuardedBy { String value(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/annotations/Immutable.java0000644000175000017500000000037711621261110026501 0ustar twernertwerner/* simple DFSG free reimplementation of the CC licensed original */ package org.jgroups.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.SOURCE) public @interface Immutable { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ExtendedReceiver.java0000644000175000017500000000055611366547366025503 0ustar twernertwernerpackage org.jgroups; /** * Extends Receiver, plus the partial state transfer methods. * This interface will disappear (be merged with Receiver) in 3.0. * @author Bela Ban * @version $Id: ExtendedReceiver.java,v 1.2 2006/09/27 12:39:14 belaban Exp $ */ public interface ExtendedReceiver extends Receiver, ExtendedMessageListener, ExtendedMembershipListener { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/0000755000175000017500000000000011621261110022300 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/ProtocolParameter.java0000644000175000017500000000313511366547366026641 0ustar twernertwerner package org.jgroups.conf; /** * Data holder for protocol data * * @author Filip Hanik (filip@filip.net) * @author Bela Ban * @version $Id: ProtocolParameter.java,v 1.5.6.1 2008/01/22 10:01:25 belaban Exp $ */ public class ProtocolParameter { private final String mParameterName; private String mParameterValue; public ProtocolParameter(String parameterName, String parameterValue) { mParameterName=parameterName; mParameterValue=parameterValue; } public String getName() { return mParameterName; } public String getValue() { return mParameterValue; } public void setValue(String replacement) { mParameterValue=replacement; } public int hashCode() { if(mParameterName != null) return mParameterName.hashCode(); else return -1; } public boolean equals(Object another) { if(another instanceof ProtocolParameter) return getName().equals(((ProtocolParameter)another).getName()); else return false; } public String getParameterString() { StringBuilder buf=new StringBuilder(mParameterName); if(mParameterValue != null) buf.append('=').append(mParameterValue); return buf.toString(); } public String getParameterStringXml() { StringBuilder buf=new StringBuilder(mParameterName); if(mParameterValue != null) buf.append("=\"").append(mParameterValue).append('\"'); return buf.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/ConfiguratorFactory.java0000644000175000017500000004447611366547366027206 0ustar twernertwerner package org.jgroups.conf; import org.w3c.dom.Element; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.JChannel; import org.jgroups.util.Util; import java.io.*; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; import java.util.HashMap; import java.util.Map; import java.util.Iterator; import java.security.AccessControlException; /** * The ConfigurationFactory is a factory that returns a protocol stack configurator. * The protocol stack configurator is an object that read a stack configuration and * parses it so that the ProtocolStack can create a stack. *
* Currently the factory returns one of the following objects:
* 1. XmlConfigurator - parses XML files
* 2. PlainConfigurator - uses the old style strings UDP:FRAG: etc etc
* * @author Filip Hanik (
filip@filip.net) * @author Bela Ban * @version $Id: ConfiguratorFactory.java,v 1.23 2007/06/06 11:02:35 belaban Exp $ */ public class ConfiguratorFactory { public static final String JAXP_MISSING_ERROR_MSG= "JAXP Error: the required XML parsing classes are not available; " + "make sure that JAXP compatible libraries are in the classpath."; static final String FORCE_CONFIGURATION="force.properties"; static final Log log=LogFactory.getLog(ConfiguratorFactory.class); static String propertiesOverride=null; // Check for the presence of the system property "force.properties", and // act appropriately if it is set. We only need to do this once since the // system properties are highly unlikely to change. static { try { Properties properties = System.getProperties(); propertiesOverride = properties.getProperty(FORCE_CONFIGURATION); } catch (SecurityException e) { propertiesOverride = null; } if(propertiesOverride != null && log.isDebugEnabled()) { log.debug("using properties override: " + propertiesOverride); } } protected ConfiguratorFactory() { } /** * Returns a protocol stack configurator based on the XML configuration * provided by the specified File. * * @param file a File with a JGroups XML configuration. * * @return a ProtocolStackConfigurator containing the stack * configuration. * * @throws ChannelException if problems occur during the configuration of * the protocol stack. */ public static ProtocolStackConfigurator getStackConfigurator(File file) throws ChannelException { ProtocolStackConfigurator returnValue; if (propertiesOverride != null) { returnValue = getStackConfigurator(propertiesOverride); } else { try { checkJAXPAvailability(); InputStream input=getConfigStream(file); returnValue=XmlConfigurator.getInstance(input); } catch(Exception ex) { throw createChannelConfigurationException(ex); } } return returnValue; } /** * Returns a protocol stack configurator based on the XML configuration * provided at the specified URL. * * @param url a URL pointing to a JGroups XML configuration. * * @return a ProtocolStackConfigurator containing the stack * configuration. * * @throws ChannelException if problems occur during the configuration of * the protocol stack. */ public static ProtocolStackConfigurator getStackConfigurator(URL url) throws ChannelException { ProtocolStackConfigurator returnValue; if (propertiesOverride != null) { returnValue = getStackConfigurator(propertiesOverride); } else { checkForNullConfiguration(url); checkJAXPAvailability(); try { returnValue=XmlConfigurator.getInstance(url); } catch (IOException ioe) { throw createChannelConfigurationException(ioe); } } return returnValue; } /** * Returns a protocol stack configurator based on the XML configuration * provided by the specified XML element. * * @param element a XML element containing a JGroups XML configuration. * * @return a ProtocolStackConfigurator containing the stack * configuration. * * @throws ChannelException if problems occur during the configuration of * the protocol stack. */ public static ProtocolStackConfigurator getStackConfigurator(Element element) throws ChannelException { ProtocolStackConfigurator returnValue; if (propertiesOverride != null) { returnValue = getStackConfigurator(propertiesOverride); } else { checkForNullConfiguration(element); // Since Element is a part of the JAXP specification and because an // Element instance already exists, there is no need to check for // JAXP availability. // // checkJAXPAvailability(); try { returnValue=XmlConfigurator.getInstance(element); } catch (IOException ioe) { throw createChannelConfigurationException(ioe); } } return returnValue; } /** * Returns a protocol stack configurator based on the provided properties * string. * * @param properties an old style property string, a string representing a * system resource containing a JGroups XML configuration, * a string representing a URL pointing to a JGroups XML * XML configuration, or a string representing a file name * that contains a JGroups XML configuration. */ public static ProtocolStackConfigurator getStackConfigurator(String properties) throws ChannelException { if (propertiesOverride != null) { properties = propertiesOverride; } // added by bela: for null String props we use the default properties if(properties == null) properties=JChannel.DEFAULT_PROTOCOL_STACK; checkForNullConfiguration(properties); ProtocolStackConfigurator returnValue; // Attempt to treat the properties string as a pointer to an XML // configuration. XmlConfigurator configurator = null; try { configurator=getXmlConfigurator(properties); } catch (IOException ioe) { throw createChannelConfigurationException(ioe); } // Did the properties string point to a JGroups XML configuration? if (configurator != null) { returnValue=configurator; } else { // Attempt to process the properties string as the old style // property string. returnValue=new PlainConfigurator(properties); } return returnValue; } /** * Returns a protocol stack configurator based on the properties passed in.
* If the properties parameter is a plain string UDP:FRAG:MERGE:GMS etc, a PlainConfigurator is returned.
* If the properties parameter is a string that represents a url for example http://www.filip.net/test.xml * or the parameter is a java.net.URL object, an XmlConfigurator is returned
* * @param properties old style property string, url string, or java.net.URL object * @return a ProtocolStackConfigurator containing the stack configuration * @throws IOException if it fails to parse the XML content * @throws IOException if the URL is invalid or a the content can not be reached * @deprecated Used by the JChannel(Object) constructor which has been deprecated. */ public static ProtocolStackConfigurator getStackConfigurator(Object properties) throws IOException { InputStream input=null; if (propertiesOverride != null) { properties = propertiesOverride; } // added by bela: for null String props we use the default properties if(properties == null) properties=JChannel.DEFAULT_PROTOCOL_STACK; if(properties instanceof URL) { try { input=((URL)properties).openStream(); } catch(Throwable t) { } } // if it is a string, then it could be a plain string or a url if(input == null && properties instanceof String) { try { input=new URL((String)properties).openStream(); } catch(Exception ignore) { // if we get here this means we don't have a URL } // another try - maybe it is a resource, e.g. udp.xml if(input == null && ((String)properties).endsWith("xml")) { try { input=Util.getResourceAsStream((String)properties, ConfiguratorFactory.class); } catch(Throwable ignore) { } } // try a regular file name // // This code was moved from the parent block (below) because of the // possibility of causing a ClassCastException. if(input == null) { try { input=new FileInputStream((String)properties); } catch(Throwable t) { } } } // try a regular file if(input == null && properties instanceof File) { try { input=new FileInputStream((File)properties); } catch(Throwable t) { } } if(input != null) { return XmlConfigurator.getInstance(input); } if(properties instanceof Element) { return XmlConfigurator.getInstance((Element)properties); } return new PlainConfigurator((String)properties); } public static InputStream getConfigStream(File file) throws Exception { if(propertiesOverride != null) return getConfigStream(propertiesOverride); checkForNullConfiguration(file); try { return new FileInputStream(file); } catch(IOException ioe) { throw createChannelConfigurationException(ioe); } } public static InputStream getConfigStream(URL url) throws Exception { if (propertiesOverride != null) return getConfigStream(propertiesOverride); try { checkJAXPAvailability(); return url.openStream(); } catch(Exception ex) { throw createChannelConfigurationException(ex); } } /** * Returns a JGroups XML configuration InputStream based on the provided * properties string. * * @param properties a string representing a system resource containing a * JGroups XML configuration, a string representing a URL * pointing to a JGroups ML configuration, or a string * representing a file name that contains a JGroups XML * configuration. * * @throws IOException if the provided properties string appears to be a * valid URL but is unreachable. */ public static InputStream getConfigStream(String properties) throws IOException { InputStream configStream = null; if (propertiesOverride != null) return getConfigStream(propertiesOverride); // Check to see if the properties string is the name of a file. try { configStream=new FileInputStream(properties); } catch(FileNotFoundException fnfe) { // the properties string is likely not a file } catch(AccessControlException access_ex) { // fixes http://jira.jboss.com/jira/browse/JGRP-94 } // Check to see if the properties string is a URL. if(configStream == null) { try { configStream=new URL(properties).openStream(); } catch (MalformedURLException mre) { // the properties string is not a URL } } // Commented so the caller is notified of this condition, but left in // the code for documentation purposes. // // catch (IOException ioe) { // the specified URL string was not reachable // } // Check to see if the properties string is the name of a resource, // e.g. udp.xml. if(configStream == null && properties.endsWith("xml")) { configStream=Util.getResourceAsStream(properties, ConfiguratorFactory.class); } return configStream; } public static InputStream getConfigStream(Object properties) throws IOException { InputStream input=null; if (propertiesOverride != null) return getConfigStream(propertiesOverride); // added by bela: for null String props we use the default properties if(properties == null) properties=JChannel.DEFAULT_PROTOCOL_STACK; if(properties instanceof URL) { try { input=((URL)properties).openStream(); } catch(Throwable t) { } } // if it is a string, then it could be a plain string or a url if(input == null && properties instanceof String) { input=getConfigStream((String)properties); } // try a regular file if(input == null && properties instanceof File) { try { input=new FileInputStream((File)properties); } catch(Throwable t) { } } if(input != null) return input; if(properties instanceof Element) { return getConfigStream((Element)properties); } return new ByteArrayInputStream(((String)properties).getBytes()); } /** * Returns an XmlConfigurator based on the provided properties string (if * possible). * * @param properties a string representing a system resource containing a * JGroups XML configuration, a string representing a URL * pointing to a JGroups ML configuration, or a string * representing a file name that contains a JGroups XML * configuration. * * @return an XmlConfigurator instance based on the provided properties * string; null if the provided properties string does * not point to an XML configuration. * * @throws IOException if the provided properties string appears to be a * valid URL but is unreachable, or if the JGroups XML * configuration pointed to by the URL can not be * parsed. */ static XmlConfigurator getXmlConfigurator(String properties) throws IOException { XmlConfigurator returnValue=null; InputStream configStream=getConfigStream(properties); if (configStream != null) { checkJAXPAvailability(); returnValue=XmlConfigurator.getInstance(configStream); } return returnValue; } /** * Creates a ChannelException instance based upon a * configuration problem. * * @param cause the exceptional configuration condition to be used as the * created ChannelException's cause. */ static ChannelException createChannelConfigurationException(Throwable cause) { return new ChannelException("unable to load the protocol stack", cause); } /** * Check to see if the specified configuration properties are * null which is not allowed. * * @param properties the specified protocol stack configuration. * * @throws NullPointerException if the specified configuration properties * are null. */ static void checkForNullConfiguration(Object properties) { if(properties == null) throw new NullPointerException("the specifed protocol stack configuration was null"); } /** * Checks the availability of the JAXP classes on the classpath. * * @throws NoClassDefFoundError if the required JAXP classes are not * availabile on the classpath. */ static void checkJAXPAvailability() { try { // TODO: Do some real class checking here instead of forcing the // load of a JGroups class that happens (by default) to do it // for us. XmlConfigurator.class.getName(); } catch (NoClassDefFoundError error) { Error tmp=new NoClassDefFoundError(JAXP_MISSING_ERROR_MSG); tmp.initCause(error); throw tmp; } } /** Replace variables of the form ${var:default} with the getProperty(var, default) * @param configurator */ public static void substituteVariables(ProtocolStackConfigurator configurator) { ProtocolData[] protocols; try { protocols=configurator.getProtocolStack(); } catch(Exception e) { protocols=null; } if(protocols == null) return; for(int i=0; i < protocols.length; i++) { ProtocolData protocol=protocols[i]; if(protocol != null) { HashMap parms=protocol.getParameters(); Map.Entry entry; ProtocolParameter parm; for(Iterator it=parms.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); parm=(ProtocolParameter)entry.getValue(); String val=parm.getValue(); String replacement=Util.substituteVariable(val); if(!replacement.equals(val)) { parm.setValue(replacement); } } } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/ProtocolData.java0000644000175000017500000000714011366547366025572 0ustar twernertwerner package org.jgroups.conf; /** * Data holder for protocol * @author Filip Hanik (
filip@filip.net) * @version $Id: ProtocolData.java,v 1.7.6.1 2008/01/22 10:01:25 belaban Exp $ */ import java.util.HashMap; import java.util.Iterator; public class ProtocolData { /** Map of property keys and values */ private final HashMap mParameters=new HashMap(); private final String mProtocolName; private final String mDescription; private final String mClassName; private boolean mIsOverRide=false; public ProtocolData(String protocolName, String description, String className, ProtocolParameter[] params) { mProtocolName=protocolName; mDescription=description; mClassName=className; if(params != null) { for(int i=0; i < params.length; i++) { mParameters.put(params[i].getName(), params[i]); } } } public ProtocolData(String overRideName, ProtocolParameter[] params) { this(overRideName, null, null, params); mIsOverRide=true; } public String getClassName() { return mClassName; } public String getProtocolName() { return mProtocolName; } public String getDescription() { return mDescription; } public HashMap getParameters() { return mParameters; } public boolean isOverride() { return mIsOverRide; } public ProtocolParameter[] getParametersAsArray() { ProtocolParameter[] result=new ProtocolParameter[mParameters.size()]; Iterator it=mParameters.keySet().iterator(); for(int i=0; i < result.length; i++) { String key=(String)it.next(); result[i]=(ProtocolParameter)mParameters.get(key); } return result; } public void override(ProtocolParameter[] params) { for(int i=0; i < params.length; i++) mParameters.put(params[i].getName(), params[i]); } public String getProtocolString(boolean new_format) { return new_format? getProtocolStringNewXml() : getProtocolString(); } public String getProtocolString() { StringBuilder buf=new StringBuilder(mClassName); if(mParameters.size() > 0) { buf.append('('); Iterator i=mParameters.keySet().iterator(); while(i.hasNext()) { String key=(String)i.next(); ProtocolParameter param=(ProtocolParameter)mParameters.get(key); buf.append(param.getParameterString()); if(i.hasNext()) buf.append(';'); }//while buf.append(')'); } return buf.toString(); } public String getProtocolStringNewXml() { StringBuilder buf=new StringBuilder(mClassName + ' '); if(mParameters.size() > 0) { Iterator i=mParameters.keySet().iterator(); while(i.hasNext()) { String key=(String)i.next(); ProtocolParameter param=(ProtocolParameter)mParameters.get(key); buf.append(param.getParameterStringXml()); if(i.hasNext()) buf.append(' '); } } return buf.toString(); } public int hashCode() { return mProtocolName.hashCode(); } public boolean equals(Object another) { if(another instanceof ProtocolData) return getProtocolName().equals(((ProtocolData)another).getProtocolName()); else return false; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/package.html0000644000175000017500000000011611366547366024611 0ustar twernertwerner Provides ways to configure a protocol stack. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/ClassConfigurator.java0000644000175000017500000002135411366547366026632 0ustar twernertwerner// $Id: ClassConfigurator.java,v 1.21.4.2 2008/05/22 13:23:05 belaban Exp $ package org.jgroups.conf; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.Global; import org.jgroups.util.Util; import java.io.ObjectStreamClass; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * This class will be replaced with the class that read info * from the magic number configurator that reads info from the xml file. * The name and the relative path of the magic number map file can be specified * as value of the property org.jgroups.conf.magicNumberFile. * It must be relative to one of the classpath elements, to allow the * classloader to locate the file. If a value is not specified, * MagicNumberReader.MAGIC_NUMBER_FILE is used, which defaults * to "jg-magic-map.xml". * * @author Filip Hanik * @author Bela Ban * @see MagicNumberReader */ public class ClassConfigurator { static volatile ClassConfigurator instance=null; // works under the new JSR 133 memory model in JDK 5 private static final short MIN_CUSTOM_MAGIC_NUMBER=1024; //this is where we store magic numbers private final Map classMap=new ConcurrentHashMap(); // key=Class, value=magic number private final Map magicMap=new ConcurrentHashMap(); // key=magic number, value=Class /** Map */ private final Map streamMapId=new HashMap(); /** Map */ private final Map streamMapClass=new HashMap(); protected final Log log=LogFactory.getLog(getClass()); public ClassConfigurator() { } public void init() throws ChannelException { //populate the map try { // make sure we have a class for DocumentBuilderFactory // getClass().getClassLoader().loadClass("javax.xml.parsers.DocumentBuilderFactory"); Util.loadClass("javax.xml.parsers.DocumentBuilderFactory", this.getClass()); MagicNumberReader reader=new MagicNumberReader(); // PropertyPermission not granted if running in an untrusted environment with JNLP. try { String mnfile=Util.getProperty(new String[]{Global.MAGIC_NUMBER_FILE, "org.jgroups.conf.magicNumberFile"}, null, null, false, null); if(mnfile != null) { if(log.isDebugEnabled()) log.debug("Using " + mnfile + " as magic number file"); reader.setFilename(mnfile); } } catch (SecurityException ex){ } ObjectStreamClass objStreamClass; ClassMap[] mapping=reader.readMagicNumberMapping(); if(mapping != null) { Short m; for(int i=0; i < mapping.length; i++) { m=new Short(mapping[i].getMagicNumber()); try { Class clazz=mapping[i].getClassForMap(); objStreamClass=ObjectStreamClass.lookup(clazz); if(objStreamClass == null) throw new ChannelException("ObjectStreamClass for " + clazz + " not found"); if(magicMap.containsKey(m)) { throw new ChannelException("magic key " + m + " (" + clazz.getName() + ')' + " is already in map; please make sure that " + "all magic keys are unique"); } else { magicMap.put(m, clazz); classMap.put(clazz, m); streamMapId.put(m, objStreamClass); streamMapClass.put(objStreamClass, m); } } catch(ClassNotFoundException cnf) { throw new ChannelException("failed loading class", cnf); } } if(log.isDebugEnabled()) log.debug("mapping is:\n" + printMagicMap()); } } catch(ChannelException ex) { throw ex; } catch(Throwable x) { // if(log.isErrorEnabled()) log.error("failed reading the magic number mapping file, reason: " + Util.print(x)); throw new ChannelException("failed reading the magic number mapping file", x); } } public static ClassConfigurator getInstance(boolean init) throws ChannelException { if(instance == null) { instance=new ClassConfigurator(); if(init) instance.init(); } return instance; } public static ClassConfigurator getInstance() throws ChannelException { return getInstance(false); } /** * Method to register a user-defined header with jg-magic-map at runtime * @param magic The magic number. Needs to be > 1024 * @param clazz The class. Usually a subclass of Header * @throws IllegalArgumentException If the magic number is already taken, or the magic number is <= 1024 */ public void add(short magic, Class clazz) throws IllegalArgumentException { if(magic <= MIN_CUSTOM_MAGIC_NUMBER) throw new IllegalArgumentException("magic number (" + magic + ") needs to be greater than " + MIN_CUSTOM_MAGIC_NUMBER); if(magicMap.containsKey(magic) || classMap.containsKey(clazz)) throw new IllegalArgumentException("magic number " + magic + " for class " + clazz.getName() + " is already present"); magicMap.put(magic, clazz); classMap.put(clazz, magic); } /** * Returns a class for a magic number. * Returns null if no class is found * * @param magic the magic number that maps to the class * @return a Class object that represents a class that implements java.io.Externalizable */ public Class get(short magic) { return magicMap.get(magic); } /** * Loads and returns the class from the class name * * @param clazzname a fully classified class name to be loaded * @return a Class object that represents a class that implements java.io.Externalizable */ public Class get(String clazzname) { try { // return ClassConfigurator.class.getClassLoader().loadClass(clazzname); return Util.loadClass(clazzname, this.getClass()); } catch(Exception x) { if(log.isErrorEnabled()) log.error("failed loading class " + clazzname, x); } return null; } /** * Returns the magic number for the class. * * @param clazz a class object that we want the magic number for * @return the magic number for a class, -1 if no mapping is available */ public short getMagicNumber(Class clazz) { Short i=classMap.get(clazz); if(i == null) return -1; else return i; } public short getMagicNumberFromObjectStreamClass(ObjectStreamClass objStream) { Short i=streamMapClass.get(objStream); if(i == null) return -1; else return i.shortValue(); } public ObjectStreamClass getObjectStreamClassFromMagicNumber(short magic_number) { ObjectStreamClass retval=null; retval=streamMapId.get(magic_number); return retval; } public String toString() { return printMagicMap(); } public String printMagicMap() { StringBuilder sb=new StringBuilder(); SortedSet keys=new TreeSet(magicMap.keySet()); for(Short key: keys) { sb.append(key).append(":\t").append(magicMap.get(key)).append('\n'); } return sb.toString(); } public String printClassMap() { StringBuilder sb=new StringBuilder(); Map.Entry entry; for(Iterator it=classMap.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); } return sb.toString(); } /* --------------------------------- Private methods ------------------------------------ */ /* ------------------------------ End of Pivate methods --------------------------------- */ public static void main(String[] args) throws Exception { ClassConfigurator test=getInstance(true); System.out.println('\n' + test.printMagicMap()); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/MagicNumberReader.java0000644000175000017500000001027311366547366026514 0ustar twernertwernerpackage org.jgroups.conf; /** * Reads and maintains mapping between magic numbers and classes * @author Filip Hanik (filip@filip.net) * @author Bela Ban * @version 1.0 */ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.util.Util; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.NamedNodeMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; public class MagicNumberReader { public static final String MAGIC_NUMBER_FILE="jg-magic-map.xml"; public String mMagicNumberFile=MAGIC_NUMBER_FILE; protected static final Log log=LogFactory.getLog(MagicNumberReader.class); public void setFilename(String file) { mMagicNumberFile=file; } /** * try to read the magic number configuration file as a Resource form the classpath using getResourceAsStream * if this fails this method tries to read the configuration file from mMagicNumberFile using a FileInputStream (not in classpath but somewhere else in the disk) * * @return an array of ClassMap objects that where parsed from the file (if found) or an empty array if file not found or had en exception */ public ClassMap[] readMagicNumberMapping() { try { InputStream stream=Util.getResourceAsStream(mMagicNumberFile, this.getClass()); // try to load the map from file even if it is not a Resource in the class path if(stream == null) { try { if(log.isTraceEnabled()) log.trace("Could not read " + mMagicNumberFile + " as Resource from the CLASSPATH, will try to read it from file."); stream=new FileInputStream(mMagicNumberFile); if(log.isTraceEnabled()) log.trace("Magic number file found at '" + mMagicNumberFile + '\''); } catch(FileNotFoundException fnfe) { if(log.isWarnEnabled()) log.warn("Failed reading - '" + mMagicNumberFile + "' is not found, got error '" + fnfe.getLocalizedMessage() + "'. Please make sure it is in the CLASSPATH or in the " + "specified location. Will continue, but marshalling will be slower"); } } if(stream == null) { return new ClassMap[0]; } return parse(stream); } catch(Exception x) { if(log.isErrorEnabled()) log.error("failed reading magic map", x); } return new ClassMap[0]; } protected static ClassMap[] parse(InputStream stream) throws Exception { DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); factory.setValidating(false); //for now DocumentBuilder builder=factory.newDocumentBuilder(); Document document=builder.parse(stream); NodeList class_list=document.getElementsByTagName("class"); java.util.Vector v=new java.util.Vector(); for(int i=0; i < class_list.getLength(); i++) { if(class_list.item(i).getNodeType() == Node.ELEMENT_NODE) { v.addElement(parseClassData(class_list.item(i))); } } ClassMap[] data=new ClassMap[v.size()]; v.copyInto(data); return data; } protected static ClassMap parseClassData(Node protocol) throws java.io.IOException { try { protocol.normalize(); NamedNodeMap attrs=protocol.getAttributes(); String clazzname=null; String magicnumber=null; magicnumber=attrs.getNamedItem("id").getNodeValue(); clazzname=attrs.getNamedItem("name").getNodeValue(); return new ClassMap(clazzname, Short.valueOf(magicnumber)); } catch(Exception x) { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/ClassMap.java0000644000175000017500000000214311366547366024700 0ustar twernertwernerpackage org.jgroups.conf; import org.jgroups.util.Util; /** * Maintains mapping between magic number and class * * @author Filip Hanik (filip@filip.net) * @author Bela Ban * @version 1.0 */ public class ClassMap { private final String mClassname; private final short mMagicNumber; public ClassMap(String clazz, short magicnumber) { mClassname=clazz; mMagicNumber=magicnumber; } public int hashCode() { return getMagicNumber(); } public String getClassName() { return mClassname; } public short getMagicNumber() { return mMagicNumber; } /** * Returns the Class object for this class
*/ public Class getClassForMap() throws ClassNotFoundException { return Util.loadClass(getClassName(), this.getClass()); } public boolean equals(Object another) { if(another instanceof ClassMap) { ClassMap obj=(ClassMap)another; return getClassName().equals(obj.getClassName()); } else return false; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/PlainConfigurator.java0000644000175000017500000000244111366547366026624 0ustar twernertwerner// $Id: PlainConfigurator.java,v 1.2 2004/09/23 16:29:14 belaban Exp $ package org.jgroups.conf; /** * A ProtocolStackConfigurator for the old style properties. *
* Old style properties are referred to as the property string used by channel earlier * they look like this PROTOCOL(param=value;param=value):PROTOCOL:PROTOCOL
* All it does is that it holds the string, it currently doesn't parse it at all. * * @author Filip Hanik (
filip@filip.net) * @version 1.0 */ public class PlainConfigurator implements ProtocolStackConfigurator { private final String mProperties; /** * Instantiates a PlainConfigurator with old style properties */ public PlainConfigurator(String properties) { mProperties = properties; } /** * returns the old style protocol string */ public String getProtocolStackString() { return mProperties; } /** * Throws a UnsupportedOperationException all the time. No parsing implemented. */ public ProtocolData[] getProtocolStack() { /**todo: Implement this org.jgroups.conf.ProtocolStackConfigurator method*/ throw new java.lang.UnsupportedOperationException("Method getProtocolStack() not yet implemented."); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/XmlConfigurator.java0000644000175000017500000004402111366547366026321 0ustar twernertwerner// $Id: XmlConfigurator.java,v 1.18 2007/05/09 23:50:21 belaban Exp $ package org.jgroups.conf; /** * Uses XML to configure a protocol stack * @author Filip Hanik (filip@filip.net) * @version 1.0 */ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.*; import org.jgroups.stack.Configurator; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.*; public class XmlConfigurator implements ProtocolStackConfigurator { public static final String ATTR_NAME="name"; public static final String ATTR_VALUE="value"; public static final String ATTR_INHERIT="inherit"; public static final String ELMT_PROT_OVERRIDE="protocol-override"; public static final String ELMT_PROT="protocol"; public static final String ELMT_PROT_NAME="protocol-name"; public static final String ELMT_CLASS="class-name"; public static final String ELMT_DESCRIPTION="description"; public static final String ELMT_PROT_PARAMS="protocol-params"; private final ArrayList mProtocolStack=new ArrayList(); private final String mStackName; protected static final Log log=LogFactory.getLog(XmlConfigurator.class); protected XmlConfigurator(String stackName, ProtocolData[] protocols) { mStackName=stackName; for(int i=0; i < protocols.length; i++) mProtocolStack.add(protocols[i]); } protected XmlConfigurator(String stackName) { this(stackName, new ProtocolData[0]); } public static XmlConfigurator getInstance(URL url) throws java.io.IOException { return getInstance(url.openStream()); } public static XmlConfigurator getInstanceOldFormat(URL url) throws java.io.IOException { return getInstanceOldFormat(url.openStream()); } public static XmlConfigurator getInstance(InputStream stream) throws java.io.IOException { return parse(stream); } public static XmlConfigurator getInstanceOldFormat(InputStream stream) throws java.io.IOException { return parseOldFormat(stream); } public static XmlConfigurator getInstance(Element el) throws java.io.IOException { return parse(el); } /** * * @param convert If false: print old plain output, else print new XML format * @return String with protocol stack in specified format */ public String getProtocolStackString(boolean convert) { StringBuilder buf=new StringBuilder(); Iterator it=mProtocolStack.iterator(); if(convert) buf.append("\n"); while(it.hasNext()) { ProtocolData d=(ProtocolData)it.next(); if(convert) buf.append(" <"); buf.append(d.getProtocolString(convert)); if(convert) buf.append("/>"); if(it.hasNext()) { if(convert) buf.append('\n'); else buf.append(':'); } } if(convert) buf.append("\n"); return buf.toString(); } public String getProtocolStackString() { return getProtocolStackString(false); } public ProtocolData[] getProtocolStack() { return (ProtocolData[])mProtocolStack.toArray(new ProtocolData[mProtocolStack.size()]); } public String getName() { return mStackName; } public void override(ProtocolData data) throws IOException { int index=mProtocolStack.indexOf(data); if(index < 0) throw new IOException("You can not override a protocol that doesn't exist"); ProtocolData source=(ProtocolData)mProtocolStack.get(index); source.override(data.getParametersAsArray()); } public void add(ProtocolData data) { mProtocolStack.add(data); } protected static XmlConfigurator parseOldFormat(InputStream stream) throws java.io.IOException { XmlConfigurator configurator=null; try { DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); factory.setValidating(false); //for now DocumentBuilder builder=factory.newDocumentBuilder(); Document document=builder.parse(stream); Element root=(Element)document.getElementsByTagName("protocol-stack").item(0); root.normalize(); //print("",new PrintWriter(System.out),root); String stackname=root.getAttribute(ATTR_NAME); String inherit=root.getAttribute(ATTR_INHERIT); boolean isinherited=(inherit != null && inherit.length() > 0); NodeList protocol_list=document.getElementsByTagName(isinherited ? ELMT_PROT_OVERRIDE : ELMT_PROT); Vector v=new Vector(); for(int i=0; i < protocol_list.getLength(); i++) { if(protocol_list.item(i).getNodeType() == Node.ELEMENT_NODE) { v.addElement(parseProtocolData(protocol_list.item(i))); } } ProtocolData[] protocols=new ProtocolData[v.size()]; v.copyInto(protocols); if(isinherited) { URL inheritURL=new URL(inherit); configurator=XmlConfigurator.getInstance(inheritURL); for(int i=0; i < protocols.length; i++) configurator.override(protocols[i]); } else { configurator=new XmlConfigurator(stackname, protocols); } } catch(Exception x) { if(x instanceof java.io.IOException) throw (java.io.IOException)x; else { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } return configurator; } protected static XmlConfigurator parse(InputStream stream) throws java.io.IOException { /** * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. * If somebody wants to improve this, please be my guest. */ try { DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); factory.setValidating(false); //for now DocumentBuilder builder=factory.newDocumentBuilder(); Document document=builder.parse(stream); // The root element of the document should be the "config" element, // but the parser(Element) method checks this so a check is not // needed here. Element configElement = document.getDocumentElement(); return parse(configElement); } catch(Exception x) { if(x instanceof java.io.IOException) throw (java.io.IOException)x; else { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } } protected static XmlConfigurator parse(Element root_element) throws java.io.IOException { XmlConfigurator configurator=null; /** LinkedList */ LinkedList prot_data=new LinkedList(); /** * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. * If somebody wants to improve this, please be my guest. */ try { // NodeList roots=root_element.getChildNodes(); // for(int i =0; i < roots.getLength(); i++) { // root=roots.item(i); // if(root.getNodeType() != Node.ELEMENT_NODE) // continue; // } String root_name=root_element.getNodeName(); if(!"config".equals(root_name.trim().toLowerCase())) { log.fatal("XML protocol stack configuration does not start with a '' element; " + "maybe the XML configuration needs to be converted to the new format ?\n" + "use 'java org.jgroups.conf.XmlConfigurator -new_format' to do so"); throw new IOException("invalid XML configuration"); } NodeList prots=root_element.getChildNodes(); for(int i=0; i < prots.getLength(); i++) { Node node = prots.item(i); if( node.getNodeType() != Node.ELEMENT_NODE ) continue; Element tag = (Element) node; String protocol = tag.getTagName(); // System.out.println("protocol: " + protocol); LinkedList tmp=new LinkedList(); NamedNodeMap attrs = tag.getAttributes(); int attrLength = attrs.getLength(); for(int a = 0; a < attrLength; a ++) { Attr attr = (Attr) attrs.item(a); String name = attr.getName(); String value = attr.getValue(); // System.out.println(" name=" + name + ", value=" + value); tmp.add(new ProtocolParameter(name, value)); } ProtocolParameter[] params=new ProtocolParameter[tmp.size()]; for(int j=0; j < tmp.size(); j++) params[j]=(ProtocolParameter)tmp.get(j); ProtocolData data=new ProtocolData(protocol, "bla", protocol, params); prot_data.add(data); } ProtocolData[] data=new ProtocolData[(prot_data.size())]; for(int k=0; k < prot_data.size(); k++) data[k]=(ProtocolData)prot_data.get(k); configurator=new XmlConfigurator("bla", data); } catch(Exception x) { if(x instanceof java.io.IOException) throw (java.io.IOException)x; else { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } return configurator; } protected static ProtocolData parseProtocolData(Node protocol) throws java.io.IOException { try { protocol.normalize(); boolean isOverride=ELMT_PROT_OVERRIDE.equals(protocol.getNodeName()); int pos=0; NodeList children=protocol.getChildNodes(); /** * there should be 4 Element Nodes if we are not overriding * 1. protocol-name * 2. description * 3. class-name * 4. protocol-params * * If we are overriding we should have * 1. protocol-name * 2. protocol-params */ // String name=null; String clazzname=null; String desc=null; ProtocolParameter[] plist=null; for(int i=0; i < children.getLength(); i++) { if(children.item(i).getNodeType() == Node.ELEMENT_NODE) { pos++; if(isOverride && (pos == 2)) pos=4; switch(pos) { case 1: name=children.item(i).getFirstChild().getNodeValue(); break; case 2: desc=children.item(i).getFirstChild().getNodeValue(); break; case 3: clazzname=children.item(i).getFirstChild().getNodeValue(); break; case 4: plist=parseProtocolParameters((Element)children.item(i)); break; }//switch }//end if }//for if(isOverride) return new ProtocolData(name, plist); else return new ProtocolData(name, desc, clazzname, plist); } catch(Exception x) { if(x instanceof java.io.IOException) throw (java.io.IOException)x; else { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } } protected static ProtocolParameter[] parseProtocolParameters(Element protparams) throws IOException { try { Vector v=new Vector(); protparams.normalize(); NodeList parameters=protparams.getChildNodes(); for(int i=0; i < parameters.getLength(); i++) { if(parameters.item(i).getNodeType() == Node.ELEMENT_NODE) { String pname=parameters.item(i).getAttributes().getNamedItem(ATTR_NAME).getNodeValue(); String pvalue=parameters.item(i).getAttributes().getNamedItem(ATTR_VALUE).getNodeValue(); ProtocolParameter p=new ProtocolParameter(pname, pvalue); v.addElement(p); }//end if }//for ProtocolParameter[] result=new ProtocolParameter[v.size()]; v.copyInto(result); return result; } catch(Exception x) { IOException tmp=new IOException(); tmp.initCause(x); throw tmp; } } public static void main(String[] args) throws Exception { String input_file=null; XmlConfigurator conf; boolean old_format=false; for(int i=0; i < args.length; i++) { if(args[i].equals("-old")) { old_format=true; continue; } if(args[i].equals("-file")) { input_file=args[++i]; continue; } help(); return; } if(input_file != null) { InputStream input=null; try { input=new FileInputStream(new File(input_file)); } catch(Throwable t) { } if(input == null) { try { input=new URL(input_file).openStream(); } catch(Throwable t) { } } if(input == null) input=Thread.currentThread().getContextClassLoader().getResourceAsStream(input_file); if(old_format) { Configurator config=new Configurator(); String cfg=inputAsString(input); Vector tmp=config.parseConfigurations(cfg); System.out.println(dump(tmp)); // conf=XmlConfigurator.getInstanceOldFormat(input); // output=conf.getProtocolStackString(true); // output=replace(output, "org.jgroups.protocols.", ""); // System.out.println(getTitle(input_file)); // System.out.println('\n' + output); } else { conf=XmlConfigurator.getInstance(input); String tmp=conf.getProtocolStackString(); System.out.println("\n" + tmp); } } else { log.error("no input file given"); } } /** * * @param tmp Vector of Configurator.ProtocolConfiguration * @return String (XML format) */ private static String dump(Vector tmp) { StringBuilder sb=new StringBuilder(); String indent=" "; sb.append("\n"); for(Iterator it=tmp.iterator(); it.hasNext();) { Configurator.ProtocolConfiguration cfg=(Configurator.ProtocolConfiguration)it.next(); sb.append(indent).append("<").append(cfg.getProtocolName()); Properties props=cfg.getProperties(); if(props.isEmpty()) { sb.append(" />\n"); } else { sb.append("\n").append(indent).append(indent); for(Iterator it2=props.entrySet().iterator(); it2.hasNext();) { Map.Entry entry=(Map.Entry)it2.next(); String key=(String)entry.getKey(); String val=(String)entry.getValue(); key=trim(key); val=trim(val); sb.append(key).append("=\"").append(val).append("\""); if(it2.hasNext()) { sb.append("\n").append(indent).append(indent); } } sb.append(" />\n"); } } sb.append("\n"); return sb.toString(); } private static String trim(String val) { String retval=""; int index; val=val.trim(); while(true) { index=val.indexOf('\n'); if(index == -1) { retval+=val; break; } retval+=val.substring(0, index); val=val.substring(index+1); } return retval; } private static String inputAsString(InputStream input) throws IOException { int len=input.available(); byte[] buf=new byte[len]; input.read(buf, 0, len); return new String(buf); } public static String replace(String input, final String expr, String replacement) { StringBuilder sb=new StringBuilder(); int new_index=0, index=0, len=expr.length(), input_len=input.length(); while(true) { new_index=input.indexOf(expr, index); if(new_index == -1) { sb.append(input.substring(index, input_len)); break; } sb.append(input.substring(index, new_index)); sb.append(replacement); index=new_index + len; } return sb.toString(); } static void help() { System.out.println("XmlConfigurator -file [-old]"); System.out.println("(-old: converts old (plain-text) input format into new XML format)"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/conf/ProtocolStackConfigurator.java0000644000175000017500000000052511366547366030351 0ustar twernertwerner// $Id: ProtocolStackConfigurator.java,v 1.1.1.1 2003/09/09 01:24:08 belaban Exp $ package org.jgroups.conf; /** * @author Filip Hanik (filip@filip.net) * @version 1.0 */ public interface ProtocolStackConfigurator { String getProtocolStackString(); ProtocolData[] getProtocolStack(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/0000755000175000017500000000000011621261110022330 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/GetNetworkInterfaces.java0000644000175000017500000000201611366547366027321 0ustar twernertwernerpackage org.jgroups.util; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; /** * Lists all network interfaces on a system * @author Bela Ban Dec 18 * @author 2003 * @version $Id: GetNetworkInterfaces.java,v 1.1 2005/06/23 13:31:09 belaban Exp $ */ public class GetNetworkInterfaces { public static void main(String[] args) throws SocketException { Enumeration en=NetworkInterface.getNetworkInterfaces(); while(en.hasMoreElements()) { NetworkInterface i=(NetworkInterface)en.nextElement(); System.out.println(i.getName() + ':'); System.out.println(" \t" + i.getDisplayName()); for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr=(InetAddress)en2.nextElement(); System.out.println(" \t" + addr + " (" + addr.getHostName() + ')'); } System.out.println("---------------------"); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Queue.java0000644000175000017500000004627611366547366024330 0ustar twernertwerner// $Id: Queue.java,v 1.30 2007/08/08 09:36:23 belaban Exp $ package org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.TimeoutException; import java.util.*; /** * Elements are added at the tail and removed from the head. Class is thread-safe in that * 1 producer and 1 consumer may add/remove elements concurrently. The class is not * explicitely designed for multiple producers or consumers. Implemented as a linked * list, so that removal of an element at the head does not cause a right-shift of the * remaining elements (as in a Vector-based implementation). * @author Bela Ban */ public class Queue { /*head and the tail of the list so that we can easily add and remove objects*/ private Element head=null, tail=null; /*flag to determine the state of the queue*/ private boolean closed=false; /*current size of the queue*/ private int size=0; /* Lock object for synchronization. Is notified when element is added */ private final Object mutex=new Object(); /** Lock object for syncing on removes. It is notified when an object is removed */ // Object remove_mutex=new Object(); /*the number of end markers that have been added*/ private int num_markers=0; /** * if the queue closes during the runtime * an endMarker object is added to the end of the queue to indicate that * the queue will close automatically when the end marker is encountered * This allows for a "soft" close. * @see Queue#close */ private static final Object endMarker=new Object(); protected static final Log log=LogFactory.getLog(Queue.class); /** * the class Element indicates an object in the queue. * This element allows for the linked list algorithm by always holding a * reference to the next element in the list. * if Element.next is null, then this element is the tail of the list. */ static class Element { /*the actual value stored in the queue*/ Object obj=null; /*pointer to the next item in the (queue) linked list*/ Element next=null; /** * creates an Element object holding its value * @param o - the object to be stored in the queue position */ Element(Object o) { obj=o; } /** * prints out the value of the object */ public String toString() { return obj != null? obj.toString() : "null"; } } /** * creates an empty queue */ public Queue() { } /** * Returns the first element. Returns null if no elements are available. */ public Object getFirst() { synchronized(mutex) { return head != null? head.obj : null; } } /** * Returns the last element. Returns null if no elements are available. */ public Object getLast() { synchronized(mutex) { return tail != null? tail.obj : null; } } /** * returns true if the Queue has been closed * however, this method will return false if the queue has been closed * using the close(true) method and the last element has yet not been received. * @return true if the queue has been closed */ public boolean closed() { synchronized(mutex) { return closed; } } /** * adds an object to the tail of this queue * If the queue has been closed with close(true) no exception will be * thrown if the queue has not been flushed yet. * @param obj - the object to be added to the queue * @exception QueueClosedException exception if closed() returns true */ public void add(Object obj) throws QueueClosedException { if(obj == null) { if(log.isErrorEnabled()) log.error("argument must not be null"); return; } /*lock the queue from other threads*/ synchronized(mutex) { if(closed) throw new QueueClosedException(); if(this.num_markers > 0) throw new QueueClosedException("queue has been closed. You can not add more elements. " + "Waiting for removal of remaining elements."); addInternal(obj); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } } public void addAll(Collection c) throws QueueClosedException { if(c == null) { if(log.isErrorEnabled()) log.error("argument must not be null"); return; } /*lock the queue from other threads*/ synchronized(mutex) { if(closed) throw new QueueClosedException(); if(this.num_markers > 0) throw new QueueClosedException("queue has been closed. You can not add more elements. " + "Waiting for removal of remaining elements."); Object obj; for(Iterator it=c.iterator(); it.hasNext();) { obj=it.next(); if(obj != null) addInternal(obj); } /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } } public void addAll(List list) throws QueueClosedException { if(list == null) { if(log.isErrorEnabled()) log.error("argument must not be null"); return; } /*lock the queue from other threads*/ synchronized(mutex) { if(closed) throw new QueueClosedException(); if(this.num_markers > 0) throw new QueueClosedException("queue has been closed. You can not add more elements. " + "Waiting for removal of remaining elements."); for(Object obj: list) { if(obj != null) addInternal(obj); } /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } } /** * Adds a new object to the head of the queue * basically (obj.equals(queue.remove(queue.add(obj)))) returns true * If the queue has been closed with close(true) no exception will be * thrown if the queue has not been flushed yet. * @param obj - the object to be added to the queue * @exception QueueClosedException exception if closed() returns true */ public void addAtHead(Object obj) throws QueueClosedException { if(obj == null) { if(log.isErrorEnabled()) log.error("argument must not be null"); return; } /*lock the queue from other threads*/ synchronized(mutex) { if(closed) throw new QueueClosedException(); if(this.num_markers > 0) throw new QueueClosedException("Queue.addAtHead(): queue has been closed. You can not add more elements. " + "Waiting for removal of remaining elements."); Element el=new Element(obj); /*check the head element in the list*/ if(head == null) { /*this is the first object, we could have done add(obj) here*/ head=el; tail=head; size=1; } else { /*set the head element to be the child of this one*/ el.next=head; /*set the head to point to the recently added object*/ head=el; /*increase the size*/ size++; } /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } } /** * Removes 1 element from head or blocks * until next element has been added or until queue has been closed * @return the first element to be taken of the queue */ public Object remove() throws QueueClosedException { Object retval; synchronized(mutex) { /*wait as long as the queue is empty. return when an element is present or queue is closed*/ while(size == 0) { if(closed) throw new QueueClosedException(); try { mutex.wait(); } catch(InterruptedException ex) { } } if(closed) throw new QueueClosedException(); /*remove the head from the queue, if we make it to this point, retval should not be null !*/ retval=removeInternal(); if(retval == null) if(log.isErrorEnabled()) log.error("element was null, should never be the case"); } /* * we ran into an Endmarker, which means that the queue was closed before * through close(true) */ // if(retval == endMarker) { // close(false); // mark queue as closed // throw new QueueClosedException(); // } return retval; } /** * Removes 1 element from the head. * If the queue is empty the operation will wait for timeout ms. * if no object is added during the timeout time, a Timout exception is thrown * @param timeout - the number of milli seconds this operation will wait before it times out * @return the first object in the queue */ public Object remove(long timeout) throws QueueClosedException, TimeoutException { Object retval; synchronized(mutex) { if(closed) throw new QueueClosedException(); /*if the queue size is zero, we want to wait until a new object is added*/ if(size == 0) { try { /*release the mutex lock and wait no more than timeout ms*/ mutex.wait(timeout); } catch(InterruptedException ex) { } } /*we either timed out, or got notified by the mutex lock object*/ if(closed) throw new QueueClosedException(); /*get the next value*/ retval=removeInternal(); /*null result means we timed out*/ if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms"); /*if we reached an end marker we are going to close the queue*/ // if(retval == endMarker) { // close(false); // throw new QueueClosedException(); // } /*at this point we actually did receive a value from the queue, return it*/ return retval; } } /** * removes a specific object from the queue. * the object is matched up using the Object.equals method. * @param obj the actual object to be removed from the queue */ public void removeElement(Object obj) throws QueueClosedException { Element el, tmp_el; if(obj == null) { if(log.isErrorEnabled()) log.error("argument must not be null"); return; } synchronized(mutex) { if(closed) /*check to see if the queue is closed*/ throw new QueueClosedException(); el=head; /*the queue is empty*/ if(el == null) return; /*check to see if the head element is the one to be removed*/ if(el.obj.equals(obj)) { /*the head element matched we will remove it*/ head=el.next; el.next=null; el.obj=null; /*check if we only had one object left *at this time the queue becomes empty *this will set the tail=head=null */ if(size == 1) tail=head; // null decrementSize(); return; } /*look through the other elements*/ while(el.next != null) { if(el.next.obj.equals(obj)) { tmp_el=el.next; if(tmp_el == tail) // if it is the last element, move tail one to the left (bela Sept 20 2002) tail=el; el.next.obj=null; el.next=el.next.next; // point to the el past the next one. can be null. tmp_el.next=null; tmp_el.obj=null; decrementSize(); break; } el=el.next; } } } /** * returns the first object on the queue, without removing it. * If the queue is empty this object blocks until the first queue object has * been added * @return the first object on the queue */ public Object peek() throws QueueClosedException { Object retval; synchronized(mutex) { while(size == 0) { if(closed) throw new QueueClosedException(); try { mutex.wait(); } catch(InterruptedException ex) { } } if(closed) throw new QueueClosedException(); retval=(head != null)? head.obj : null; } if(retval == endMarker) { close(false); // mark queue as closed throw new QueueClosedException(); } return retval; } /** * returns the first object on the queue, without removing it. * If the queue is empty this object blocks until the first queue object has * been added or the operation times out * @param timeout how long in milli seconds will this operation wait for an object to be added to the queue * before it times out * @return the first object on the queue */ public Object peek(long timeout) throws QueueClosedException, TimeoutException { Object retval; synchronized(mutex) { if(size == 0) { if(closed) throw new QueueClosedException(); try { mutex.wait(timeout); } catch(InterruptedException ex) { } } if(closed) throw new QueueClosedException(); retval=head != null? head.obj : null; if(retval == null) throw new TimeoutException("timeout=" + timeout + "ms"); if(retval == endMarker) { close(false); throw new QueueClosedException(); } return retval; } } /** Removes all elements from the queue. This method can succeed even when the queue is closed */ public void clear() { synchronized(mutex) { head=tail=null; size=0; num_markers=0; mutex.notifyAll(); } } /** Marks the queues as closed. When an add or remove operation is attempted on a closed queue, an exception is thrown. @param flush_entries When true, a end-of-entries marker is added to the end of the queue. Entries may be added and removed, but when the end-of-entries marker is encountered, the queue is marked as closed. This allows to flush pending messages before closing the queue. */ public void close(boolean flush_entries) { synchronized(mutex) { if(flush_entries && size > 0) { try { add(endMarker); // add an end-of-entries marker to the end of the queue num_markers++; } catch(QueueClosedException closed_ex) { } return; } closed=true; mutex.notifyAll(); } } /** Waits until the queue has been closed. Returns immediately if already closed * @param timeout Number of milliseconds to wait. A value <= 0 means to wait forever */ public void waitUntilClosed(long timeout) { synchronized(mutex) { if(closed) return; try { mutex.wait(timeout); } catch(InterruptedException e) { } } } /** * resets the queue. * This operation removes all the objects in the queue and marks the queue open */ public void reset() { synchronized(mutex) { num_markers=0; if(!closed) close(false); size=0; head=null; tail=null; closed=false; mutex.notifyAll(); } } /** * Returns all the elements of the queue * @return A copy of the queue */ public LinkedList values() { LinkedList retval=new LinkedList(); synchronized(mutex) { Element el=head; while(el != null) { retval.add(el.obj); el=el.next; } } return retval; } /** * returns the number of objects that are currently in the queue */ public int size() { synchronized(mutex) { return size - num_markers; } } /** * prints the size of the queue */ public String toString() { return "Queue (" + size() + ") elements"; } /* ------------------------------------- Private Methods ----------------------------------- */ private final void addInternal(Object obj) { /*create a new linked list element*/ Element el=new Element(obj); /*check the first element*/ if(head == null) { /*the object added is the first element*/ /*set the head to be this object*/ head=el; /*set the tail to be this object*/ tail=head; /*set the size to be one, since the queue was empty*/ size=1; } else { /*add the object to the end of the linked list*/ tail.next=el; /*set the tail to point to the last element*/ tail=el; /*increase the size*/ size++; } } /** * Removes the first element. Returns null if no elements in queue. * Always called with mutex locked (we don't have to lock mutex ourselves) */ private Object removeInternal() { Element retval; Object obj; /*if the head is null, the queue is empty*/ if(head == null) return null; retval=head; // head must be non-null now head=head.next; if(head == null) tail=null; decrementSize(); if(head != null && head.obj == endMarker) { closed=true; mutex.notifyAll(); } retval.next=null; obj=retval.obj; retval.obj=null; return obj; } /** Doesn't need to be synchronized; is always called from synchronized methods */ final private void decrementSize() { size--; if(size < 0) size=0; } /* ---------------------------------- End of Private Methods -------------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ContextObjectInputStream.java0000644000175000017500000000547611366547366030210 0ustar twernertwernerpackage org.jgroups.util; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.util.HashMap; /** * ObjectInputStream which sets a contact classloader for reading bytes into objects. Copied from * MarshalledValueInputStream of JBoss * @author Bela Ban * @version $Id: ContextObjectInputStream.java,v 1.7 2006/05/02 08:23:25 belaban Exp $ */ public class ContextObjectInputStream extends ObjectInputStream { /** * A class wide cache of proxy classes populated by resolveProxyClass */ private static final HashMap classCache=new HashMap(); private static final HashMap primClasses = new HashMap(9, 1.0F); static { primClasses.put("boolean", boolean.class); primClasses.put("byte", byte.class); primClasses.put("char", char.class); primClasses.put("short", short.class); primClasses.put("int", int.class); primClasses.put("long", long.class); primClasses.put("float", float.class); primClasses.put("double", double.class); primClasses.put("void", void.class); } /** * Creates a new instance of MarshalledValueOutputStream */ public ContextObjectInputStream(InputStream is) throws IOException { super(is); } protected Class resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException { String className=v.getName(); Class resolvedClass=null; // Check the class cache first if it exists synchronized(classCache) { resolvedClass=(Class)classCache.get(className); } if(resolvedClass == null) { try { resolvedClass=Util.loadClass(className, this.getClass()); } catch(ClassNotFoundException e) { } if(resolvedClass == null) { /* This is a backport von JDK 1.4's java.io.ObjectInputstream to support * retrieval of primitive classes (e.g. Boolean.TYPE) in JDK 1.3. * This is required for org.jgroups.blocks.MethodCall to support primitive * Argument types in JDK1.3: */ resolvedClass = (Class) primClasses.get(className); if (resolvedClass == null) { /* Use the super.resolveClass() call which will resolve array classes and primitives. We do not use this by default as this can result in caching of stale values across redeployments. */ resolvedClass=super.resolveClass(v); } } synchronized(classCache) { classCache.put(className, resolvedClass); } } return resolvedClass; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/QueueClosedException.java0000644000175000017500000000105611366547366027324 0ustar twernertwerner// $Id: QueueClosedException.java,v 1.3 2006/11/13 17:42:11 bstansberry Exp $ package org.jgroups.util; public class QueueClosedException extends Exception { private static final long serialVersionUID = -7575787375592873964L; public QueueClosedException() { } public QueueClosedException( String msg ) { super( msg ); } public String toString() { if ( this.getMessage() != null ) return "QueueClosedException: " + this.getMessage(); else return "QueueClosedException"; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Buffer.java0000644000175000017500000000231311366547366024435 0ustar twernertwernerpackage org.jgroups.util; /** * Buffer with an offset and length. Will be replaced with NIO equivalent once JDK 1.4 becomes baseline. This class is * immutable * @author Bela Ban * @version $Id: Buffer.java,v 1.5 2007/01/04 12:11:09 belaban Exp $ */ public class Buffer { private final byte[] buf; private final int offset; private final int length; public Buffer(byte[] buf, int offset, int length) { this.buf=buf; this.offset=offset; this.length=length; } public byte[] getBuf() { return buf; } public int getOffset() { return offset; } public int getLength() { return length; } public Buffer copy() { byte[] new_buf=buf != null? new byte[length] : null; int new_length=new_buf != null? new_buf.length : 0; if(new_buf != null) System.arraycopy(buf, offset, new_buf, 0, length); return new Buffer(new_buf, 0, new_length); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(length).append(" bytes"); if(offset > 0) sb.append(" (offset=").append(offset).append(")"); return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Rsp.java0000644000175000017500000000362711366547366024001 0ustar twernertwerner// $Id: Rsp.java,v 1.6.4.1 2008/01/22 10:01:17 belaban Exp $ package org.jgroups.util; import org.jgroups.Address; /** * class that represents a response from a communication */ public class Rsp { /* flag that represents whether the response was received */ boolean received=false; /* flag that represents whether the response was suspected */ boolean suspected=false; /* The sender of this response */ Address sender=null; /* the value from the response */ Object retval=null; public Rsp(Address sender) { this.sender=sender; } public Rsp(Address sender, boolean suspected) { this.sender=sender; this.suspected=suspected; } public Rsp(Address sender, Object retval) { this.sender=sender; this.retval=retval; received=true; } public boolean equals(Object obj) { if(!(obj instanceof Rsp)) return false; Rsp other=(Rsp)obj; if(sender != null) return sender.equals(other.sender); return other.sender == null; } public Object getValue() { return retval; } public void setValue(Object val) { this.retval=val; } public Address getSender() { return sender; } public boolean wasReceived() { return received; } public void setReceived(boolean received) { this.received=received; if(received) suspected=false; } public boolean wasSuspected() { return suspected; } public void setSuspected(boolean suspected) { this.suspected=suspected; if(suspected) received=false; } public String toString() { return new StringBuilder("sender=").append(sender).append(", retval=").append(retval).append(", received="). append(received).append(", suspected=").append(suspected).toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/SizeBoundedQueue.java0000644000175000017500000000647511366547366026461 0ustar twernertwernerpackage org.jgroups.util; import java.util.Map; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; /** * Queue as described in http://jira.jboss.com/jira/browse/JGRP-376. However, this queue only works with * {@link org.jgroups.protocols.TP.IncomingPacket} elements. * The queue maintains a max number of bytes and a total of all of the messages in the internal queues. Whenever a * message is added, we increment the total by the length of the message. When a message is removed, we decrement the * total. Removal blocks until a message is available, addition blocks if the max size has been exceeded, until there * is enough space to add another message. * Note that the max size should always be greater than the size of the largest message to be received, otherwise an * additon would always fail because msg.length > max size !
* Access patterns: this instance is always accessed by the thread pool only ! Concurrent take() or poll() methods, * but only a single thread at a time calls put() ! * @author Bela Ban * @version $Id: SizeBoundedQueue.java,v 1.3 2006/12/31 14:29:52 belaban Exp $ */ public class SizeBoundedQueue implements BlockingQueue { int max_size=1000 * 1000; int capacity=0; /** Map>. Maintains a list of unicast messages per sender */ final Map ucast_msgs=new ConcurrentHashMap(10); /** Map>. Maintains a list of multicast messages per sender */ final Map mcast_msgs=new ConcurrentHashMap(10); public boolean add(Object o) { return false; } public int drainTo(Collection c) { return 0; } public int drainTo(Collection c, int maxElements) { return 0; } public boolean offer(Object o) { return false; } public boolean offer(Object o, long timeout, TimeUnit unit) throws InterruptedException { return false; } public Object poll(long timeout, TimeUnit unit) throws InterruptedException { return null; } public void put(Object o) throws InterruptedException { } public int remainingCapacity() { return 0; } public Object take() throws InterruptedException { return null; } public Object element() { return null; } public Object peek() { return null; } public Object poll() { return null; } public Object remove() { return null; } public boolean addAll(Collection c) { return false; } public void clear() { } public boolean contains(Object o) { return false; } public boolean containsAll(Collection c) { return false; } public boolean isEmpty() { return false; } public Iterator iterator() { return null; } public boolean remove(Object o) { return false; } public boolean removeAll(Collection c) { return false; } public boolean retainAll(Collection c) { return false; } public int size() { return 0; } public Object[] toArray() { return new Object[0]; } public Object[] toArray(Object[] a) { return new Object[0]; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ExposedByteArrayInputStream.java0000644000175000017500000000502611366547366030656 0ustar twernertwernerpackage org.jgroups.util; import java.io.ByteArrayInputStream; /** * @author Bela Ban * @version $Id: ExposedByteArrayInputStream.java,v 1.1.14.1 2008/10/28 09:39:01 belaban Exp $ */ public class ExposedByteArrayInputStream extends ByteArrayInputStream { /** * Creates a ByteArrayInputStream * so that it uses buf as its * buffer array. * The buffer array is not copied. * The initial value of pos * is 0 and the initial value * of count is the length of * buf. * @param buf the input buffer. */ public ExposedByteArrayInputStream(byte[] buf) { super(buf); } /** * Creates ByteArrayInputStream * that uses buf as its * buffer array. The initial value of pos * is offset and the initial value * of count is the minimum of offset+length * and buf.length. * The buffer array is not copied. The buffer's mark is * set to the specified offset. * @param buf the input buffer. * @param offset the offset in the buffer of the first byte to read. * @param length the maximum number of bytes to read from the buffer. */ public ExposedByteArrayInputStream(byte[] buf, int offset, int length) { super(buf, offset, length); } public void setData(byte[] buf, int offset, int length) { this.buf=buf; this.pos=offset; this.count=Math.min(offset + length, buf.length); this.mark=offset; } public int read() { return (pos < count)? (buf[pos++] & 0xff) : -1; } public int read(byte b[], int off, int len) { if(b == null) { throw new NullPointerException(); } else if(off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } if(pos >= count) { return -1; } if(pos + len > count) { len=count - pos; } if(len <= 0) { return 0; } System.arraycopy(buf, pos, b, off, len); pos+=len; return len; } public long skip(long n) { if(pos + n > count) { n=count - pos; } if(n < 0) { return 0; } pos+=n; return n; } public int available() { return count - pos; } public void reset() { pos=mark; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/BoundedList.java0000644000175000017500000000173611366547366025450 0ustar twernertwernerpackage org.jgroups.util; import java.util.concurrent.ConcurrentLinkedQueue; /** * A bounded subclass of LinkedList, oldest elements are removed once max capacity is exceeded. Note that this * class is not synchronized (like LinkedList). * @author Bela Ban Nov 20, 2003 * @version $Id: BoundedList.java,v 1.5 2007/08/08 12:02:05 belaban Exp $ */ public class BoundedList extends ConcurrentLinkedQueue { int max_capacity=10; public BoundedList() { super(); } public BoundedList(int size) { super(); max_capacity=size; } /** * Adds an element at the tail. Removes an object from the head if capacity is exceeded * @param obj The object to be added */ public boolean add(T obj) { if(obj == null) return false; while(size() >= max_capacity && size() > 0) { poll(); } return super.add(obj); } public T removeFromHead() { return poll(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Tuple.java0000644000175000017500000000145211366547366024320 0ustar twernertwernerpackage org.jgroups.util; /** * Holds 2 values, useful when we have a map with a key, but more than 1 value and we don't want to create a separate * holder object for the values, and don't want to pass the values as a list or array. * @author Bela Ban * @version $Id: Tuple.java,v 1.1.2.2 2008/01/21 11:36:07 belaban Exp $ */ public class Tuple { private V1 val1; private V2 val2; public Tuple(V1 val1, V2 val2) { this.val1=val1; this.val2=val2; } public V1 getVal1() { return val1; } public void setVal1(V1 val1) { this.val1=val1; } public V2 getVal2() { return val2; } public void setVal2(V2 val2) { this.val2=val2; } public String toString() { return val1 + " : " + val2; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/obsolete/0000755000175000017500000000000011621261110024144 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/obsolete/CondVar.java.txt0000644000175000017500000000740711366547366027223 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.TimeoutException; /** * Class that checks on a condition and - if condition doesn't match the expected result - waits until the result * matches the expected result, or a timeout occurs. First version used WaitableBoolean from util.concurrent, but * that class would not allow for timeouts. * @author Bela Ban * @version $Id: CondVar.java.txt,v 1.1 2006/12/31 14:56:56 belaban Exp $ */ public class CondVar { Object cond; final String name; final Object lock; public CondVar(String name, Object cond) { this.name=name; this.cond=cond; lock=this; } public CondVar(String name, Object cond, Object lock) { this.name=name; this.cond=cond; this.lock=lock; } public Object get() { synchronized(lock) { return cond; } } /** Sets the result */ public void set(Object result) { synchronized(lock) { cond=result; lock.notifyAll(); } } public Object getLock() { return lock; } /** * Waits until the condition matches the expected result. Returns immediately if they match, otherwise waits * for timeout milliseconds or until the results match. * @param result The result, needs to match the condition (using equals()). * @param timeout Number of milliseconds to wait. A value of <= 0 means to wait forever * @throws TimeoutException Thrown if the result still doesn't match the condition after timeout * milliseconds have elapsed */ public void waitUntilWithTimeout(Object result, long timeout) throws TimeoutException { _waitUntilWithTimeout(result, timeout); } /** * Waits until the condition matches the expected result. Returns immediately if they match, otherwise waits * for timeout milliseconds or until the results match. This method doesn't throw a TimeoutException * @param result The result, needs to match the condition (using equals()). * @param timeout Number of milliseconds to wait. A value of <= 0 means to wait forever */ public void waitUntil(Object result, long timeout) { try { _waitUntilWithTimeout(result, timeout); } catch(TimeoutException e) { } } public void waitUntil(Object result) { try {waitUntilWithTimeout(result, 0);} catch(TimeoutException e) {} } private void _waitUntilWithTimeout(Object result, long timeout) throws TimeoutException { long time_to_wait=timeout, start; boolean timeout_occurred=false; synchronized(lock) { if(result == null && cond == null) return; start=System.currentTimeMillis(); while(match(result, cond) == false) { if(timeout <= 0) { doWait(); } else { if(time_to_wait <= 0) { timeout_occurred=true; break; // terminate the while loop } else { doWait(time_to_wait); time_to_wait=timeout - (System.currentTimeMillis() - start); } } } if(timeout_occurred) throw new TimeoutException(); } } void doWait() { try {lock.wait();} catch(InterruptedException e) {} } void doWait(long timeout) { try {lock.wait(timeout);} catch(InterruptedException e) {} } private boolean match(Object o1, Object o2) { if(o1 != null) return o1.equals(o2); else return o2.equals(o1); } public String toString() { return name + "=" + cond; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/obsolete/List.java.txt0000644000175000017500000002674211366547366026605 0ustar twernertwerner// $Id: List.java.txt,v 1.1 2007/08/08 08:58:28 belaban Exp $ package org.jgroups.util; import java.io.*; import java.util.*; /** * Doubly-linked list. Elements can be added at head or tail and removed from head/tail. * This class is tuned for element access at either head or tail, random access to elements * is not very fast; in this case use Vector. Concurrent access is supported: a thread is blocked * while another thread adds/removes an object. When no objects are available, removal returns null. * @author Bela Ban */ public class List implements Externalizable, Cloneable { protected Element head=null, tail=null; protected int size=0; protected transient final Object mutex=new Object(); class Element { Object obj=null; Element next=null; Element prev=null; Element(Object o) { obj=o; } } public List() { } /** Adds an object at the tail of the list. */ public void add(Object obj) { Element el=new Element(obj); synchronized(mutex) { if(head == null) { head=el; tail=head; size=1; } else { el.prev=tail; tail.next=el; tail=el; size++; } } } /** Adds an object at the head of the list. */ public void addAtHead(Object obj) { Element el=new Element(obj); synchronized(mutex) { if(head == null) { head=el; tail=head; size=1; } else { el.next=head; head.prev=el; head=el; size++; } } } public void addAll(Collection c) { if(c == null) return; for(Iterator it=c.iterator(); it.hasNext();) { add(it.next()); } } /** Removes an object from the tail of the list. Returns null if no elements available */ public Object remove() { Element retval=null; synchronized(mutex) { if(tail == null) return null; retval=tail; if(head == tail) { // last element head=null; tail=null; } else { tail.prev.next=null; tail=tail.prev; retval.prev=null; } size--; } return retval.obj; } /** Removes an object from the head of the list. Returns null if no elements available */ public Object removeFromHead() { Element retval=null; synchronized(mutex) { if(head == null) return null; retval=head; if(head == tail) { // last element head=null; tail=null; } else { head=head.next; head.prev=null; retval.next=null; } size--; } return retval.obj; } /** Returns element at the tail (if present), but does not remove it from list. */ public Object peek() { synchronized(mutex) { return tail != null ? tail.obj : null; } } /** Returns element at the head (if present), but does not remove it from list. */ public Object peekAtHead() { synchronized(mutex) { return head != null ? head.obj : null; } } /** Removes element obj from the list, checking for equality using the equals operator. Only the first duplicate object is removed. Returns the removed object. */ public Object removeElement(Object obj) { Element el=null; Object retval=null; synchronized(mutex) { el=head; while(el != null) { if(el.obj.equals(obj)) { retval=el.obj; if(head == tail) { // only 1 element left in the list head=null; tail=null; } else if(el.prev == null) { // we're at the head head=el.next; head.prev=null; el.next=null; } else if(el.next == null) { // we're at the tail tail=el.prev; tail.next=null; el.prev=null; } else { // we're somewhere in the middle of the list el.prev.next=el.next; el.next.prev=el.prev; el.next=null; el.prev=null; } size--; break; } el=el.next; } } return retval; } public void removeAll() { synchronized(mutex) { size=0; head=null; tail=null; } } public int size() { return size; } public String toString() { StringBuilder ret=new StringBuilder("["); Element el=head; while(el != null) { if(el.obj != null) ret.append(el.obj).append(" "); el=el.next; } ret.append(']'); return ret.toString(); } public String toStringWithDelimiter(String delimiter) { Element el=head; boolean first=true; StringBuilder sb=new StringBuilder(); while(el != null) { if(first) { first=false; } else { sb.append(delimiter); } sb.append(el.obj); el=el.next; } return sb.toString(); } public String dump() { StringBuilder ret=new StringBuilder("["); for(Element el=head; el != null; el=el.next) ret.append(el.obj).append(" "); return ret.toString() + ']'; } public Vector getContents() { Vector retval=new Vector(size); Element el; synchronized(mutex) { el=head; while(el != null) { retval.addElement(el.obj); el=el.next; } } return retval; } public Enumeration elements() { return new ListEnumerator(head); } public boolean contains(Object obj) { Element el=head; while(el != null) { if(el.obj != null && el.obj.equals(obj)) return true; el=el.next; } return false; } public List copy() { List retval=new List(); synchronized(mutex) { for(Element el=head; el != null; el=el.next) retval.add(el.obj); } return retval; } protected Object clone() throws CloneNotSupportedException { // calling clone() is superfluous because we don't want a shallow copy return copy(); } public void writeExternal(ObjectOutput out) throws IOException { Element el; synchronized(mutex) { el=head; out.writeInt(size); for(int i=0; i < size; i++) { out.writeObject(el.obj); el=el.next; } } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { Object obj; int new_size=in.readInt(); if(new_size == 0) return; for(int i=0; i < new_size; i++) { obj=in.readObject(); add(obj); } } // public void writeTo(DataOutputStream out) throws IOException { // Element el; // Object obj; // if(size == 0) { // out.writeInt(0); // return; // } // out.writeInt(size); // el=head; // while(el != null) { // obj=el.obj; // if(obj instanceof Streamable) { // out.writeByte(1); // ((Streamable)obj).writeTo(out); // } // else { // out.writeByte(0); // ObjectOutputStream oos=new ObjectOutputStream(out); // very inefficient // oos.writeObject(obj); // oos.close(); // } // el=el.next; // } // } // // public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { // Object obj; // int size=in.readInt(); // byte b; // // for(int i=0; i < size; i++) { // b=in.readByte(); // if(b == 1) { // // } // else if(b == 0) { // // } // else // throw new InstantiationException("byte '" + b + "' not recognized (needs to be 1 or 0)"); // } // // } class ListEnumerator implements Enumeration { Element curr=null; ListEnumerator(Element start) { curr=start; } public boolean hasMoreElements() { return curr != null; } public Object nextElement() { Object retval; if(curr == null) throw new NoSuchElementException(); retval=curr.obj; curr=curr.next; return retval; } } // public static void main(String args[]) { // List l=new List(); // l.add("Bela"); // l.add("Janet"); // l.add("Marco"); // l.add("Ralph"); // for(Enumeration e=l.elements(); e.hasMoreElements();) { // System.out.println(e.nextElement()); // } // System.out.println(l + ".contains(\"Bela\"): " + l.contains("Bela")); // l.add(new Integer(1)); // l.add(new Integer(2)); // l.add(new Integer(5)); // l.add(new Integer(6)); // System.out.println(l + ".contains(2): " + l.contains(new Integer(2))); // } public static void main(String[] args) { List l=new List(); Long n; l.addAtHead(new Integer(1)); l.addAtHead(new Integer(2)); l.addAtHead(new Integer(3)); l.addAtHead(new Integer(4)); l.addAtHead(new Integer(5)); System.out.println("list: " + l.toStringWithDelimiter(", ")); System.out.println("Removed from head: " + l.removeFromHead()); System.out.println("Removed from head: " + l.removeFromHead()); System.out.println("Removed from head: " + l.removeFromHead()); System.out.println("Removed from head: " + l.removeFromHead()); System.out.println("Removed from head: " + l.removeFromHead()); System.out.println("Removed from head: " + l.removeFromHead()); System.out.println("Removed from head: " + l.removeFromHead()); System.out.print("Adding 50000 numbers:"); for(long i=0; i < 50000; i++) { n=new Long(i); if(i % 2 == 0) { l.addAtHead(n); } else { l.add(n); } } System.out.println(" OK"); long num=0; System.out.print("Removing all elements: "); while((l.remove()) != null) num++; System.out.println("OK, removed " + num + " objects"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Scheduler.java0000644000175000017500000002056311366547366025151 0ustar twernertwerner// $Id: Scheduler.java,v 1.15.4.2 2008/02/12 05:47:26 vlada Exp $ package org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Global; /** * Implementation of a priority scheduler. The scheduler maintains a queue to the end of which * all tasks are added. It continually looks at the first queue element, assigns a thread to * it, runs the thread and waits for completion. When a new priority task is added, * it will be added to the head of the queue and the scheduler will be interrupted. In this * case, the currently handled task is suspended, and the one at the head of the queue * handled. This is recursive: a priority task can always be interrupted by another priority * task. Recursion ends when no more priority tasks are added, or when the thread pool is * exhausted. * * @author Bela Ban */ public class Scheduler implements Runnable { final Queue queue=new Queue(); Thread sched_thread=null; Task current_task=null; ThreadPool pool=null; SchedulerListener listener=null; protected static final Log log=LogFactory.getLog(Scheduler.class); /** Process items on the queue concurrently. The default is to wait until the processing of an item * has completed before fetching the next item from the queue. Note that setting this to true * may destroy the properties of a protocol stack, e.g total or causal order may not be * guaranteed. Set this to true only if you know what you're doing ! */ boolean concurrent_processing=false; /** max number of threads, will only be allocated when needed */ int NUM_THREADS=128; static final int WAIT_FOR_THREAD_AVAILABILITY=3000; public Scheduler() { String tmp=Util.getProperty(new String[]{Global.SCHEDULER_MAX_THREADS}, null, null, false, "128"); this.NUM_THREADS=Integer.parseInt(tmp); } public Scheduler(int num_threads) { this.NUM_THREADS=num_threads; } public void setListener(SchedulerListener l) { listener=l; } public boolean getConcurrentProcessing() { return concurrent_processing; } public void setConcurrentProcessing(boolean process_concurrently) { this.concurrent_processing=process_concurrently; } public void run() { while(sched_thread != null) { if(queue.closed()) break; try { current_task=(Task)queue.peek(); // get the first task in the queue (blocks until available) if(current_task == null) { // @remove if(log.isWarnEnabled()) log.warn("current task is null, queue.size()=" + queue.size() + ", queue.closed()=" + queue.closed() + ", continuing"); continue; } if(current_task.suspended) { current_task.suspended=false; current_task.thread.resume(); if(listener != null) listener.resumed(current_task.target); } else { if(current_task.thread == null) { current_task.thread=pool.getThread(); if(current_task.thread == null) { // thread pool exhausted if(log.isWarnEnabled()) log.warn("thread pool exhausted, waiting for " + WAIT_FOR_THREAD_AVAILABILITY + "ms before retrying"); Util.sleep(WAIT_FOR_THREAD_AVAILABILITY); continue; } } // if we get here, current_task.thread and current_task.target are guaranteed to be non-null if(listener != null) listener.started(current_task.target); if(current_task.thread.assignTask(current_task.target) == false) continue; } /* * http://jira.jboss.com/jira/browse/JGRP-690 * Thread interrupt status is not always cleared by default */ if(Thread.interrupted()) throw new InterruptedException(); if(concurrent_processing == false) { // this is the default: process serially synchronized(current_task.thread) { while(!current_task.thread.done() && !current_task.thread.suspended) current_task.thread.wait(); } if(listener != null) listener.stopped(current_task.target); } queue.removeElement(current_task); } catch(InterruptedException interrupted) { if(sched_thread == null || queue.closed()) break; if(current_task.thread != null) { current_task.thread.suspend(); if(listener != null) listener.suspended(current_task.target); current_task.suspended=true; } } catch(QueueClosedException closed_ex) { return; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception=" + Util.print(ex)); } } if(log.isTraceEnabled()) log.trace("scheduler thread terminated"); } public void addPrio(Runnable task) { Task new_task=new Task(task); boolean do_interrupt=false; try { synchronized(queue) { // sync against add() if(queue.size() == 0) queue.add(new_task); else { queue.addAtHead(new_task); do_interrupt=true; } } if(do_interrupt) // moved out of 'synchronized(queue)' to minimize lock contention sched_thread.interrupt(); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception=" + e); } } public void add(Runnable task) { Task new_task=new Task(task); try { synchronized(queue) { // sync against addPrio() queue.add(new_task); } } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e); } } public void start() { if(queue.closed()) queue.reset(); if(sched_thread == null) { pool=new ThreadPool(NUM_THREADS); sched_thread=new Thread(this, "Scheduler main thread"); sched_thread.setDaemon(true); sched_thread.start(); } } /** * Stop the scheduler thread. The thread may be waiting for its next task (queue.peek()) or it may be waiting on * the currently executing thread. In the first case, closing the queue will throw a QueueClosed exception which * terminates the scheduler thread. In the second case, after closing the queue, we interrupt the scheduler thread, * which then checks whether the queue is closed. If this is the case, the scheduler thread terminates. */ public void stop() { Thread tmp=null; // 1. Close the queue queue.close(false); // will stop thread at next peek(); // 2. Interrupt the scheduler thread if(sched_thread != null && sched_thread.isAlive()) { tmp=sched_thread; sched_thread=null; tmp.interrupt(); try { tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } if(tmp.isAlive()) if(log.isErrorEnabled()) log.error("scheduler thread is still not dead !!!"); } sched_thread=null; // 3. Delete the thread pool if(pool != null) { pool.destroy(); pool=null; } } public static class Task { ReusableThread thread=null; Runnable target=null; boolean suspended=false; Task(Runnable target) { this.target=target; } public String toString() { return "[thread=" + thread + ", target=" + target + ", suspended=" + suspended + ']'; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/SchedulerListener.java0000644000175000017500000000070211366547366026650 0ustar twernertwerner// $Id: SchedulerListener.java,v 1.2 2005/07/17 11:33:58 chrislott Exp $ package org.jgroups.util; /** * Provides callback for use with a {@link Scheduler}. */ public interface SchedulerListener { /** * @param r */ void started(Runnable r); /** * @param r */ void stopped(Runnable r); /** * @param r */ void suspended(Runnable r); /** * @param r */ void resumed(Runnable r); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Proxy.java0000644000175000017500000007354511366547366024364 0ustar twernertwerner// $Id: Proxy.java,v 1.3.4.1 2008/01/22 10:01:16 belaban Exp $ package org.jgroups.util; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocketFactory; import java.io.*; import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.*; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Redirects incoming TCP connections to other hosts/ports. All redirections are defined in a file as for example *
 * 127.0.0.1:8888=www.ibm.com:80
 * localhost:80=pop.mail.yahoo.com:110
 * 
* The first line forwards all requests to port 8888 on to www.ibm.com at port 80 (it also forwards the HTTP * response back to the sender. The second line essentially provides a POP-3 service on port 8110, using * Yahoo's POP service. This is neat when you're behind a firewall and one of the few services in the outside * world that are not blocked is port 80 (HHTP).
* Note that JDK 1.4 is required for this class. Also, concurrent.jar has to be on the classpath. Note that * you also need to include jsse.jar/jce.jar (same location as rt.jar) if you want SSL sockets.
* To create SSLServerSockets you'll need to do the following: * Generate a certificate as follows: *
 * keytool -genkey -keystore /home/bela/.keystore -keyalg rsa -alias bela -storepass  -keypass 
 * 
* * Start the Proxy as follows: *
 * java -Djavax.net.ssl.keyStore=/home/bela/.keystore -Djavax.net.ssl.keyStorePassword=
 *      -Djavax.net.ssl.trustStore=/home/bela/.keystore -Djavax.net.ssl.trustStorePassword=
 *      org.jgroups.util.Proxy -file /home/bela/map.properties
 * 
* Start client as follows: *
 * java -Djavax.net.ssl.trustStore=/home/bela/.keystore -Djavax.net.ssl.trustStorePassword= sslclient
 * 
*
* To import a certificate into the keystore, use the following steps: *
 * openssl x509 -in server.crt -out server.crt.der -outform DER
 * keytool -import -trustcacerts -alias  -file server.crt.der
 * 
* This will store the server's certificate in the ${user.home}/.keystore key store. *
* Note that an SSL client or server can be debugged by starting it as follows: *
-Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol -Djavax.net.debug=ssl
*
* If you run a web browser, simply enter https://: as URL to connect to an SSLServerSocket *
Note that we cannot use JDK 1.4's selectors for SSL sockets, as * getChannel() on an SSL socket doesn't seem to work. * @todo Check whether SSLSocket.getChannel() or SSLServerSocket.getChannel() works. * @author Bela Ban */ public class Proxy { InetAddress local=null, remote=null; int local_port=0, remote_port=0; static boolean verbose=false; static boolean debug=false; String mapping_file=null; // contains a list of src and dest host:port pairs final HashMap mappings=new HashMap(); // keys=MyInetSocketAddr (src), values=MyInetSocketAddr (dest) Executor executor; // maintains a thread pool static final int MIN_THREAD_POOL_SIZE=2; static final int MAX_THREAD_POOL_SIZE=64; // for processing requests static final int BUFSIZE=1024; // size of data transfer buffer public Proxy(InetAddress local, int local_port, InetAddress remote, int remote_port, boolean verbose, boolean debug) { this.local=local; this.local_port=local_port; this.remote=remote; this.remote_port=remote_port; Proxy.verbose=verbose; Proxy.debug=debug; } public Proxy(InetAddress local, int local_port, InetAddress remote, int remote_port, boolean verbose, boolean debug, String mapping_file) { this(local, local_port, remote, remote_port, verbose, debug); this.mapping_file=mapping_file; } public void start() throws Exception { Map.Entry entry; Selector selector; ServerSocketChannel sock_channel; MyInetSocketAddress key, value; if (remote !=null && local !=null) mappings.put(new InetSocketAddress(local, local_port), new InetSocketAddress(remote, remote_port)); if (mapping_file !=null) { try { populateMappings(mapping_file); } catch (Exception ex) { log("Failed reading " + mapping_file); throw ex; } } log("\nProxy started at " + new java.util.Date()); if (verbose) { log("\nMappings:\n---------"); for (Iterator it=mappings.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry) it.next(); log(toString((InetSocketAddress) entry.getKey()) + " <--> " + toString((InetSocketAddress) entry.getValue())); } log("\n"); } // 1. Create a Selector selector=Selector.open(); // Create a thread pool (Executor) executor=new ThreadPoolExecutor(MIN_THREAD_POOL_SIZE, MAX_THREAD_POOL_SIZE, 30000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1000)); for (Iterator it=mappings.keySet().iterator(); it.hasNext();) { key=(MyInetSocketAddress) it.next(); value=(MyInetSocketAddress) mappings.get(key); // if either source or destination are SSL, we cannot use JDK 1.4 // NIO selectors, but have to fall back on separate threads per connection if (key.ssl() || value.ssl()) { // if(2 == 2) { SocketAcceptor acceptor=new SocketAcceptor(key, value); executor.execute(acceptor); continue; } // 2. Create a ServerSocketChannel sock_channel=ServerSocketChannel.open(); sock_channel.configureBlocking(false); sock_channel.socket().bind(key); // 3. Register the selector with all server sockets. 'Key' is attachment, so we get it again on // select(). That way we can associate it with the mappings hashmap to find the corresponding // value sock_channel.register(selector, SelectionKey.OP_ACCEPT, key); } // 4. Start main loop. won't return until CTRL-C'ed loop(selector); } /** We handle only non-SSL connections */ void loop(Selector selector) { Set ready_keys; SelectionKey key; ServerSocketChannel srv_sock; SocketChannel in_sock, out_sock; InetSocketAddress src, dest; while (true) { if (verbose) log("[Proxy] ready to accept connection"); // 4. Call Selector.select() try { selector.select(); // get set of ready objects ready_keys=selector.selectedKeys(); for (Iterator it=ready_keys.iterator(); it.hasNext();) { key=(SelectionKey) it.next(); it.remove(); if (key.isAcceptable()) { srv_sock=(ServerSocketChannel) key.channel(); // get server socket and attachment src=(InetSocketAddress) key.attachment(); in_sock=srv_sock.accept(); // accept request if (verbose) log("Proxy.loop()", "accepted connection from " + toString(in_sock)); dest=(InetSocketAddress) mappings.get(src); // find corresponding dest if (dest == null) { in_sock.close(); log("Proxy.loop()", "did not find a destination host for " + src); continue; } else { if (verbose) log("Proxy.loop()", "relaying traffic from " + toString(src) + " to " + toString(dest)); } // establish connection to destination host try { out_sock=SocketChannel.open(dest); // uses thread pool (Executor) to handle request, closes socks at end handleConnection(in_sock, out_sock); } catch (Exception ex) { in_sock.close(); throw ex; } } } } catch (Exception ex) { log("Proxy.loop()", "exception: " + ex); } } } // void handleConnection(Socket in_sock, Socket out_sock) { // try { // Relayer r=new Relayer(in_sock, out_sock); // executor.execute(r); // r=new Relayer(out_sock, in_sock); // executor.execute(r); // } // catch (Exception ex) { // log("Proxy.handleConnection()", "exception: " + ex); // } // finally { // close(in_sock, out_sock); // } // } void handleConnection(SocketChannel in, SocketChannel out) { try { _handleConnection(in, out); } catch (Exception ex) { log("Proxy.handleConnection()", "exception: " + ex); } } void _handleConnection(final SocketChannel in_channel, final SocketChannel out_channel) throws Exception { executor.execute(new Runnable() { public void run() { Selector sel=null; SocketChannel tmp; Set ready_keys; SelectionKey key; ByteBuffer transfer_buf=ByteBuffer.allocate(BUFSIZE); try { sel=Selector.open(); in_channel.configureBlocking(false); out_channel.configureBlocking(false); in_channel.register(sel, SelectionKey.OP_READ); out_channel.register(sel, SelectionKey.OP_READ); while (sel.select() > 0) { ready_keys=sel.selectedKeys(); for (Iterator it=ready_keys.iterator(); it.hasNext();) { key=(SelectionKey) it.next(); it.remove(); // remove current entry (why ?) tmp=(SocketChannel) key.channel(); if (tmp == null) { log( "Proxy._handleConnection()", "attachment is null, continuing"); continue; } if (key.isReadable()) { // data is available to be read from tmp if (tmp == in_channel) { // read all data from in_channel and forward it to out_channel (request) if (relay(tmp, out_channel, transfer_buf) == false) return; } if (tmp == out_channel) { // read all data from out_channel and forward it // to in_channel (response) if (relay(tmp, in_channel, transfer_buf) == false) return; } } } } } catch (Exception ex) { ex.printStackTrace(); } finally { close(sel, in_channel, out_channel); } } }); } void close(Selector sel, SocketChannel in_channel, SocketChannel out_channel) { try { if (sel !=null) sel.close(); } catch (Exception ex) { } try { if (in_channel !=null) in_channel.close(); } catch (Exception ex) { } try { if (out_channel !=null) out_channel.close(); } catch (Exception ex) { } } /** * Read all data from from and write it to to. * Returns false if channel was closed */ boolean relay(SocketChannel from, SocketChannel to, ByteBuffer buf) throws Exception { int num; StringBuilder sb; buf.clear(); while (true) { num=from.read(buf); if (num < 0) return false; else if (num == 0) return true; buf.flip(); if (verbose) { log(printRelayedData(toString(from), toString(to), buf.remaining())); } if (debug) { sb=new StringBuilder(); sb.append(new String(buf.array()).trim()); sb.append('\n'); log(sb.toString()); } to.write(buf); buf.flip(); } } String toString(SocketChannel ch) { StringBuilder sb=new StringBuilder(); Socket sock; if (ch == null) return null; if ((sock=ch.socket()) == null) return null; sb.append(sock.getInetAddress().getHostName()).append(':').append(sock.getPort()); return sb.toString(); } String toString(InetSocketAddress addr) { StringBuilder sb; sb=new StringBuilder(); if (addr == null) return null; sb.append(addr.getAddress().getHostName()).append(':').append(addr.getPort()); if (addr instanceof MyInetSocketAddress) sb.append(" [ssl=").append(((MyInetSocketAddress) addr).ssl()).append(']'); return sb.toString(); } static String printRelayedData(String from, String to, int num_bytes) { StringBuilder sb; sb=new StringBuilder(); sb.append("\n[PROXY] ").append(from); sb.append(" to ").append(to); sb.append(" (").append(num_bytes).append(" bytes)"); // log("Proxy.relay()", sb.toString()); return sb.toString(); } /** * Populates mappings hashmap. An example of a definition file is: *
     * http://localhost:8888=http://www.yahoo.com:80
     * https://localhost:2200=https://cvs.sourceforge.net:22
     * http://localhost:8000=https://www.ibm.com:443
     * 
* Mappings can be http-https, https-http, http-http or https-https */ void populateMappings(String filename) throws Exception { FileInputStream in=new FileInputStream(filename); BufferedReader reader; String line; URI key, value; int index; boolean ssl_key, ssl_value; final String HTTPS="https"; reader=new BufferedReader(new InputStreamReader(in)); while ((line=reader.readLine()) !=null) { line=line.trim(); if (line.startsWith("//") || line.startsWith("#") || line.length() == 0) continue; index=line.indexOf('='); if (index == -1) throw new Exception("Proxy.populateMappings(): detected no '=' character in " + line); key=new URI(line.substring(0, index)); ssl_key=key.getScheme().trim().equals(HTTPS); value=new URI(line.substring(index + 1)); ssl_value=value.getScheme().trim().equals(HTTPS); check(key); check(value); log("key: " + key + ", value: " + value); mappings.put(new MyInetSocketAddress(key.getHost(), key.getPort(), ssl_key), new MyInetSocketAddress(value.getHost(), value.getPort(), ssl_value)); } in.close(); } /** Checks whether a URI is http(s)://: */ void check(URI u) throws Exception { if (u.getScheme() == null) throw new Exception( "scheme is null in " + u + ", (valid URI is \"http(s)://:\")"); if (u.getHost() == null) throw new Exception( "host is null in " + u + ", (valid URI is \"http(s)://:\")"); if (u.getPort() <=0) throw new Exception( "port is <=0 in " + u + ", (valid URI is \"http(s)://:\")"); } /** Input is "host:port" */ SocketAddress strToAddr(String input) throws Exception { StringTokenizer tok=new StringTokenizer(input, ":"); String host, port; host=tok.nextToken(); port=tok.nextToken(); return new InetSocketAddress(host, Integer.parseInt(port)); } String printSelectionOps(SelectionKey key) { StringBuilder sb=new StringBuilder(); if ((key.readyOps() & SelectionKey.OP_ACCEPT) !=0) sb.append("OP_ACCEPT "); if ((key.readyOps() & SelectionKey.OP_CONNECT) !=0) sb.append("OP_CONNECT "); if ((key.readyOps() & SelectionKey.OP_READ) !=0) sb.append("OP_READ "); if ((key.readyOps() & SelectionKey.OP_WRITE) !=0) sb.append("OP_WRITE "); return sb.toString(); } public static void main(String[] args) { Proxy p; InetAddress local=null, remote=null; int local_port=0, remote_port=0; String tmp, tmp_addr, tmp_port; boolean verbose=false, debug=false; int index; String mapping_file=null; try { for (int i=0; i < args.length; i++) { tmp=args[i]; if ("-help".equals(tmp)) { help(); return; } if ("-verbose".equals(tmp)) { verbose=true; continue; } if ("-local".equals(tmp)) { tmp_addr=args[++i]; index=tmp_addr.indexOf(':'); if (index > -1) { // it is in the format address:port tmp_port=tmp_addr.substring(index + 1); local_port=Integer.parseInt(tmp_port); tmp_addr=tmp_addr.substring(0, index); local=InetAddress.getByName(tmp_addr); } else local=InetAddress.getByName(args[++i]); continue; } if ("-local_port".equals(tmp)) { local_port=Integer.parseInt(args[++i]); continue; } if ("-remote".equals(tmp)) { tmp_addr=args[++i]; index=tmp_addr.indexOf(':'); if (index > -1) { // it is in the format address:port tmp_port=tmp_addr.substring(index + 1); remote_port=Integer.parseInt(tmp_port); tmp_addr=tmp_addr.substring(0, index); remote=InetAddress.getByName(tmp_addr); } else remote=InetAddress.getByName(args[++i]); continue; } if ("-remote_port".equals(tmp)) { remote_port=Integer.parseInt(args[++i]); continue; } if ("-file".equals(tmp)) { mapping_file=args[++i]; continue; } if ("-debug".equals(tmp)) { debug=true; continue; } help(); return; } if (local == null) local=InetAddress.getLocalHost(); p=new Proxy(local, local_port, remote, remote_port, verbose, debug, mapping_file); p.start(); } catch (Throwable ex) { ex.printStackTrace(); } } static void help() { System.out.println("Proxy [-help] [-local ] [-local_port ] " + "[-remote ] [-remote_port ] [-verbose] " + "[-file ] [-debug]"); } static void log(String method_name, String msg) { System.out.println('[' + method_name + "]: " + msg); } static void log(String msg) { System.out.println(msg); } static void close(Socket in, Socket out) { if (in !=null) { try { in.close(); } catch (Exception ex) { } } if (out !=null) { try { out.close(); } catch (Exception ex) { } } } static void close(Socket sock) { if (sock !=null) { try { sock.close(); } catch (Exception ex) { } } } static class Relayer implements Runnable { final Socket in_sock; final Socket out_sock; final InputStream in; final OutputStream out; Thread t=null; final java.util.List listeners=new ArrayList(); String name=null; interface Listener { void connectionClosed(); } public Relayer(Socket in_sock, Socket out_sock, String name) throws Exception { this.in_sock=in_sock; this.out_sock=out_sock; this.name=name; in=in_sock.getInputStream(); out=out_sock.getOutputStream(); } public void addListener(Listener l) { if(l != null && !listeners.contains(l)) listeners.add(l); } public void run() { byte[] buf=new byte[1024]; int num; StringBuilder sb; try { while(t != null) { if ((num=in.read(buf)) == -1) break; if (verbose) { //sb=new StringBuilder(); //sb.append("forwarding ").append(num).append(" bytes from ").append(toString(in_sock)); //sb.append(" to ").append(toString(out_sock)); // log("Proxy.Relayer.run()", sb.toString()); log(printRelayedData(toString(in_sock), toString(out_sock), num)); } if (debug) { sb=new StringBuilder(); sb.append(new String(buf, 0, num).trim()); log(sb.toString()); } out.write(buf, 0, num); //if(debug) // System.out.println(new String(buf)); } } catch (Exception ex) { log("Proxy.Relayer.run(): [" + name + "] exception=" + ex + ", in_sock=" + in_sock + ", out_sock=" + out_sock); } finally { stop(); } } public void start() { if(t == null) { t=new Thread(this, "Proxy.Relayer"); t.setDaemon(true); t.start(); } } public void stop() { t=null; close(in_sock); close(out_sock); } String toString(Socket s) { if(s == null) return null; return s.getInetAddress().getHostName() + ':' + s.getPort(); } void notifyListeners() { for(Iterator it=listeners.iterator(); it.hasNext();) { try { ((Listener)it.next()).connectionClosed(); } catch(Throwable ex) { ; } } } } static class MyInetSocketAddress extends InetSocketAddress { boolean is_ssl=false; public MyInetSocketAddress(InetAddress addr, int port) { super(addr, port); } public MyInetSocketAddress(InetAddress addr, int port, boolean is_ssl) { super(addr, port); this.is_ssl=is_ssl; } public MyInetSocketAddress(int port) { super(port); } public MyInetSocketAddress(int port, boolean is_ssl) { super(port); this.is_ssl=is_ssl; } public MyInetSocketAddress(String hostname, int port) { super(hostname, port); } public MyInetSocketAddress(String hostname, int port, boolean is_ssl) { super(hostname, port); this.is_ssl=is_ssl; } public boolean ssl() { return is_ssl; } public String toString() { return super.toString() + " [ssl: " + ssl() + ']'; } } /** * Handles accepts on an SSLServerSocket or ServerSocket. Creates a {@link * Connection} for each successful accept(). * * @author bela Dec 19, 2002 */ class SocketAcceptor implements Runnable { ServerSocket srv_sock=null; MyInetSocketAddress dest=null; /** * Create an SSLServerSocket or ServerSocket and continuously call * accept() on it. * @param sock_addr */ public SocketAcceptor(MyInetSocketAddress sock_addr, MyInetSocketAddress dest) throws Exception { this.dest=dest; if(sock_addr.ssl()) { srv_sock=createSSLServerSocket(sock_addr); } else { srv_sock=createServerSocket(sock_addr); } executor.execute(this); } public void run() { Connection conn; Socket s, dest_sock; while (srv_sock !=null) { try { s=srv_sock.accept(); dest_sock=dest.ssl() ? createSSLSocket(dest) : createSocket(dest); conn=new Connection(s, dest_sock); conn.start(); } catch (Exception e) { log("Proxy.SSLServerSocketAcceptor.run(): exception=" + e); break; } } } Socket createSocket(InetSocketAddress addr) throws Exception { return new Socket(addr.getAddress(), addr.getPort()); } Socket createSSLSocket(InetSocketAddress addr) throws Exception { SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); return sslsocketfactory.createSocket(addr.getAddress(), addr.getPort()); } ServerSocket createServerSocket(InetSocketAddress addr) throws Exception { return new ServerSocket(addr.getPort(), 10, addr.getAddress()); } ServerSocket createSSLServerSocket(InetSocketAddress addr) throws Exception { SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); SSLServerSocket sslserversocket; sslserversocket=(SSLServerSocket)sslserversocketfactory.createServerSocket(addr.getPort(), 10, addr.getAddress()); return sslserversocket; } } /** * Handles an incoming SSLSocket or Socket. Looks up the destination in the * mapping hashmap, key is the incoming socket address. Creates an outgoing * socket (regular or SSL, depending on settings) and relays data between * incoming and outgoing sockets. Closes the connection when either incoming * or outgoing socket is closed, or when stop() is called. * * @author bela Dec 19, 2002 */ static class Connection implements Relayer.Listener { Relayer in_to_out=null; Relayer out_to_in=null; /** * Creates an outgoing (regular or SSL) socket according to the mapping * table. Sets both input and output stream. Caller needs to call * start() after the instance has been created. * @param in The Socket we got as result of accept() * @throws Exception Thrown if either the input or output streams cannot * be created. */ public Connection(Socket in, Socket out) throws Exception { in_to_out=new Relayer(in, out, "in-out"); in_to_out.addListener(this); out_to_in=new Relayer(out, in, "out-in"); out_to_in.addListener(this); } /** Starts relaying between incoming and outgoing sockets. * Returns immediately (thread is started). * */ public void start() { in_to_out.start(); out_to_in.start(); } public void stop() { if (in_to_out !=null) { in_to_out.stop(); } if (out_to_in !=null) { out_to_in.stop(); } } public void connectionClosed() { stop(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Marshaller.java0000644000175000017500000001143411366547366025322 0ustar twernertwerner// $Id: Marshaller.java,v 1.6 2007/05/01 09:15:17 belaban Exp $ package org.jgroups.util; import org.jgroups.conf.ClassConfigurator; import org.jgroups.ChannelException; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * Title: JGroups Communications * Description: Contact me at mail@filip.net * Copyright: Copyright (c) 2002 * Company: www.filip.net * @author Filip Hanik * @author Bela Ban * @version 1.0 * * This class marshalls classes, in other words it serializes and deserializes classes * to and from object streams. * It performs a magic number matching to decrease the number of bytes that are being sent * over the wire. * If no magic number is available for the class, the classname is sent over instead */ public class Marshaller { /** * The class configurator maps classes to magic numbers */ private static final ClassConfigurator mConfigurator; static { try { mConfigurator=ClassConfigurator.getInstance(true); } catch(ChannelException e) { throw new ExceptionInInitializerError(e.toString()); } } public Marshaller() { } /** * reads the magic number, instantiates the class (from the * configurator) and invokes the readExternal method on the object. * If no magic number is present, the method will read the * string and then get the class from the configurator. * @param in an ObjectInput stream - the stream should be composed as follows:
* [boolean -> int|string -> object data] *
* If the boolean is true, then the next value is an int, the magic number.
* If the boolean is false, then the next value is a string (the class name)
* The object data is what the object instance uses to populate its fields
*/ public static Externalizable read(ObjectInput in) throws IOException { try { boolean is_null=in.readBoolean(); if(is_null) return null; //see if we want to use a magic number boolean usemagic=in.readBoolean(); //the class that we will use to instantiate the object with Class extclass=null; if(usemagic) { //read the magic number short magic=in.readShort(); //from the magic number, get the class extclass=mConfigurator.get(magic); } else { //we don't have a magic number, read the class name String magic=in.readUTF(); //get the class, ie let the configurator load it extclass=mConfigurator.get(magic); }//end if //instantiate the object Externalizable newinstance=(Externalizable)extclass.newInstance(); //populate the object with its data newinstance.readExternal(in); //return the instance return newinstance; } catch(Throwable x) { if(x instanceof IOException) throw (IOException)x; else throw new IOException(x.toString()); } } /** * Writes an object to the ObjectOutput stream. * If possible, we will send over a magic number instead of the class name * so that we transfer less amount of data. * @param inst - an object instance to be serialized, can not be null * @param out - the ObjectOutput stream we will write the serialized data to */ public static void write(Externalizable inst, ObjectOutput out) throws IOException { boolean is_null=(inst == null); try { // if inst is a null value we write this first out.writeBoolean(is_null); if(is_null) return; //find out if we have a magic number for this class short magic=mConfigurator.getMagicNumber(inst.getClass()); //-1 means no magic number otherwise we have one if(magic != -1) { //true means we use a magic number out.writeBoolean(true); //write the magic number out.writeShort(magic); } else { //we don't have a magic number out.writeBoolean(false); //write the classname instead out.writeUTF(inst.getClass().getName()); }//end if //write the object data inst.writeExternal(out); } catch(Exception x) { if(x instanceof IOException) throw (IOException)x; else throw new java.io.IOException(x.toString()); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Util.java0000644000175000017500000024751611366547366024161 0ustar twernertwernerpackage org.jgroups.util; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import org.jgroups.*; import org.jgroups.auth.AuthToken; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.FD; import org.jgroups.protocols.PingHeader; import org.jgroups.protocols.UdpHeader; import org.jgroups.protocols.PingRsp; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import java.io.*; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; import java.security.MessageDigest; import java.text.NumberFormat; import java.util.*; /** * Collection of various utility routines that can not be assigned to other classes. * @author Bela Ban * @version $Id: Util.java,v 1.137.2.14 2009/12/17 16:30:05 belaban Exp $ */ public class Util { private static NumberFormat f; private static Map PRIMITIVE_TYPES=new HashMap(10); private static final byte TYPE_NULL = 0; private static final byte TYPE_STREAMABLE = 1; private static final byte TYPE_SERIALIZABLE = 2; private static final byte TYPE_BOOLEAN = 10; private static final byte TYPE_BYTE = 11; private static final byte TYPE_CHAR = 12; private static final byte TYPE_DOUBLE = 13; private static final byte TYPE_FLOAT = 14; private static final byte TYPE_INT = 15; private static final byte TYPE_LONG = 16; private static final byte TYPE_SHORT = 17; private static final byte TYPE_STRING = 18; // constants public static final int MAX_PORT=65535; // highest port allocatable static boolean resolve_dns=false; static boolean JGROUPS_COMPAT=false; /** * Global thread group to which all (most!) JGroups threads belong */ private static ThreadGroup GLOBAL_GROUP=new ThreadGroup("JGroups") { public void uncaughtException(Thread t, Throwable e) { LogFactory.getLog("org.jgroups").error("uncaught exception in " + t + " (thread group=" + GLOBAL_GROUP + " )", e); } }; public static ThreadGroup getGlobalThreadGroup() { return GLOBAL_GROUP; } static { /* Trying to get value of resolve_dns. PropertyPermission not granted if * running in an untrusted environment with JNLP */ try { resolve_dns=Boolean.valueOf(System.getProperty("resolve.dns", "false")).booleanValue(); } catch (SecurityException ex){ resolve_dns=false; } f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); try { String tmp=Util.getProperty(new String[]{Global.MARSHALLING_COMPAT}, null, null, false, "false"); JGROUPS_COMPAT=Boolean.valueOf(tmp).booleanValue(); } catch (SecurityException ex){ } PRIMITIVE_TYPES.put(Boolean.class, new Byte(TYPE_BOOLEAN)); PRIMITIVE_TYPES.put(Byte.class, new Byte(TYPE_BYTE)); PRIMITIVE_TYPES.put(Character.class, new Byte(TYPE_CHAR)); PRIMITIVE_TYPES.put(Double.class, new Byte(TYPE_DOUBLE)); PRIMITIVE_TYPES.put(Float.class, new Byte(TYPE_FLOAT)); PRIMITIVE_TYPES.put(Integer.class, new Byte(TYPE_INT)); PRIMITIVE_TYPES.put(Long.class, new Byte(TYPE_LONG)); PRIMITIVE_TYPES.put(Short.class, new Byte(TYPE_SHORT)); PRIMITIVE_TYPES.put(String.class, new Byte(TYPE_STRING)); } /** * Verifies that val is <= max memory * @param buf_name * @param val */ public static void checkBufferSize(String buf_name, long val) { // sanity check that max_credits doesn't exceed memory allocated to VM by -Xmx long max_mem=Runtime.getRuntime().maxMemory(); if(val > max_mem) { throw new IllegalArgumentException(buf_name + "(" + Util.printBytes(val) + ") exceeds max memory allocated to VM (" + Util.printBytes(max_mem) + ")"); } } public static int keyPress(String msg) { System.out.println(msg); try { return System.in.read(); } catch(IOException e) { return 0; } } public static void close(InputStream inp) { if(inp != null) try {inp.close();} catch(IOException e) {} } public static void close(OutputStream out) { if(out != null) { try {out.close();} catch(IOException e) {} } } public static void close(Socket s) { if(s != null) { try {s.close();} catch(Exception ex) {} } } public static void close(DatagramSocket my_sock) { if(my_sock != null) { try {my_sock.close();} catch(Throwable t) {} } } public static void close(Channel ch) { if(ch != null) { try {ch.close();} catch(Throwable t) {} } } public static void close(Channel ... channels) { if(channels != null) { for(Channel ch: channels) Util.close(ch); } } /** * Creates an object from a byte buffer */ public static Object objectFromByteBuffer(byte[] buffer) throws Exception { if(buffer == null) return null; if(JGROUPS_COMPAT) return oldObjectFromByteBuffer(buffer); return objectFromByteBuffer(buffer, 0, buffer.length); } public static Object objectFromByteBuffer(byte[] buffer, int offset, int length) throws Exception { if(buffer == null) return null; if(JGROUPS_COMPAT) return oldObjectFromByteBuffer(buffer, offset, length); Object retval=null; InputStream in=null; ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); byte b=(byte)in_stream.read(); try { switch(b) { case TYPE_NULL: return null; case TYPE_STREAMABLE: in=new DataInputStream(in_stream); retval=readGenericStreamable((DataInputStream)in); break; case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable in=new ContextObjectInputStream(in_stream); // changed Nov 29 2004 (bela) retval=((ContextObjectInputStream)in).readObject(); break; case TYPE_BOOLEAN: in=new DataInputStream(in_stream); retval=Boolean.valueOf(((DataInputStream)in).readBoolean()); break; case TYPE_BYTE: in=new DataInputStream(in_stream); retval=new Byte(((DataInputStream)in).readByte()); break; case TYPE_CHAR: in=new DataInputStream(in_stream); retval=new Character(((DataInputStream)in).readChar()); break; case TYPE_DOUBLE: in=new DataInputStream(in_stream); retval=new Double(((DataInputStream)in).readDouble()); break; case TYPE_FLOAT: in=new DataInputStream(in_stream); retval=new Float(((DataInputStream)in).readFloat()); break; case TYPE_INT: in=new DataInputStream(in_stream); retval=new Integer(((DataInputStream)in).readInt()); break; case TYPE_LONG: in=new DataInputStream(in_stream); retval=new Long(((DataInputStream)in).readLong()); break; case TYPE_SHORT: in=new DataInputStream(in_stream); retval=new Short(((DataInputStream)in).readShort()); break; case TYPE_STRING: in=new DataInputStream(in_stream); if(((DataInputStream)in).readBoolean()) { // large string ObjectInputStream ois=new ObjectInputStream(in); try { return ois.readObject(); } finally { ois.close(); } } else { retval=((DataInputStream)in).readUTF(); } break; default: throw new IllegalArgumentException("type " + b + " is invalid"); } return retval; } finally { Util.close(in); } } /** * Serializes/Streams an object into a byte buffer. * The object has to implement interface Serializable or Externalizable * or Streamable. Only Streamable objects are interoperable w/ jgroups-me */ public static byte[] objectToByteBuffer(Object obj) throws Exception { if(JGROUPS_COMPAT) return oldObjectToByteBuffer(obj); byte[] result=null; final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); if(obj == null) { out_stream.write(TYPE_NULL); out_stream.flush(); return out_stream.toByteArray(); } OutputStream out=null; Byte type; try { if(obj instanceof Streamable) { // use Streamable if we can out_stream.write(TYPE_STREAMABLE); out=new DataOutputStream(out_stream); writeGenericStreamable((Streamable)obj, (DataOutputStream)out); } else if((type=(Byte)PRIMITIVE_TYPES.get(obj.getClass())) != null) { out_stream.write(type.byteValue()); out=new DataOutputStream(out_stream); switch(type.byteValue()) { case TYPE_BOOLEAN: ((DataOutputStream)out).writeBoolean(((Boolean)obj).booleanValue()); break; case TYPE_BYTE: ((DataOutputStream)out).writeByte(((Byte)obj).byteValue()); break; case TYPE_CHAR: ((DataOutputStream)out).writeChar(((Character)obj).charValue()); break; case TYPE_DOUBLE: ((DataOutputStream)out).writeDouble(((Double)obj).doubleValue()); break; case TYPE_FLOAT: ((DataOutputStream)out).writeFloat(((Float)obj).floatValue()); break; case TYPE_INT: ((DataOutputStream)out).writeInt(((Integer)obj).intValue()); break; case TYPE_LONG: ((DataOutputStream)out).writeLong(((Long)obj).longValue()); break; case TYPE_SHORT: ((DataOutputStream)out).writeShort(((Short)obj).shortValue()); break; case TYPE_STRING: String str=(String)obj; if(str.length() > Short.MAX_VALUE) { ((DataOutputStream)out).writeBoolean(true); ObjectOutputStream oos=new ObjectOutputStream(out); try { oos.writeObject(str); } finally { oos.close(); } } else { ((DataOutputStream)out).writeBoolean(false); ((DataOutputStream)out).writeUTF(str); } break; default: throw new IllegalArgumentException("type " + type + " is invalid"); } } else { // will throw an exception if object is not serializable out_stream.write(TYPE_SERIALIZABLE); out=new ObjectOutputStream(out_stream); ((ObjectOutputStream)out).writeObject(obj); } } finally { Util.close(out); } result=out_stream.toByteArray(); return result; } /** For backward compatibility in JBoss 4.0.2 */ public static Object oldObjectFromByteBuffer(byte[] buffer) throws Exception { if(buffer == null) return null; return oldObjectFromByteBuffer(buffer, 0, buffer.length); } public static Object oldObjectFromByteBuffer(byte[] buffer, int offset, int length) throws Exception { if(buffer == null) return null; Object retval=null; try { // to read the object as an Externalizable ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); ObjectInputStream in=new ContextObjectInputStream(in_stream); // changed Nov 29 2004 (bela) retval=in.readObject(); in.close(); } catch(StreamCorruptedException sce) { try { // is it Streamable? ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); DataInputStream in=new DataInputStream(in_stream); retval=readGenericStreamable(in); in.close(); } catch(Exception ee) { IOException tmp=new IOException("unmarshalling failed"); tmp.initCause(ee); throw tmp; } } if(retval == null) return null; return retval; } /** * Serializes/Streams an object into a byte buffer. * The object has to implement interface Serializable or Externalizable * or Streamable. Only Streamable objects are interoperable w/ jgroups-me */ public static byte[] oldObjectToByteBuffer(Object obj) throws Exception { byte[] result=null; final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); if(obj instanceof Streamable) { // use Streamable if we can DataOutputStream out=new DataOutputStream(out_stream); writeGenericStreamable((Streamable)obj, out); out.close(); } else { ObjectOutputStream out=new ObjectOutputStream(out_stream); out.writeObject(obj); out.close(); } result=out_stream.toByteArray(); return result; } public static Streamable streamableFromByteBuffer(Class cl, byte[] buffer) throws Exception { if(buffer == null) return null; Streamable retval=null; ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer); DataInputStream in=new DataInputStream(in_stream); // changed Nov 29 2004 (bela) retval=(Streamable)cl.newInstance(); retval.readFrom(in); in.close(); return retval; } public static Streamable streamableFromByteBuffer(Class cl, byte[] buffer, int offset, int length) throws Exception { if(buffer == null) return null; Streamable retval=null; ByteArrayInputStream in_stream=new ByteArrayInputStream(buffer, offset, length); DataInputStream in=new DataInputStream(in_stream); // changed Nov 29 2004 (bela) retval=(Streamable)cl.newInstance(); retval.readFrom(in); in.close(); return retval; } public static byte[] streamableToByteBuffer(Streamable obj) throws Exception { byte[] result=null; final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); DataOutputStream out=new DataOutputStream(out_stream); obj.writeTo(out); result=out_stream.toByteArray(); out.close(); return result; } public static byte[] collectionToByteBuffer(Collection c) throws Exception { byte[] result=null; final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(512); DataOutputStream out=new DataOutputStream(out_stream); Util.writeAddresses(c, out); result=out_stream.toByteArray(); out.close(); return result; } public static int size(Address addr) { int retval=Global.BYTE_SIZE; // presence byte if(addr != null) retval+=addr.size() + Global.BYTE_SIZE; // plus type of address return retval; } public static void writeAuthToken(AuthToken token, DataOutputStream out) throws IOException{ Util.writeString(token.getName(), out); token.writeTo(out); } public static AuthToken readAuthToken(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { try{ String type = Util.readString(in); Object obj = Class.forName(type).newInstance(); AuthToken token = (AuthToken) obj; token.readFrom(in); return token; } catch(ClassNotFoundException cnfe){ return null; } } public static void writeAddress(Address addr, DataOutputStream out) throws IOException { if(addr == null) { out.writeBoolean(false); return; } out.writeBoolean(true); if(addr instanceof IpAddress) { // regular case, we don't need to include class information about the type of Address, e.g. JmsAddress out.writeBoolean(true); addr.writeTo(out); } else { out.writeBoolean(false); writeOtherAddress(addr, out); } } public static Address readAddress(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { Address addr=null; if(in.readBoolean() == false) return null; if(in.readBoolean()) { addr=new IpAddress(); addr.readFrom(in); } else { addr=readOtherAddress(in); } return addr; } private static Address readOtherAddress(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { ClassConfigurator conf; try { conf=ClassConfigurator.getInstance(false); } catch(ChannelException e) { IllegalAccessException new_ex=new IllegalAccessException(); new_ex.initCause(e); throw new_ex; } int b=in.read(); short magic_number; String classname; Class cl=null; Address addr; if(b == 1) { magic_number=in.readShort(); cl=conf.get(magic_number); } else { classname=in.readUTF(); cl=conf.get(classname); } addr=(Address)cl.newInstance(); addr.readFrom(in); return addr; } private static void writeOtherAddress(Address addr, DataOutputStream out) throws IOException { ClassConfigurator conf=null; try { conf=ClassConfigurator.getInstance(false); } catch(ChannelException e) { IOException new_ex=new IOException(); new_ex.initCause(e); throw new_ex; } short magic_number=conf != null? conf.getMagicNumber(addr.getClass()) : -1; // write the class info if(magic_number == -1) { out.write(0); out.writeUTF(addr.getClass().getName()); } else { out.write(1); out.writeShort(magic_number); } // write the data itself addr.writeTo(out); } /** * Writes a Vector of Addresses. Can contain 65K addresses at most * @param v A Collection
* @param out * @throws IOException */ public static void writeAddresses(Collection v, DataOutputStream out) throws IOException { if(v == null) { out.writeShort(-1); return; } out.writeShort(v.size()); Address addr; for(Iterator it=v.iterator(); it.hasNext();) { addr=(Address)it.next(); Util.writeAddress(addr, out); } } /** * * @param in * @param cl The type of Collection, e.g. Vector.class * @return Collection of Address objects * @throws IOException * @throws IllegalAccessException * @throws InstantiationException */ public static Collection
readAddresses(DataInputStream in, Class cl) throws IOException, IllegalAccessException, InstantiationException { short length=in.readShort(); if(length < 0) return null; Collection
retval=(Collection
)cl.newInstance(); Address addr; for(int i=0; i < length; i++) { addr=Util.readAddress(in); retval.add(addr); } return retval; } /** * Returns the marshalled size of a Collection of Addresses. * Assumes elements are of the same type ! * @param addrs Collection
* @return long size */ public static long size(Collection addrs) { int retval=Global.SHORT_SIZE; // number of elements if(addrs != null && !addrs.isEmpty()) { Address addr=(Address)addrs.iterator().next(); retval+=size(addr) * addrs.size(); } return retval; } public static void writeStreamable(Streamable obj, DataOutputStream out) throws IOException { if(obj == null) { out.writeBoolean(false); return; } out.writeBoolean(true); obj.writeTo(out); } public static Streamable readStreamable(Class clazz, DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { Streamable retval=null; if(in.readBoolean() == false) return null; retval=(Streamable)clazz.newInstance(); retval.readFrom(in); return retval; } public static void writeGenericStreamable(Streamable obj, DataOutputStream out) throws IOException { short magic_number; String classname; if(obj == null) { out.write(0); return; } try { out.write(1); magic_number=ClassConfigurator.getInstance(false).getMagicNumber(obj.getClass()); // write the magic number or the class name if(magic_number == -1) { out.writeBoolean(false); classname=obj.getClass().getName(); out.writeUTF(classname); } else { out.writeBoolean(true); out.writeShort(magic_number); } // write the contents obj.writeTo(out); } catch(ChannelException e) { throw new IOException("failed writing object of type " + obj.getClass() + " to stream: " + e.toString()); } } public static Streamable readGenericStreamable(DataInputStream in) throws IOException { Streamable retval=null; int b=in.read(); if(b == 0) return null; boolean use_magic_number=in.readBoolean(); String classname; Class clazz; try { if(use_magic_number) { short magic_number=in.readShort(); clazz=ClassConfigurator.getInstance(false).get(magic_number); if (clazz==null) { throw new ClassNotFoundException("Class for magic number "+magic_number+" cannot be found."); } } else { classname=in.readUTF(); clazz=ClassConfigurator.getInstance(false).get(classname); if (clazz==null) { throw new ClassNotFoundException(classname); } } retval=(Streamable)clazz.newInstance(); retval.readFrom(in); return retval; } catch(Exception ex) { throw new IOException("failed reading object: " + ex.toString()); } } public static void writeObject(Object obj, DataOutputStream out) throws Exception { if(obj == null || !(obj instanceof Streamable)) { byte[] buf=objectToByteBuffer(obj); out.writeShort(buf.length); out.write(buf, 0, buf.length); } else { out.writeShort(-1); writeGenericStreamable((Streamable)obj, out); } } public static Object readObject(DataInputStream in) throws Exception { short len=in.readShort(); Object retval=null; if(len == -1) { retval=readGenericStreamable(in); } else { byte[] buf=new byte[len]; in.readFully(buf, 0, len); retval=objectFromByteBuffer(buf); } return retval; } public static void writeString(String s, DataOutputStream out) throws IOException { if(s != null) { out.write(1); out.writeUTF(s); } else { out.write(0); } } public static String readString(DataInputStream in) throws IOException { int b=in.read(); if(b == 1) return in.readUTF(); return null; } public static void writeByteBuffer(byte[] buf, DataOutputStream out) throws IOException { if(buf != null) { out.write(1); out.writeInt(buf.length); out.write(buf, 0, buf.length); } else { out.write(0); } } public static byte[] readByteBuffer(DataInputStream in) throws IOException { int b=in.read(); if(b == 1) { b=in.readInt(); byte[] buf=new byte[b]; in.readFully(buf, 0, buf.length); return buf; } return null; } public static Buffer messageToByteBuffer(Message msg) throws IOException { ExposedByteArrayOutputStream output=new ExposedByteArrayOutputStream(512); DataOutputStream out=new DataOutputStream(output); out.writeBoolean(msg != null); if(msg != null) msg.writeTo(out); out.flush(); Buffer retval=new Buffer(output.getRawBuffer(), 0, output.size()); out.close(); output.close(); return retval; } public static Message byteBufferToMessage(byte[] buffer, int offset, int length) throws Exception { ByteArrayInputStream input=new ByteArrayInputStream(buffer, offset, length); DataInputStream in=new DataInputStream(input); if(!in.readBoolean()) return null; Message msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(in); return msg; } /** * Marshalls a list of messages. * @param xmit_list LinkedList * @return Buffer * @throws IOException */ public static Buffer msgListToByteBuffer(List xmit_list) throws IOException { ExposedByteArrayOutputStream output=new ExposedByteArrayOutputStream(512); DataOutputStream out=new DataOutputStream(output); Buffer retval=null; out.writeInt(xmit_list.size()); for(Message msg: xmit_list) { msg.writeTo(out); } out.flush(); retval=new Buffer(output.getRawBuffer(), 0, output.size()); out.close(); output.close(); return retval; } public static List byteBufferToMessageList(byte[] buffer, int offset, int length) throws Exception { List retval=null; ByteArrayInputStream input=new ByteArrayInputStream(buffer, offset, length); DataInputStream in=new DataInputStream(input); int size=in.readInt(); if(size == 0) return null; Message msg; retval=new LinkedList(); for(int i=0; i < size; i++) { msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(in); retval.add(msg); } return retval; } public static boolean match(Object obj1, Object obj2) { if(obj1 == null && obj2 == null) return true; if(obj1 != null) return obj1.equals(obj2); else return obj2.equals(obj1); } public static boolean match(long[] a1, long[] a2) { if(a1 == null && a2 == null) return true; if(a1 == null || a2 == null) return false; if(a1 == a2) // identity return true; // at this point, a1 != null and a2 != null if(a1.length != a2.length) return false; for(int i=0; i < a1.length; i++) { if(a1[i] != a2[i]) return false; } return true; } /** Sleep for timeout msecs. Returns when timeout has elapsed or thread was interrupted */ public static void sleep(long timeout) { try { Thread.sleep(timeout); } catch(Throwable e) { } } public static void sleep(long timeout, int nanos) { try { Thread.sleep(timeout, nanos); } catch(Throwable e) { } } /** * On most UNIX systems, the minimum sleep time is 10-20ms. Even if we specify sleep(1), the thread will * sleep for at least 10-20ms. On Windows, sleep() seems to be implemented as a busy sleep, that is the * thread never relinquishes control and therefore the sleep(x) is exactly x ms long. */ public static void sleep(long msecs, boolean busy_sleep) { if(!busy_sleep) { sleep(msecs); return; } long start=System.currentTimeMillis(); long stop=start + msecs; while(stop > start) { start=System.currentTimeMillis(); } } /** Returns a random value in the range [1 - range] */ public static long random(long range) { return (long)((Math.random() * range) % range) + 1; } /** Sleeps between 1 and timeout milliseconds, chosen randomly. Timeout must be > 1 */ public static void sleepRandom(long timeout) { if(timeout <= 0) { return; } long r=(int)((Math.random() * 100000) % timeout) + 1; sleep(r); } /** Sleeps between floor and ceiling milliseconds, chosen randomly */ public static void sleepRandom(long floor, long ceiling) { if(ceiling - floor<= 0) { return; } long diff = ceiling - floor; long r=(int)((Math.random() * 100000) % diff) + floor; sleep(r); } /** Tosses a coin weighted with probability and returns true or false. Example: if probability=0.8, chances are that in 80% of all cases, true will be returned and false in 20%. */ public static boolean tossWeightedCoin(double probability) { long r=random(100); long cutoff=(long)(probability * 100); return r < cutoff; } public static String getHostname() { try { return InetAddress.getLocalHost().getHostName(); } catch(Exception ex) { } return "localhost"; } public static void dumpStack(boolean exit) { try { throw new Exception("Dumping stack:"); } catch(Exception e) { e.printStackTrace(); if(exit) System.exit(0); } } public static String dumpThreads() { StringBuilder sb=new StringBuilder(); ThreadMXBean bean=ManagementFactory.getThreadMXBean(); long[] ids=bean.getAllThreadIds(); ThreadInfo[] threads=bean.getThreadInfo(ids, 20); for(int i=0; i < threads.length; i++) { ThreadInfo info=threads[i]; if(info == null) continue; sb.append(info.getThreadName()).append(":\n"); StackTraceElement[] stack_trace=info.getStackTrace(); for(int j=0; j < stack_trace.length; j++) { StackTraceElement el=stack_trace[j]; sb.append("at ").append(el.getClassName()).append(".").append(el.getMethodName()); sb.append("(").append(el.getFileName()).append(":").append(el.getLineNumber()).append(")"); sb.append("\n"); } sb.append("\n\n"); } return sb.toString(); } public static boolean interruptAndWaitToDie(Thread t) { return interruptAndWaitToDie(t, Global.THREAD_SHUTDOWN_WAIT_TIME); } public static boolean interruptAndWaitToDie(Thread t, long timeout) { if(t == null) throw new IllegalArgumentException("Thread can not be null"); t.interrupt(); // interrupts the sleep() try { t.join(timeout); } catch(InterruptedException e){ Thread.currentThread().interrupt(); // set interrupt flag again } return t.isAlive(); } /** * Debugging method used to dump the content of a protocol queue in a * condensed form. Useful to follow the evolution of the queue's content in * time. */ public static String dumpQueue(Queue q) { StringBuilder sb=new StringBuilder(); LinkedList values=q.values(); if(values.isEmpty()) { sb.append("empty"); } else { for(Iterator it=values.iterator(); it.hasNext();) { Object o=it.next(); String s=null; if(o instanceof Event) { Event event=(Event)o; int type=event.getType(); s=Event.type2String(type); if(type == Event.VIEW_CHANGE) s+=" " + event.getArg(); if(type == Event.MSG) s+=" " + event.getArg(); if(type == Event.MSG) { s+="["; Message m=(Message)event.getArg(); Map headers=new HashMap(m.getHeaders()); for(Iterator i=headers.keySet().iterator(); i.hasNext();) { Object headerKey=i.next(); Object value=headers.get(headerKey); String headerToString=null; if(value instanceof FD.FdHeader) { headerToString=value.toString(); } else if(value instanceof PingHeader) { headerToString=headerKey + "-"; if(((PingHeader)value).type == PingHeader.GET_MBRS_REQ) { headerToString+="GMREQ"; } else if(((PingHeader)value).type == PingHeader.GET_MBRS_RSP) { headerToString+="GMRSP"; } else { headerToString+="UNKNOWN"; } } else { headerToString=headerKey + "-" + (value == null ? "null" : value.toString()); } s+=headerToString; if(i.hasNext()) { s+=","; } } s+="]"; } } else { s=o.toString(); } sb.append(s).append("\n"); } } return sb.toString(); } /** * Use with caution: lots of overhead */ public static String printStackTrace(Throwable t) { StringWriter s=new StringWriter(); PrintWriter p=new PrintWriter(s); t.printStackTrace(p); return s.toString(); } public static String getStackTrace(Throwable t) { return printStackTrace(t); } public static String print(Throwable t) { return printStackTrace(t); } public static void crash() { System.exit(-1); } public static String printEvent(Event evt) { Message msg; if(evt.getType() == Event.MSG) { msg=(Message)evt.getArg(); if(msg != null) { if(msg.getLength() > 0) return printMessage(msg); else return msg.printObjectHeaders(); } } return evt.toString(); } /** Tries to read an object from the message's buffer and prints it */ public static String printMessage(Message msg) { if(msg == null) return ""; if(msg.getLength() == 0) return null; try { return msg.getObject().toString(); } catch(Exception e) { // it is not an object return ""; } } public static String mapToString(Map map) { if(map == null) return "null"; StringBuilder sb=new StringBuilder(); for(Map.Entry entry: map.entrySet()) { Object key=entry.getKey(); Object val=entry.getValue(); sb.append(key).append("="); if(val == null) sb.append("null"); else sb.append(val); sb.append("\n"); } return sb.toString(); } /** Tries to read a MethodCall object from the message's buffer and prints it. Returns empty string if object is not a method call */ public static String printMethodCall(Message msg) { Object obj; if(msg == null) return ""; if(msg.getLength() == 0) return ""; try { obj=msg.getObject(); return obj.toString(); } catch(Exception e) { // it is not an object return ""; } } public static void printThreads() { Thread threads[]=new Thread[Thread.activeCount()]; Thread.enumerate(threads); System.out.println("------- Threads -------"); for(int i=0; i < threads.length; i++) { System.out.println("#" + i + ": " + threads[i]); } System.out.println("------- Threads -------\n"); } public static String activeThreads() { StringBuilder sb=new StringBuilder(); Thread threads[]=new Thread[Thread.activeCount()]; Thread.enumerate(threads); sb.append("------- Threads -------\n"); for(int i=0; i < threads.length; i++) { sb.append("#").append(i).append(": ").append(threads[i]).append('\n'); } sb.append("------- Threads -------\n"); return sb.toString(); } public static String printBytes(long bytes) { double tmp; if(bytes < 1000) return bytes + "b"; if(bytes < 1000000) { tmp=bytes / 1000.0; return f.format(tmp) + "KB"; } if(bytes < 1000000000) { tmp=bytes / 1000000.0; return f.format(tmp) + "MB"; } else { tmp=bytes / 1000000000.0; return f.format(tmp) + "GB"; } } public static String printBytes(double bytes) { double tmp; if(bytes < 1000) return bytes + "b"; if(bytes < 1000000) { tmp=bytes / 1000.0; return f.format(tmp) + "KB"; } if(bytes < 1000000000) { tmp=bytes / 1000000.0; return f.format(tmp) + "MB"; } else { tmp=bytes / 1000000000.0; return f.format(tmp) + "GB"; } } /** Fragments a byte buffer into smaller fragments of (max.) frag_size. Example: a byte buffer of 1024 bytes and a frag_size of 248 gives 4 fragments of 248 bytes each and 1 fragment of 32 bytes. @return An array of byte buffers (byte[]). */ public static byte[][] fragmentBuffer(byte[] buf, int frag_size, final int length) { byte[] retval[]; int accumulated_size=0; byte[] fragment; int tmp_size=0; int num_frags; int index=0; num_frags=length % frag_size == 0 ? length / frag_size : length / frag_size + 1; retval=new byte[num_frags][]; while(accumulated_size < length) { if(accumulated_size + frag_size <= length) tmp_size=frag_size; else tmp_size=length - accumulated_size; fragment=new byte[tmp_size]; System.arraycopy(buf, accumulated_size, fragment, 0, tmp_size); retval[index++]=fragment; accumulated_size+=tmp_size; } return retval; } public static byte[][] fragmentBuffer(byte[] buf, int frag_size) { return fragmentBuffer(buf, frag_size, buf.length); } /** * Given a buffer and a fragmentation size, compute a list of fragmentation offset/length pairs, and * return them in a list. Example:
* Buffer is 10 bytes, frag_size is 4 bytes. Return value will be ({0,4}, {4,4}, {8,2}). * This is a total of 3 fragments: the first fragment starts at 0, and has a length of 4 bytes, the second fragment * starts at offset 4 and has a length of 4 bytes, and the last fragment starts at offset 8 and has a length * of 2 bytes. * @param frag_size * @return List. A List of offset/length pairs */ public static java.util.List computeFragOffsets(int offset, int length, int frag_size) { java.util.List retval=new ArrayList(); long total_size=length + offset; int index=offset; int tmp_size=0; Range r; while(index < total_size) { if(index + frag_size <= total_size) tmp_size=frag_size; else tmp_size=(int)(total_size - index); r=new Range(index, tmp_size); retval.add(r); index+=tmp_size; } return retval; } public static java.util.List computeFragOffsets(byte[] buf, int frag_size) { return computeFragOffsets(0, buf.length, frag_size); } /** Concatenates smaller fragments into entire buffers. @param fragments An array of byte buffers (byte[]) @return A byte buffer */ public static byte[] defragmentBuffer(byte[] fragments[]) { int total_length=0; byte[] ret; int index=0; if(fragments == null) return null; for(int i=0; i < fragments.length; i++) { if(fragments[i] == null) continue; total_length+=fragments[i].length; } ret=new byte[total_length]; for(int i=0; i < fragments.length; i++) { if(fragments[i] == null) continue; System.arraycopy(fragments[i], 0, ret, index, fragments[i].length); index+=fragments[i].length; } return ret; } public static void printFragments(byte[] frags[]) { for(int i=0; i < frags.length; i++) System.out.println('\'' + new String(frags[i]) + '\''); } public static String printListWithDelimiter(Collection list, String delimiter) { boolean first=true; StringBuilder sb=new StringBuilder(); for(T el: list) { if(first) { first=false; } else { sb.append(delimiter); } sb.append(el); } return sb.toString(); } // /** // Peeks for view on the channel until n views have been received or timeout has elapsed. // Used to determine the view in which we want to start work. Usually, we start as only // member in our own view (1st view) and the next view (2nd view) will be the full view // of all members, or a timeout if we're the first member. If a non-view (a message or // block) is received, the method returns immediately. // @param channel The channel used to peek for views. Has to be operational. // @param number_of_views The number of views to wait for. 2 is a good number to ensure that, // if there are other members, we start working with them included in our view. // @param timeout Number of milliseconds to wait until view is forced to return. A value // of <= 0 means wait forever. // */ // public static View peekViews(Channel channel, int number_of_views, long timeout) { // View retval=null; // Object obj=null; // int num=0; // long start_time=System.currentTimeMillis(); // if(timeout <= 0) { // while(true) { // try { // obj=channel.peek(0); // if(obj == null || !(obj instanceof View)) // break; // else { // retval=(View)channel.receive(0); // num++; // if(num >= number_of_views) // break; // } // } // catch(Exception ex) { // break; // } // } // } // else { // while(timeout > 0) { // try { // obj=channel.peek(timeout); // if(obj == null || !(obj instanceof View)) // break; // else { // retval=(View)channel.receive(timeout); // num++; // if(num >= number_of_views) // break; // } // } // catch(Exception ex) { // break; // } // timeout=timeout - (System.currentTimeMillis() - start_time); // } // } // return retval; // } public static String array2String(long[] array) { StringBuilder ret=new StringBuilder("["); if(array != null) { for(int i=0; i < array.length; i++) ret.append(array[i]).append(" "); } ret.append(']'); return ret.toString(); } public static String array2String(short[] array) { StringBuilder ret=new StringBuilder("["); if(array != null) { for(int i=0; i < array.length; i++) ret.append(array[i]).append(" "); } ret.append(']'); return ret.toString(); } public static String array2String(int[] array) { StringBuilder ret=new StringBuilder("["); if(array != null) { for(int i=0; i < array.length; i++) ret.append(array[i]).append(" "); } ret.append(']'); return ret.toString(); } public static String array2String(boolean[] array) { StringBuilder ret=new StringBuilder("["); if(array != null) { for(int i=0; i < array.length; i++) ret.append(array[i]).append(" "); } ret.append(']'); return ret.toString(); } public static String array2String(Object[] array) { StringBuilder ret=new StringBuilder("["); if(array != null) { for(int i=0; i < array.length; i++) ret.append(array[i]).append(" "); } ret.append(']'); return ret.toString(); } /** Returns true if all elements of c match obj */ public static boolean all(Collection c, Object obj) { for(Iterator iterator=c.iterator(); iterator.hasNext();) { Object o=iterator.next(); if(!o.equals(obj)) return false; } return true; } /** * Selects a random subset of members according to subset_percentage and returns them. * Picks no member twice from the same membership. If the percentage is smaller than 1 -> picks 1 member. */ public static Vector pickSubset(Vector members, double subset_percentage) { Vector ret=new Vector(), tmp_mbrs; int num_mbrs=members.size(), subset_size, index; if(num_mbrs == 0) return ret; subset_size=(int)Math.ceil(num_mbrs * subset_percentage); tmp_mbrs=(Vector)members.clone(); for(int i=subset_size; i > 0 && !tmp_mbrs.isEmpty(); i--) { index=(int)((Math.random() * num_mbrs) % tmp_mbrs.size()); ret.addElement(tmp_mbrs.elementAt(index)); tmp_mbrs.removeElementAt(index); } return ret; } public static Object pickRandomElement(List list) { if(list == null) return null; int size=list.size(); int index=(int)((Math.random() * size * 10) % size); return list.get(index); } public static Object pickRandomElement(Object[] array) { if(array == null) return null; int size=array.length; int index=(int)((Math.random() * size * 10) % size); return array[index]; } /** * Returns all members that left between 2 views. All members that are element of old_mbrs but not element of * new_mbrs are returned. */ public static Vector
determineLeftMembers(Vector
old_mbrs, Vector
new_mbrs) { Vector
retval=new Vector
(); Address mbr; if(old_mbrs == null || new_mbrs == null) return retval; for(int i=0; i < old_mbrs.size(); i++) { mbr=old_mbrs.elementAt(i); if(!new_mbrs.contains(mbr)) retval.addElement(mbr); } return retval; } public static String printMembers(Vector v) { StringBuilder sb=new StringBuilder("("); boolean first=true; Object el; if(v != null) { for(int i=0; i < v.size(); i++) { if(!first) sb.append(", "); else first=false; el=v.elementAt(i); sb.append(el); } } sb.append(')'); return sb.toString(); } public static String printPingRsps(List rsps) { StringBuilder sb=new StringBuilder(); if(rsps != null) { int total=rsps.size(); int servers=0, clients=0, coords=0; for(PingRsp rsp: rsps) { if(rsp.isCoord()) coords++; if(rsp.isServer()) servers++; else clients++; } sb.append(total + " total (" + servers + " servers (" + coords + " coord), " + clients + " clients)"); } return sb.toString(); } /** Makes sure that we detect when a peer connection is in the closed state (not closed while we send data, but before we send data). Two writes ensure that, if the peer closed the connection, the first write will send the peer from FIN to RST state, and the second will cause a signal (IOException). */ public static void doubleWrite(byte[] buf, OutputStream out) throws Exception { if(buf.length > 1) { out.write(buf, 0, 1); out.write(buf, 1, buf.length - 1); } else { out.write(buf, 0, 0); out.write(buf); } } /** Makes sure that we detect when a peer connection is in the closed state (not closed while we send data, but before we send data). Two writes ensure that, if the peer closed the connection, the first write will send the peer from FIN to RST state, and the second will cause a signal (IOException). */ public static void doubleWrite(byte[] buf, int offset, int length, OutputStream out) throws Exception { if(length > 1) { out.write(buf, offset, 1); out.write(buf, offset+1, length - 1); } else { out.write(buf, offset, 0); out.write(buf, offset, length); } } /** * if we were to register for OP_WRITE and send the remaining data on * readyOps for this channel we have to either block the caller thread or * queue the message buffers that may arrive while waiting for OP_WRITE. * Instead of the above approach this method will continuously write to the * channel until the buffer sent fully. */ public static void writeFully(ByteBuffer buf, WritableByteChannel out) throws IOException { int written = 0; int toWrite = buf.limit(); while (written < toWrite) { written += out.write(buf); } } // /* double writes are not required.*/ // public static void doubleWriteBuffer( // ByteBuffer buf, // WritableByteChannel out) // throws Exception // { // if (buf.limit() > 1) // { // int actualLimit = buf.limit(); // buf.limit(1); // writeFully(buf,out); // buf.limit(actualLimit); // writeFully(buf,out); // } // else // { // buf.limit(0); // writeFully(buf,out); // buf.limit(1); // writeFully(buf,out); // } // } public static long sizeOf(String classname) { Object inst; byte[] data; try { inst=Util.loadClass(classname, null).newInstance(); data=Util.objectToByteBuffer(inst); return data.length; } catch(Exception ex) { return -1; } } public static long sizeOf(Object inst) { byte[] data; try { data=Util.objectToByteBuffer(inst); return data.length; } catch(Exception ex) { return -1; } } public static int sizeOf(Streamable inst) { byte[] data; ByteArrayOutputStream output; DataOutputStream out; try { output=new ByteArrayOutputStream(); out=new DataOutputStream(output); inst.writeTo(out); out.flush(); data=output.toByteArray(); return data.length; } catch(Exception ex) { return -1; } } /** * Tries to load the class from the current thread's context class loader. If * not successful, tries to load the class from the current instance. * @param classname Desired class. * @param clazz Class object used to obtain a class loader * if no context class loader is available. * @return Class, or null on failure. */ public static Class loadClass(String classname, Class clazz) throws ClassNotFoundException { ClassLoader loader; try { loader=Thread.currentThread().getContextClassLoader(); if(loader != null) { return loader.loadClass(classname); } } catch(Throwable t) { } if(clazz != null) { try { loader=clazz.getClassLoader(); if(loader != null) { return loader.loadClass(classname); } } catch(Throwable t) { } } try { loader=ClassLoader.getSystemClassLoader(); if(loader != null) { return loader.loadClass(classname); } } catch(Throwable t) { } throw new ClassNotFoundException(classname); } public static InputStream getResourceAsStream(String name, Class clazz) { ClassLoader loader; InputStream retval=null; try { loader=Thread.currentThread().getContextClassLoader(); if(loader != null) { retval=loader.getResourceAsStream(name); if(retval != null) return retval; } } catch(Throwable t) { } if(clazz != null) { try { loader=clazz.getClassLoader(); if(loader != null) { retval=loader.getResourceAsStream(name); if(retval != null) return retval; } } catch(Throwable t) { } } try { loader=ClassLoader.getSystemClassLoader(); if(loader != null) { return loader.getResourceAsStream(name); } } catch(Throwable t) { } return retval; } /** Checks whether 2 Addresses are on the same host */ public static boolean sameHost(Address one, Address two) { InetAddress a, b; String host_a, host_b; if(one == null || two == null) return false; if(!(one instanceof IpAddress) || !(two instanceof IpAddress)) { return false; } a=((IpAddress)one).getIpAddress(); b=((IpAddress)two).getIpAddress(); if(a == null || b == null) return false; host_a=a.getHostAddress(); host_b=b.getHostAddress(); // System.out.println("host_a=" + host_a + ", host_b=" + host_b); return host_a.equals(host_b); } public static boolean fileExists(String fname) { return (new File(fname)).exists(); } /** * Parses comma-delimited longs; e.g., 2000,4000,8000. * Returns array of long, or null. */ public static long[] parseCommaDelimitedLongs(String s) { StringTokenizer tok; Vector v=new Vector(); Long l; long[] retval=null; if(s == null) return null; tok=new StringTokenizer(s, ","); while(tok.hasMoreTokens()) { l=new Long(tok.nextToken()); v.addElement(l); } if(v.isEmpty()) return null; retval=new long[v.size()]; for(int i=0; i < v.size(); i++) retval[i]=((Long)v.elementAt(i)).longValue(); return retval; } /** e.g. "bela,jeannette,michelle" --> List{"bela", "jeannette", "michelle"} */ public static List parseCommaDelimitedStrings(String l) { return parseStringList(l, ","); } public static List parseStringList(String l, String separator) { List tmp=new LinkedList(); StringTokenizer tok=new StringTokenizer(l, separator); String t; while(tok.hasMoreTokens()) { t=tok.nextToken(); tmp.add(t.trim()); } return tmp; } public static int parseInt(Properties props,String property,int defaultValue) { int result = defaultValue; String str=props.getProperty(property); if(str != null) { result=Integer.parseInt(str); props.remove(property); } return result; } public static long parseLong(Properties props,String property,long defaultValue) { long result = defaultValue; String str=props.getProperty(property); if(str != null) { result=Integer.parseInt(str); props.remove(property); } return result; } public static boolean parseBoolean(Properties props,String property,boolean defaultValue) { boolean result = defaultValue; String str=props.getProperty(property); if(str != null) { result=str.equalsIgnoreCase("true"); props.remove(property); } return result; } public static InetAddress parseBindAddress(Properties props, String property) throws UnknownHostException { InetAddress bind_addr=null; boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); String str=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", ignore_systemprops, null); if(str != null) { bind_addr=InetAddress.getByName(str); props.remove(property); } return bind_addr; } /** * * @param s * @return List */ public static List parseInterfaceList(String s) throws Exception { List interfaces=new ArrayList(10); if(s == null) return null; StringTokenizer tok=new StringTokenizer(s, ","); String interface_name; NetworkInterface intf; while(tok.hasMoreTokens()) { interface_name=tok.nextToken(); // try by name first (e.g. (eth0") intf=NetworkInterface.getByName(interface_name); // next try by IP address or symbolic name if(intf == null) intf=NetworkInterface.getByInetAddress(InetAddress.getByName(interface_name)); if(intf == null) throw new Exception("interface " + interface_name + " not found"); if(!interfaces.contains(intf)) { interfaces.add(intf); } } return interfaces; } public static String print(List interfaces) { StringBuilder sb=new StringBuilder(); boolean first=true; for(NetworkInterface intf: interfaces) { if(first) { first=false; } else { sb.append(", "); } sb.append(intf.getName()); } return sb.toString(); } public static String shortName(String hostname) { int index; StringBuilder sb=new StringBuilder(); if(hostname == null) return null; index=hostname.indexOf('.'); if(index > 0 && !Character.isDigit(hostname.charAt(0))) sb.append(hostname.substring(0, index)); else sb.append(hostname); return sb.toString(); } public static String shortName(InetAddress hostname) { if(hostname == null) return null; StringBuilder sb=new StringBuilder(); if(resolve_dns) sb.append(hostname.getHostName()); else sb.append(hostname.getHostAddress()); return sb.toString(); } public static boolean startFlush(Channel c, List
flushParticipants, int numberOfAttempts, long randomSleepTimeoutFloor,long randomSleepTimeoutCeiling) { boolean successfulFlush = false; int attemptCount = 0; while(attemptCount < numberOfAttempts){ successfulFlush = c.startFlush(flushParticipants, false); if(successfulFlush) break; Util.sleepRandom(randomSleepTimeoutFloor,randomSleepTimeoutCeiling); attemptCount++; } return successfulFlush; } public static boolean startFlush(Channel c, List
flushParticipants) { return startFlush(c,flushParticipants,4,1000,5000); } public static boolean startFlush(Channel c, int numberOfAttempts, long randomSleepTimeoutFloor,long randomSleepTimeoutCeiling) { boolean successfulFlush = false; int attemptCount = 0; while(attemptCount < numberOfAttempts){ successfulFlush = c.startFlush(false); if(successfulFlush) break; Util.sleepRandom(randomSleepTimeoutFloor,randomSleepTimeoutCeiling); attemptCount++; } return successfulFlush; } public static boolean startFlush(Channel c) { return startFlush(c,4,1000,5000); } /** Finds first available port starting at start_port and returns server socket */ public static ServerSocket createServerSocket(int start_port) throws IOException{ ServerSocket ret=null; while(true) { try { ret=new ServerSocket(start_port); } catch(BindException bind_ex) { start_port++; continue; } catch(IOException io_ex) { } break; } if(ret == null) throw new IOException("Could not create server socket, start port:" +start_port); return ret; } public static ServerSocket createServerSocket(InetAddress bind_addr, int start_port) throws IOException{ ServerSocket ret=null; while(true) { try { ret=new ServerSocket(start_port, 50, bind_addr); } catch(BindException bind_ex) { start_port++; continue; } catch(IOException io_ex) { } break; } if(ret == null) throw new IOException("Could not create server socket, start port:" +start_port); return ret; } /** * Creates a DatagramSocket bound to addr. If addr is null, socket won't be bound. If address is already in use, * start_port will be incremented until a socket can be created. * @param addr The InetAddress to which the socket should be bound. If null, the socket will not be bound. * @param port The port which the socket should use. If 0, a random port will be used. If > 0, but port is already * in use, it will be incremented until an unused port is found, or until MAX_PORT is reached. */ public static DatagramSocket createDatagramSocket(InetAddress addr, int port) throws Exception { DatagramSocket sock=null; if(addr == null) { if(port == 0) { return new DatagramSocket(); } else { while(port < MAX_PORT) { try { return new DatagramSocket(port); } catch(BindException bind_ex) { // port already used port++; } catch(Exception ex) { throw ex; } } } } else { if(port == 0) port=1024; while(port < MAX_PORT) { try { return new DatagramSocket(port, addr); } catch(BindException bind_ex) { // port already used port++; } catch(Exception ex) { throw ex; } } } if(sock == null) throw new IOException ("Could not create udp socket, port = "+ port); return sock; // will never be reached, but the stupid compiler didn't figure it out... } public static MulticastSocket createMulticastSocket(int port) throws IOException { return createMulticastSocket(null, port, null); } public static MulticastSocket createMulticastSocket(InetAddress mcast_addr, int port, Log log) throws IOException { if(mcast_addr != null && !mcast_addr.isMulticastAddress()) { if(log != null && log.isWarnEnabled()) log.warn("mcast_addr (" + mcast_addr + ") is not a multicast address, will be ignored"); return new MulticastSocket(port); } SocketAddress saddr=new InetSocketAddress(mcast_addr, port); MulticastSocket retval=null; try { retval=new MulticastSocket(saddr); } catch(IOException ex) { if(log != null && log.isWarnEnabled()) { StringBuilder sb=new StringBuilder(); String type=mcast_addr != null ? mcast_addr instanceof Inet4Address? "IPv4" : "IPv6" : "n/a"; sb.append("could not bind to " + mcast_addr + " (" + type + " address)"); sb.append("; make sure your mcast_addr is of the same type as the preferred IP stack (IPv4 or IPv6)"); sb.append(" by checking the value of the system properties java.net.preferIPv4Stack and java.net.preferIPv6Addresses."); sb.append("\nWill ignore mcast_addr, but this may lead to cross talking " + "(see http://www.jboss.com/wiki/Edit.jsp?page=CrossTalking for details). "); sb.append("\nException was: " + ex); log.warn(sb); } } if(retval == null) retval=new MulticastSocket(port); return retval; } /** * Returns the address of the interface to use defined by bind_addr and bind_interface * @param props * @return * @throws UnknownHostException * @throws SocketException */ public static InetAddress getBindAddress(Properties props) throws UnknownHostException, SocketException { boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); String bind_addr=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", ignore_systemprops, null); String bind_interface=Util.getProperty(new String[]{Global.BIND_INTERFACE, null}, props, "bind_interface", ignore_systemprops, null); InetAddress retval=null, bind_addr_host=null; if(bind_addr != null) { bind_addr_host=InetAddress.getByName(bind_addr); } if(bind_interface != null) { NetworkInterface intf=NetworkInterface.getByName(bind_interface); if(intf != null) { for(Enumeration addresses=intf.getInetAddresses(); addresses.hasMoreElements();) { InetAddress addr=addresses.nextElement(); if(bind_addr == null) { retval=addr; break; } else { if(bind_addr_host != null) { if(bind_addr_host.equals(addr)) { retval=addr; break; } } else if(addr.getHostAddress().trim().equalsIgnoreCase(bind_addr)) { retval=addr; break; } } } } else { throw new UnknownHostException("network interface " + bind_interface + " not found"); } } if(retval == null) { retval=bind_addr != null? InetAddress.getByName(bind_addr) : InetAddress.getLocalHost(); } props.remove("bind_addr"); props.remove("bind_interface"); return retval; } public static boolean checkForLinux() { return checkForPresence("os.name", "linux"); } public static boolean checkForHp() { return checkForPresence("os.name", "hp"); } public static boolean checkForSolaris() { return checkForPresence("os.name", "sun"); } public static boolean checkForWindows() { return checkForPresence("os.name", "win"); } private static boolean checkForPresence(String key, String value) { try { String tmp=System.getProperty(key); return tmp != null && tmp.trim().toLowerCase().startsWith(value); } catch(Throwable t) { return false; } } public static void prompt(String s) { System.out.println(s); System.out.flush(); try { while(System.in.available() > 0) System.in.read(); System.in.read(); } catch(IOException e) { e.printStackTrace(); } } public static int getJavaVersion() { String version=System.getProperty("java.version"); int retval=0; if(version != null) { if(version.startsWith("1.2")) return 12; if(version.startsWith("1.3")) return 13; if(version.startsWith("1.4")) return 14; if(version.startsWith("1.5")) return 15; if(version.startsWith("5")) return 15; if(version.startsWith("1.6")) return 16; if(version.startsWith("6")) return 16; } return retval; } public static Vector unmodifiableVector(Vector v) { if(v == null) return null; return new UnmodifiableVector(v); } public static String memStats(boolean gc) { StringBuilder sb=new StringBuilder(); Runtime rt=Runtime.getRuntime(); if(gc) rt.gc(); long free_mem, total_mem, used_mem; free_mem=rt.freeMemory(); total_mem=rt.totalMemory(); used_mem=total_mem - free_mem; sb.append("Free mem: ").append(free_mem).append("\nUsed mem: ").append(used_mem); sb.append("\nTotal mem: ").append(total_mem); return sb.toString(); } // public static InetAddress getFirstNonLoopbackAddress() throws SocketException { // Enumeration en=NetworkInterface.getNetworkInterfaces(); // while(en.hasMoreElements()) { // NetworkInterface i=(NetworkInterface)en.nextElement(); // for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { // InetAddress addr=(InetAddress)en2.nextElement(); // if(!addr.isLoopbackAddress()) // return addr; // } // } // return null; // } public static InetAddress getFirstNonLoopbackAddress() throws SocketException { Enumeration en=NetworkInterface.getNetworkInterfaces(); boolean preferIpv4=Boolean.getBoolean("java.net.preferIPv4Stack"); boolean preferIPv6=Boolean.getBoolean("java.net.preferIPv6Addresses"); while(en.hasMoreElements()) { NetworkInterface i=(NetworkInterface)en.nextElement(); for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr=(InetAddress)en2.nextElement(); if(!addr.isLoopbackAddress()) { if(addr instanceof Inet4Address) { if(preferIPv6) continue; return addr; } if(addr instanceof Inet6Address) { if(preferIpv4) continue; return addr; } } } } return null; } public static InetAddress getFirstNonLoopbackIPv6Address() throws SocketException { Enumeration en=NetworkInterface.getNetworkInterfaces(); while(en.hasMoreElements()) { NetworkInterface i=(NetworkInterface)en.nextElement(); for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr=(InetAddress)en2.nextElement(); if(!addr.isLoopbackAddress()) { if(addr instanceof Inet4Address) { continue; } if(addr instanceof Inet6Address) { return addr; } } } } return null; } public static List getAllAvailableInterfaces() throws SocketException { List retval=new ArrayList(10); NetworkInterface intf; for(Enumeration en=NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { intf=(NetworkInterface)en.nextElement(); retval.add(intf); } return retval; } /** * Returns a value associated wither with one or more system properties, or found in the props map * @param system_props * @param props List of properties read from the configuration file * @param prop_name The name of the property, will be removed from props if found * @param ignore_sysprops If true, system properties are not used and the values will only be retrieved from * props (not system_props) * @param default_value Used to return a default value if the properties or system properties didn't have the value * @return The value, or null if not found */ public static String getProperty(String[] system_props, Properties props, String prop_name, boolean ignore_sysprops, String default_value) { String retval=null; if(props != null && prop_name != null) { retval=props.getProperty(prop_name); props.remove(prop_name); } if(!ignore_sysprops) { String tmp, prop; if(system_props != null) { for(int i=0; i < system_props.length; i++) { prop=system_props[i]; if(prop != null) { try { tmp=System.getProperty(prop); if(tmp != null) return tmp; // system properties override config file definitions } catch(SecurityException ex) {} } } } } if(retval == null) return default_value; return retval; } public static boolean isBindAddressPropertyIgnored() { try { String tmp=System.getProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY); if(tmp == null) { tmp=System.getProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD); if(tmp == null) return false; } tmp=tmp.trim().toLowerCase(); return !(tmp.equals("false") || tmp.equals("no") || tmp.equals("off")) && (tmp.equals("true") || tmp.equals("yes") || tmp.equals("on")); } catch(SecurityException ex) { return false; } } public static MBeanServer getMBeanServer() { ArrayList servers = MBeanServerFactory.findMBeanServer(null); if (servers != null && !servers.isEmpty()) { // return 'jboss' server if available for (int i = 0; i < servers.size(); i++) { MBeanServer srv = (MBeanServer) servers.get(i); if ("jboss".equalsIgnoreCase(srv.getDefaultDomain())) return srv; } // return first available server return (MBeanServer) servers.get(0); } else { //if it all fails, get the default server return ManagementFactory.getPlatformMBeanServer(); } } public static String getProperty(Protocol prot, String prop_name) { if(prot == null) return null; String name=prot.getProperties().getProperty(prop_name); return name == null? name : name.trim(); } /* public static void main(String[] args) { DatagramSocket sock; InetAddress addr=null; int port=0; for(int i=0; i < args.length; i++) { if(args[i].equals("-help")) { System.out.println("Util [-help] [-addr] [-port]"); return; } if(args[i].equals("-addr")) { try { addr=InetAddress.getByName(args[++i]); continue; } catch(Exception ex) { log.error(ex); return; } } if(args[i].equals("-port")) { port=Integer.parseInt(args[++i]); continue; } System.out.println("Util [-help] [-addr] [-port]"); return; } try { sock=createDatagramSocket(addr, port); System.out.println("sock: local address is " + sock.getLocalAddress() + ":" + sock.getLocalPort() + ", remote address is " + sock.getInetAddress() + ":" + sock.getPort()); System.in.read(); } catch(Exception ex) { log.error(ex); } } */ public static void main(String args[]) throws Exception { ClassConfigurator.getInstance(true); Message msg=new Message(null, new IpAddress("127.0.0.1", 4444), "Bela"); int size=Util.sizeOf(msg); System.out.println("size=" + msg.size() + ", streamable size=" + size); msg.putHeader("belaban", new NakAckHeader((byte)1, 23, 34)); size=Util.sizeOf(msg); System.out.println("size=" + msg.size() + ", streamable size=" + size); msg.putHeader("bla", new UdpHeader("groupname")); size=Util.sizeOf(msg); System.out.println("size=" + msg.size() + ", streamable size=" + size); IpAddress a1=new IpAddress(1234), a2=new IpAddress("127.0.0.1", 3333); a1.setAdditionalData("Bela".getBytes()); size=Util.sizeOf(a1); System.out.println("size=" + a1.size() + ", streamable size of a1=" + size); size=Util.sizeOf(a2); System.out.println("size=" + a2.size() + ", streamable size of a2=" + size); // System.out.println("Check for Linux: " + checkForLinux()); // System.out.println("Check for Solaris: " + checkForSolaris()); // System.out.println("Check for Windows: " + checkForWindows()); // System.out.println("version: " + getJavaVersion()); } public static String generateList(Collection c, String separator) { if(c == null) return null; StringBuilder sb=new StringBuilder(); boolean first=true; for(Iterator it=c.iterator(); it.hasNext();) { if(first) { first=false; } else { sb.append(separator); } sb.append(it.next()); } return sb.toString(); } /** * Replaces variables of ${var:default} with System.getProperty(var, default). If no variables are found, returns * the same string, otherwise a copy of the string with variables substituted * @param val * @return A string with vars replaced, or the same string if no vars found */ public static String substituteVariable(String val) { if(val == null) return val; String retval=val, prev; while(retval.contains("${")) { // handle multiple variables in val prev=retval; retval=_substituteVar(retval); if(retval.equals(prev)) break; } return retval; } private static String _substituteVar(String val) { int start_index, end_index; start_index=val.indexOf("${"); if(start_index == -1) return val; end_index=val.indexOf("}", start_index+2); if(end_index == -1) throw new IllegalArgumentException("missing \"}\" in " + val); String tmp=getProperty(val.substring(start_index +2, end_index)); if(tmp == null) return val; StringBuilder sb=new StringBuilder(); sb.append(val.substring(0, start_index)); sb.append(tmp); sb.append(val.substring(end_index+1)); return sb.toString(); } private static String getProperty(String s) { String var, default_val, retval=null; int index=s.indexOf(":"); if(index >= 0) { var=s.substring(0, index); default_val=s.substring(index+1); retval=System.getProperty(var, default_val); } else { var=s; retval=System.getProperty(var); } return retval; } /** * Used to convert a byte array in to a java.lang.String object * @param bytes the bytes to be converted * @return the String representation */ private static String getString(byte[] bytes) { StringBuilder sb=new StringBuilder(); for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; sb.append(0x00FF & b); if (i + 1 < bytes.length) { sb.append("-"); } } return sb.toString(); } /** * Converts a java.lang.String in to a MD5 hashed String * @param source the source String * @return the MD5 hashed version of the string */ public static String md5(String source) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] bytes = md.digest(source.getBytes()); return getString(bytes); } catch (Exception e) { return null; } } /** * Converts a java.lang.String in to a SHA hashed String * @param source the source String * @return the MD5 hashed version of the string */ public static String sha(String source) { try { MessageDigest md = MessageDigest.getInstance("SHA"); byte[] bytes = md.digest(source.getBytes()); return getString(bytes); } catch (Exception e) { e.printStackTrace(); return null; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Digest.java0000644000175000017500000004225011366547366024447 0ustar twernertwernerpackage org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.annotations.Immutable; import java.io.*; import static java.lang.Math.max; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * A message digest, which is used by the PBCAST layer for gossiping (also used by NAKACK for * keeping track of current seqnos for all members). It contains pairs of senders and a range of seqnos * (low and high), where each sender is associated with its highest and lowest seqnos seen so far. That * is, the lowest seqno which was not yet garbage-collected and the highest that was seen so far and is * deliverable (or was already delivered) to the application. A range of [0 - 0] means no messages have * been received yet. *

April 3 2001 (bela): Added high_seqnos_seen member. It is used to disseminate * information about the last (highest) message M received from a sender P. Since we might be using a * negative acknowledgment message numbering scheme, we would never know if the last message was * lost. Therefore we periodically gossip and include the last message seqno. Members who haven't seen * it (e.g. because msg was dropped) will request a retransmission. See DESIGN for details. * @author Bela Ban * @version $Id: Digest.java,v 1.8.2.1 2008/01/22 10:01:17 belaban Exp $ */ public class Digest implements Externalizable, Streamable { public static final Digest EMPTY_DIGEST = new Digest(); /** Map<Address, Entry> */ protected final Map senders; protected static final Log log=LogFactory.getLog(Digest.class); /** Used for externalization */ public Digest() { senders=createSenders(7); } public Digest(int size) { senders=createSenders(size); } /** Creates a new digest from an existing map by copying the keys and values from map */ public Digest(Map map) { senders=createSenders(map); } public Digest(Digest d) { this(d.senders); } public Digest(Address sender, long low, long highest_delivered, long highest_received) { senders=createSenders(1); senders.put(sender, new Entry(low, highest_delivered, highest_received)); } public Digest(Address sender, long low, long highest_delivered) { senders=createSenders(1); senders.put(sender, new Entry(low, highest_delivered)); } /** Returns an unmodifiable map, so modifications will result in exceptions */ public Map getSenders() { return Collections.unmodifiableMap(senders); } public boolean equals(Object obj) { if(!(obj instanceof Digest)) return false; Digest other=(Digest)obj; return senders.equals(other.senders); } public boolean contains(Address sender) { return senders.containsKey(sender); } /** Returns the Entry for the given sender. Note that Entry is immutable */ public Entry get(Address sender) { return senders.get(sender); } /** * Compares two digests and returns true if the senders are the same, otherwise false. * @param other * @return True if senders are the same, otherwise false. */ public boolean sameSenders(Digest other) { if(other == null) return false; if(this.senders.size() != other.senders.size()) return false; Set

my_senders=senders.keySet(), other_senders=other.senders.keySet(); return my_senders.equals(other_senders); } public Digest difference(Digest other) { if(other == null) return copy(); Digest result=EMPTY_DIGEST; if(this.equals(other)) { return result; } else { //find intersection and compare their entries Map resultMap=new ConcurrentHashMap(7); Set
intersection=new TreeSet
(this.senders.keySet()); intersection.retainAll(other.senders.keySet()); for(Address address : intersection) { Entry e1=this.get(address); Entry e2=other.get(address); if(e1.getHighestDeliveredSeqno() != e2.getHighestDeliveredSeqno()) { long low=Math.min(e1.highest_delivered_seqno, e2.highest_delivered_seqno); long high=max(e1.highest_delivered_seqno, e2.highest_delivered_seqno); Entry r=new Entry(low, high); resultMap.put(address, r); } } //any entries left in (this - intersection)? //if yes, add them to result if(intersection.size() != this.senders.keySet().size()) { Set
thisMinusInteresection=new TreeSet
(this.senders.keySet()); thisMinusInteresection.removeAll(intersection); for(Address address : thisMinusInteresection) { resultMap.put(address, new Entry(this.get(address))); } } //any entries left in (other - intersection)? //if yes, add them to result if(intersection.size() != other.senders.keySet().size()) { Set
otherMinusInteresection=new TreeSet
(other.senders.keySet()); otherMinusInteresection.removeAll(intersection); for(Address address : otherMinusInteresection) { resultMap.put(address, new Entry(other.get(address))); } } result=new Digest(resultMap); } return result; } public Digest highestSequence(Digest other) { if(other == null) return copy(); Digest result=EMPTY_DIGEST; if(this.equals(other)) { return this; } else { //find intersection and compare their entries Map resultMap=new ConcurrentHashMap(7); Set
intersection=new TreeSet
(this.senders.keySet()); intersection.retainAll(other.senders.keySet()); for(Address address : intersection) { Entry e1=this.get(address); Entry e2=other.get(address); long high=max(e1.highest_delivered_seqno, e2.highest_delivered_seqno); Entry r=new Entry(0, high); resultMap.put(address, r); } //any entries left in (this - intersection)? //if yes, add them to result if(intersection.size() != this.senders.keySet().size()) { Set
thisMinusInteresection=new TreeSet
(this.senders.keySet()); thisMinusInteresection.removeAll(intersection); for(Address address : thisMinusInteresection) { resultMap.put(address, new Entry(this.get(address))); } } //any entries left in (other - intersection)? //if yes, add them to result if(intersection.size() != other.senders.keySet().size()) { Set
otherMinusInteresection=new TreeSet
(other.senders.keySet()); otherMinusInteresection.removeAll(intersection); for(Address address : otherMinusInteresection) { resultMap.put(address, new Entry(other.get(address))); } } result=new Digest(resultMap); } return result; } public int size() { return senders.size(); } public long lowSeqnoAt(Address sender) { Entry entry=senders.get(sender); if(entry == null) return -1; else return entry.low_seqno; } public long highestDeliveredSeqnoAt(Address sender) { Entry entry=senders.get(sender); if(entry == null) return -1; else return entry.highest_delivered_seqno; } public long highestReceivedSeqnoAt(Address sender) { Entry entry=senders.get(sender); if(entry == null) return -1; else return entry.highest_received_seqno; } /** * Returns true if all senders of the current digest have their seqnos >= the ones from other * @param other * @return */ public boolean isGreaterThanOrEqual(Digest other) { if(other == null) return true; Map our_map=getSenders(); Address sender; Entry my_entry, their_entry; long my_highest, their_highest; for(Map.Entry entry: our_map.entrySet()) { sender=entry.getKey(); my_entry=entry.getValue(); their_entry=other.get(sender); if(their_entry == null) continue; my_highest=my_entry.getHighest(); their_highest=their_entry.getHighest(); if(my_highest < their_highest) return false; } return true; } public Digest copy() { return new Digest(senders); } public String toString() { StringBuilder sb=new StringBuilder(); boolean first=true; if(senders.isEmpty()) return "[]"; Map.Entry entry; Address key; Entry val; for(Iterator> it=senders.entrySet().iterator(); it.hasNext();) { entry=it.next(); key=entry.getKey(); val=entry.getValue(); if(!first) { sb.append(", "); } else { first=false; } sb.append(key).append(": ").append('[').append(val.low_seqno).append(" : "); sb.append(val.highest_delivered_seqno); if(val.highest_received_seqno >= 0) sb.append(" (").append(val.highest_received_seqno).append(")"); sb.append("]"); } return sb.toString(); } public String printHighestDeliveredSeqnos() { StringBuilder sb=new StringBuilder("["); boolean first=true; Map.Entry entry; Address key; Entry val; TreeMap copy=new TreeMap(senders); for(Iterator> it=copy.entrySet().iterator(); it.hasNext();) { entry=it.next(); key=entry.getKey(); val=entry.getValue(); if(!first) { sb.append(", "); } else { first=false; } sb.append(key).append("#").append(val.highest_delivered_seqno); } sb.append(']'); return sb.toString(); } public String printHighestReceivedSeqnos() { StringBuilder sb=new StringBuilder(); boolean first=true; Map.Entry entry; Address key; Entry val; for(Iterator> it=senders.entrySet().iterator(); it.hasNext();) { entry=it.next(); key=entry.getKey(); val=entry.getValue(); if(!first) { sb.append(", "); } else { sb.append('['); first=false; } sb.append(key).append("#").append(val.highest_received_seqno); } sb.append(']'); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(senders); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { Map tmp=(Map)in.readObject(); senders.clear(); senders.putAll(tmp); } public void writeTo(DataOutputStream out) throws IOException { out.writeShort(senders.size()); Map.Entry entry; Address key; Entry val; for(Iterator> it=senders.entrySet().iterator(); it.hasNext();) { entry=it.next(); key=entry.getKey(); val=entry.getValue(); Util.writeAddress(key, out); out.writeLong(val.low_seqno); out.writeLong(val.highest_delivered_seqno); out.writeLong(val.highest_received_seqno); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { short size=in.readShort(); Map tmp=new HashMap(size); Address key; for(int i=0; i < size; i++) { key=Util.readAddress(in); tmp.put(key, new Entry(in.readLong(), in.readLong(), in.readLong())); } senders.clear(); senders.putAll(tmp); } public long serializedSize() { long retval=Global.SHORT_SIZE; // number of elements in 'senders' if(!senders.isEmpty()) { Address addr=senders.keySet().iterator().next(); int len=addr.size() + 2 * Global.BYTE_SIZE; // presence byte, IpAddress vs other address len+=Entry.SIZE; // 3 longs in one Entry retval+=len * senders.size(); } return retval; } private static Map createSenders(int size) { return new ConcurrentHashMap(size); } private static Map createSenders(Map map) { return new ConcurrentHashMap(map); } /** * Class keeping track of the lowest and highest sequence numbers delivered, and the highest * sequence numbers received, per member. This class is immutable */ @Immutable public static class Entry implements Externalizable, Streamable { private long low_seqno=0; private long highest_delivered_seqno=0; // the highest delivered seqno, e.g. in 1,2,4,5,7 --> 2 private long highest_received_seqno=0; //the highest received seqno, e.g. in 1,2,4,5,7 --> 7 final static int SIZE=Global.LONG_SIZE * 3; public Entry() { } public Entry(long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { this.low_seqno=low_seqno; this.highest_delivered_seqno=highest_delivered_seqno; this.highest_received_seqno=highest_received_seqno; check(); } public Entry(long low_seqno, long highest_delivered_seqno) { this.low_seqno=low_seqno; this.highest_delivered_seqno=highest_delivered_seqno; check(); } public Entry(Entry other) { if(other != null) { low_seqno=other.low_seqno; highest_delivered_seqno=other.highest_delivered_seqno; highest_received_seqno=other.highest_received_seqno; check(); } } public final long getLow() {return low_seqno;} public final long getHighestDeliveredSeqno() {return highest_delivered_seqno;} public final long getHighestReceivedSeqno() {return highest_received_seqno;} /** Return the max of the highest delivered or highest received seqno */ public final long getHighest() {return max(highest_delivered_seqno, highest_received_seqno);} public boolean equals(Object obj) { if(!(obj instanceof Entry)) return false; Entry other=(Entry)obj; return low_seqno == other.low_seqno && highest_delivered_seqno == other.highest_delivered_seqno && highest_received_seqno == other.highest_received_seqno; } public String toString() { return new StringBuilder("low=").append(low_seqno).append(", highest delivered=").append(highest_delivered_seqno). append(", highest received=").append(highest_received_seqno).toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(low_seqno); out.writeLong(highest_delivered_seqno); out.writeLong(highest_received_seqno); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { low_seqno=in.readLong(); highest_delivered_seqno=in.readLong(); highest_received_seqno=in.readLong(); } public int size() { return SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeLong(low_seqno); out.writeLong(highest_delivered_seqno); out.writeLong(highest_received_seqno); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { low_seqno=in.readLong(); highest_delivered_seqno=in.readLong(); highest_received_seqno=in.readLong(); } private void check() { if(low_seqno > highest_delivered_seqno) throw new IllegalArgumentException("low_seqno (" + low_seqno + ") is greater than highest_delivered_seqno (" + highest_delivered_seqno + ")"); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Headers.java0000644000175000017500000001364111366547366024605 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.Global; import org.jgroups.Header; import java.util.HashMap; import java.util.Map; /** * Open addressing based implementation of a hashmap (not supporting the Map interface though) for message * headers. The keys are strings and the values Headers, and they're stored in an array in the format * key-1 | header-1 | key-2 | header-2. The array is populated from left to right, so any null slots can terminate * an interation, or signal empty slots. *
* It is assumed that we only have a few headers, 3-4 on average. Note that getting a header for a given key and * putting a new key/header are operations with O(n) cost, so this implementation is not recommended for * a large number of elements. *
* This class is not synchronized * @author Bela Ban * @version $Id: Headers.java,v 1.11.2.5 2008/07/31 12:55:23 belaban Exp $ */ public class Headers { /** Used to store strings and headers, e.g: name-1 | header-1 | name-2 | header-2 | null | null | name-3 | header-3 */ private Object[] data; /** Add space for 3 new elements when resizing */ private static final int RESIZE_INCR=6; public Headers(int initial_capacity) { data=new Object[initial_capacity << 1]; } public Headers(Headers hdrs) { data=new Object[hdrs.data.length]; System.arraycopy(hdrs.data, 0, this.data, 0, hdrs.data.length); } public Object[] getRawData() { return data; } /** * Returns the header associated with key * @param key * @return */ public Header getHeader(String key) { for(int i=0; i < data.length; i+=2) { if(data[i] == null) return null; if(data[i].equals(key)) return (Header)data[i+1]; } return null; } public Map getHeaders() { Map retval=new HashMap(data.length / 2); for(int i=0; i < data.length; i+=2) { if(data[i] != null) retval.put((String)data[i], (Header)data[i+1]); else break; } return retval; } public String printHeaders() { StringBuilder sb=new StringBuilder(); boolean first=true; for(int i=0; i < data.length; i+=2) { if(data[i] != null) { if(first) first=false; else sb.append(", "); sb.append(data[i]).append(": ").append(data[i+1]); } else break; } return sb.toString(); } /** Puts a header given a key into the hashmap. Overwrites potential existing entry. */ public void putHeader(String key, Header hdr) { _putHeader(key, hdr, 0, true); } /** * Puts a header given a key into the map, only if the key doesn't exist yet * @param key * @param hdr * @return the previous value associated with the specified key, or * null if there was no mapping for the key. * (A null return can also indicate that the map * previously associated null with the key, * if the implementation supports null values.) */ public Header putHeaderIfAbsent(String key, Header hdr) { return _putHeader(key, hdr, 0, false); } /** * * @param key * @return the header assoaicted with key * @deprecated Use getHeader() instead. The issue with removing a header is described in * http://jira.jboss.com/jira/browse/JGRP-393 */ public Header removeHeader(String key) { return getHeader(key); } public Headers copy() { return new Headers(this); } public int marshalledSize() { int retval=0; for(int i=0; i < data.length; i+=2) { if(data[i] != null) { retval+=((String)data[i]).length() +2; retval+=(Global.SHORT_SIZE *2); // 2 for magic number, 2 for size (short) retval+=((Header)data[i+1]).size(); } else break; } return retval; } public int size() { int retval=0; for(int i=0; i < data.length; i+=2) { if(data[i] != null) retval++; else break; } return retval; } public int capacity() { return data.length / 2; } public String printObjectHeaders() { StringBuilder sb=new StringBuilder(); for(int i=0; i < data.length; i+=2) { if(data[i] != null) sb.append(data[i]).append(": ").append(data[i+1]).append('\n'); else break; } return sb.toString(); } public String toString() { return printHeaders(); } /** * Increases the capacity of the array and copies the contents of the old into the new array */ private void resize() { int new_size=data.length + RESIZE_INCR; Object[] new_data=new Object[new_size]; System.arraycopy(data, 0, new_data, 0, data.length); data=new_data; } private Header _putHeader(String key, Header hdr, int start_index, boolean replace_if_present) { int i=start_index; while(i < data.length) { if(data[i] == null) { data[i]=key; data[i+1]=hdr; return null; } if(data[i].equals(key)) { Header retval=(Header)data[i+1]; if(replace_if_present) { data[i+1]=hdr; } return retval; } i+=2; if(i >= data.length) { resize(); } } throw new IllegalStateException("unable to add element " + key + ", index=" + i); // we should never come here } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Queue.java.concurrent0000644000175000017500000000463011366547366026475 0ustar twernertwerner// $Id: Queue.java.concurrent,v 1.1.1.1 2003/09/09 01:24:12 belaban Exp $ package org.jgroups.util; import org.jgroups.TimeoutException; import org.jgroups.log.Trace; import java.util.Vector; import EDU.oswego.cs.dl.util.concurrent.WaitFreeQueue; /** * Elements are added at the tail and removed from the head. Class is thread-safe in that * 1 producer and 1 consumer may add/remove elements concurrently. The class is not * explicitely designed for multiple producers or consumers. Implemented as a linked * list, so that removal of an element at the head does not cause a right-shift of the * remaining elements (as in a Vector-based implementation). * @author Bela Ban * @author Filip Hanik */ public class Queue extends WaitFreeQueue { boolean closed=false; int size=0; public Queue() { } public Object getFirst() { return super.peek(); } public Object getLast() { return null; // not implemented } public boolean closed() { return closed; } public void add(Object obj) throws QueueClosedException { try { super.put(obj); size++; } catch(InterruptedException e) { e.printStackTrace(); } } public void addAtHead(Object obj) throws QueueClosedException { try { super.put(obj); size++; } catch(InterruptedException e) { e.printStackTrace(); } } public Object remove() throws QueueClosedException { Object retval=null; try { retval=super.take(); size--; return retval; } catch(InterruptedException e) { e.printStackTrace(); return retval; } } public Object remove(long timeout) throws QueueClosedException, TimeoutException { return remove(); } public void removeElement(Object obj) throws QueueClosedException { ; } public Object peek() { return super.peek(); } public Object peek(long timeout) throws QueueClosedException, TimeoutException { return peek(); } public void close(boolean flush_entries) { closed=true; } public void reset() { closed=false; size=0; } public int size() { return size; } public String toString() { return super.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/package.html0000644000175000017500000000017111366547366024642 0ustar twernertwerner Provides useful functionality which cannot be assigned to any particular other package. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/MagicObjectInputStream.java0000644000175000017500000000315311366547366027572 0ustar twernertwernerpackage org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.conf.ClassConfigurator; import java.io.IOException; import java.io.InputStream; import java.io.ObjectStreamClass; /** * Uses magic numbers for class descriptors * @author Bela Ban * @version $Id: MagicObjectInputStream.java,v 1.6 2007/05/01 09:15:17 belaban Exp $ */ public class MagicObjectInputStream extends ContextObjectInputStream { static volatile ClassConfigurator conf=null; static final Log log=LogFactory.getLog(MagicObjectInputStream.class); public MagicObjectInputStream(InputStream is) throws IOException { super(is); if(conf == null) { try { conf=ClassConfigurator.getInstance(false); } catch(ChannelException e) { log.error("ClassConfigurator could not be instantiated", e); } } } protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass retval; short magic_num=super.readShort(); if(conf == null || magic_num == -1) { return super.readClassDescriptor(); } retval=conf.getObjectStreamClassFromMagicNumber(magic_num); if(retval == null) throw new ClassNotFoundException("failed fetching class descriptor for magic number " + magic_num); //if(log.isTraceEnabled()) //log.trace("reading descriptor (from " + magic_num + "): " + retval.getName()); return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Triple.java0000644000175000017500000000175411366547366024473 0ustar twernertwernerpackage org.jgroups.util; /** * Holds 3 values, useful when we have a map with a key, but more than 1 value and we don't want to create a separate * holder object for the values, and don't want to pass the values as a list or array. * @author Bela Ban * @version $Id: Triple.java,v 1.1.2.2 2008/01/22 10:49:38 belaban Exp $ */ public class Triple { private V1 val1; private V2 val2; private V3 val3; public Triple(V1 val1, V2 val2, V3 val3) { this.val1=val1; this.val2=val2; this.val3=val3; } public V1 getVal1() { return val1; } public void setVal1(V1 val1) { this.val1=val1; } public V2 getVal2() { return val2; } public void setVal2(V2 val2) { this.val2=val2; } public V3 getVal3() { return val3; } public void setVal3(V3 val3) { this.val3=val3; } public String toString() { return val1 + " : " + val2 + " : " + val3; } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ExposedDataOutputStream.java0000644000175000017500000000437411366547366030033 0ustar twernertwernerpackage org.jgroups.util; import java.io.DataOutputStream; import java.io.OutputStream; import java.io.IOException; /** * @author Bela Ban * @version $Id: ExposedDataOutputStream.java,v 1.2.4.1 2008/10/28 09:32:36 belaban Exp $ */ public class ExposedDataOutputStream extends DataOutputStream { /** * Creates a new data output stream to write data to the specified * underlying output stream. The counter written is * set to zero. * * @param out the underlying output stream, to be saved for later * use. * @see java.io.FilterOutputStream#out */ public ExposedDataOutputStream(OutputStream out) { super(out); } public void reset() { written=0; } public OutputStream getOutputStream() { return out; } /** * Writes the specified byte (the low eight bits of the argument * b) to the underlying output stream. If no exception * is thrown, the counter written is incremented by * 1. *

* Implements the write method of OutputStream. * * @param b the byte to be written. * @exception java.io.IOException if an I/O error occurs. * @see java.io.FilterOutputStream#out */ public void write(int b) throws IOException { out.write(b); incCount(1); } /** * Writes len bytes from the specified byte array * starting at offset off to the underlying output stream. * If no exception is thrown, the counter written is * incremented by len. * * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. * @exception IOException if an I/O error occurs. * @see java.io.FilterOutputStream#out */ public void write(byte b[], int off, int len) throws IOException { out.write(b, off, len); incCount(len); } private void incCount(int value) { int temp = written + value; if (temp < 0) { temp = Integer.MAX_VALUE; } written = temp; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/AckCollector.java0000644000175000017500000000534111366547366025575 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.Address; import org.jgroups.TimeoutException; import org.jgroups.View; import java.util.*; /** * @author Bela Ban * @version $Id: AckCollector.java,v 1.14.2.2 2009/08/11 11:28:03 belaban Exp $ */ public class AckCollector { /** List: list of members from whom we haven't received an ACK yet */ private final List missing_acks; private final Promise all_acks_received=new Promise(); private final Set
suspected_mbrs=new HashSet
(); public AckCollector() { missing_acks=new ArrayList(); } public AckCollector(List l) { missing_acks=new ArrayList(l); } public String printMissing() { synchronized(this) { return missing_acks.toString(); } } @Deprecated public static String printReceived() { return "n/a"; } public void reset(List
members) { synchronized(this) { suspected_mbrs.clear(); missing_acks.clear(); if(members != null && !members.isEmpty()) missing_acks.addAll(members); missing_acks.removeAll(suspected_mbrs); all_acks_received.reset(); } } public int size() { synchronized(this) { return missing_acks.size(); } } public void ack(Object member) { synchronized(this) { missing_acks.remove(member); if(missing_acks.isEmpty()) all_acks_received.setResult(Boolean.TRUE); } } public void suspect(Address member) { synchronized(this) { ack(member); suspected_mbrs.add(member); } } public void unsuspect(Address member) { synchronized(this) { suspected_mbrs.remove(member); } } public void handleView(View v) { if(v == null) return; Vector
mbrs=v.getMembers(); synchronized(this) { suspected_mbrs.retainAll(mbrs); } } public boolean waitForAllAcks() { if(missing_acks.isEmpty()) return true; Object result=all_acks_received.getResult(); return result != null && result instanceof Boolean && ((Boolean)result).booleanValue(); } public boolean waitForAllAcks(long timeout) throws TimeoutException { if(missing_acks.isEmpty()) return true; Object result=all_acks_received.getResultWithTimeout(timeout); return result != null && result instanceof Boolean && ((Boolean)result).booleanValue(); } public String toString() { return "missing=" + printMissing(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/LazyThreadFactory.java0000644000175000017500000000553111366547366026630 0ustar twernertwernerpackage org.jgroups.util; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedQueue; /** * Lazily names threads: whenever the address or cluster name is changed, all threads are renamed * @author Bela Ban * @version $Id: LazyThreadFactory.java,v 1.1.2.3 2008/06/17 15:30:31 belaban Exp $ */ public class LazyThreadFactory extends DefaultThreadFactory { private Collection> threads=new ConcurrentLinkedQueue>(); private static final StringBuilder ADDR=new StringBuilder(""); private static final StringBuilder CL_NAME=new StringBuilder(""); public LazyThreadFactory(ThreadGroup group, String baseName, boolean createDaemons) { super(group, baseName, createDaemons); } public LazyThreadFactory(ThreadGroup group, String baseName, boolean createDaemons, boolean use_numbering) { super(group, baseName, createDaemons, use_numbering); } public Thread newThread(ThreadGroup group, Runnable r, String name) { Thread retval=null; String addr=address; if(addr == null) addr=""; String cluster_name=clusterName; if(cluster_name == null) cluster_name=""; retval=super.newThread(group, r, name, addr, cluster_name); threads.add(new WeakReference(retval)); return retval; } public void setAddress(String address) { boolean changed=false; if(!Util.match(this.address, address)) changed=true; super.setAddress(address); if(changed) renameThreads(); } public void setClusterName(String cluster_name) { boolean changed=false; if(!Util.match(this.clusterName, cluster_name)) changed=true; super.setClusterName(cluster_name); if(changed) renameThreads(); } protected void renameThreads() { for(Iterator> it=threads.iterator(); it.hasNext();) { WeakReference ref=it.next(); Thread thread=ref.get(); if(thread == null) { it.remove(); continue; } String name=thread.getName(); name=changeName(name); thread.setName(name); } } /** Replaces "" with the address and with cluster name */ private String changeName(String name) { String retval=name; StringBuilder tmp; if(address != null) { tmp=new StringBuilder(address); retval=retval.replace(ADDR, tmp); } if(clusterName != null) { tmp=new StringBuilder(clusterName); retval=retval.replace(CL_NAME, tmp); } return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/todo.txt0000644000175000017500000000615411366547366024076 0ustar twernertwerner Todo List ========= $Id: todo.txt,v 1.1.1.1 2003/09/09 01:24:12 belaban Exp $ Currently Proxy resides on the server system, listens on multiple ports (as defined in mapping file) and forwards incoming connections to correct destinations. 1. Destination (host:port) as part of message --------------------------------------------- Currently the server takes incoming requests and routes them to destinations according to the specified mapping. However it should be possible to include the destination with an incoming request: when the proxy accepts a new connection it needs to check whether there is a destination directive. If not, it forwards the request according to the mapping table, otherwise it uses the destination information shipped with the request to route the message. If dynamic destination information is used, we will be able to listen on a single server socket only; all clients send their request (including the destination it should be forwarded to) to the server, and the server then handles the request in a separate thread. Note that the client side has to be instrumented to include dynamic forwarding information with each request (see Client Proxy). 2. Client Proxy --------------- When a client is behind a firewall, it is often not possible to create (TCP) connections to hosts outside the firewall on ports other than HTTP (80). To overcome this problem we create local server sockets which accept requests and route them to port 80 on a remote server machine. Each server socket is configured with a local port (on which to listen) and a remote destination to which all requests should be sent to. When receiving a request the forwarding destination is included with the message and sent to port 80 of the outside machine. The server proxy then forwards the request according to the routing destination included in the message. 2.1. Example: mapping table client side --------------------------------------- localhost:8110=pop.mail.yahoo.com:110 remote.host.com:80 localhost:2200=cvs.sf.net:22 remote.host.com:80 (ssl) The first line creates a server socket on the localhost at port 8110. Every incoming request will be forwarded to remote.host.com port 80 and will include a forwarding destination of pop.mail.yahoo.com:110. This means the proxy on the server side will receive a connection on port 80. It extracts the header, which contains the pop.mail.yahoo.com:110 destination and forwards the request to Yahoo. The response is then returned via the regular way. 2.1.1 SOCKS support ------------------- In the above example we established a direct TCP connection to remote.host.com:80. However, if we have a firewall, and all outgoing/incoming traffic is blocked, and we have to use SOCKS(5) to get outside, then this direct connection will fail. Therefore we need to enable SOCKS support in the Client Proxy by telling the VM about the SOCKS server and port. Fortunately this does not require any code changes, but simply the setting of some system properties. How to do is id described in http://java.sun.com/j2se/1.4/docs/guide/net/properties.html. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/PortsManager.java0000644000175000017500000001215111366547366025627 0ustar twernertwernerpackage org.jgroups.util; import java.io.*; import java.util.*; /** * Maintains a list of ports used on this host, associated with time stamps. The ports are persistet into the * temp file system. * @author Bela Ban * @version $Id: PortsManager.java,v 1.4.2.3 2009/01/05 07:41:00 belaban Exp $ */ public class PortsManager { private String filename="jgroups-ports.txt"; private String temp_dir=System.getProperty("java.io.tmpdir", "/tmp"); private final static String file_separator=System.getProperty("file.separator", "/"); private String absolute_name=temp_dir + file_separator + filename; /** Time after which a port can be removed and re-allocated */ private long expiry_time=60 * 1000L; public PortsManager() { } public PortsManager(long expiry_time) { this.expiry_time=expiry_time; } public PortsManager(String ports_file) { if(ports_file != null) this.absolute_name=ports_file; } public PortsManager(long expiry_time, String ports_file) { this.expiry_time=expiry_time; if(ports_file != null) absolute_name=ports_file; } public PortsManager(long expiry_time, String filename, String temp_dir) { this.expiry_time=expiry_time; this.filename=filename; this.temp_dir=temp_dir; absolute_name=temp_dir + file_separator + filename; } public long getExpiryTime() { return expiry_time; } public void setExpiryTime(long expiry_time) { this.expiry_time=expiry_time; } /** Loads the file, weeds out expired ports, returns the next available port and saves the new port in the file */ public int getNextAvailablePort(int start_port) { Map map; int retval=-1; try { map=load(); long current_time=System.currentTimeMillis(), timestamp; if(map.isEmpty()) { map.put(start_port, current_time); store(map); return start_port; } // weed out expired ports Map.Entry entry; for(Iterator> it=map.entrySet().iterator(); it.hasNext();) { entry=it.next(); timestamp=entry.getValue(); if(current_time - timestamp >= expiry_time) { it.remove(); } } // find next available port Set ports=map.keySet(); while(ports.contains(start_port)) { start_port++; } map.put(start_port, System.currentTimeMillis()); store(map); return start_port; } catch(IOException e) { return retval; } } /** Loads the file, removes the port (if existent) and closes the file again */ public void removePort(int port) { Map map; try { map=load(); if(map.isEmpty()) { return; } map.remove(port); store(map); } catch(IOException e) { } } /** * Updates the timestamp for the given port * @param port */ public void updatePort(int port) { Map map; try { map=load(); if(map.isEmpty()) { return; } map.put(port, System.currentTimeMillis()); store(map); } catch(IOException e) { } } /** Deletes the underlying file. Used for unit testing, not recommended for regular use ! */ public void deleteFile() { File file=new File(absolute_name); file.delete(); } private Map load() throws IOException { InputStream in=null; Map retval=new HashMap(); try { in=new FileInputStream(absolute_name); Properties props=new Properties(); props.load(in); for(Iterator> it=props.entrySet().iterator(); it.hasNext();) { Map.Entry entry=it.next(); String keystr=(String)entry.getKey(), valstr=(String)entry.getValue(); int key=Integer.parseInt(keystr); long val=Long.parseLong(valstr); retval.put(key, val); } return retval; } catch(Throwable t) { return retval; } finally { Util.close(in); } } private void store(Map map) throws IOException { OutputStream out=null; try { out=new FileOutputStream(absolute_name); Properties props=new Properties(); for(Map.Entry entry: map.entrySet()) { String key=entry.getKey().toString(); String val=entry.getValue().toString(); props.put(key, val); } props.store(out, "Persistent JGroups ports"); } finally { Util.close(out); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/DirectExecutor.java0000644000175000017500000000045011366547366026155 0ustar twernertwernerpackage org.jgroups.util; import java.util.concurrent.Executor; /** * @author Bela Ban * @version $Id: DirectExecutor.java,v 1.2 2006/12/19 11:03:14 belaban Exp $ */ public class DirectExecutor implements Executor { public void execute(Runnable command) { command.run(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ThreadFactory.java0000644000175000017500000000071711366547366025771 0ustar twernertwernerpackage org.jgroups.util; public interface ThreadFactory extends java.util.concurrent.ThreadFactory{ Thread newThread(Runnable r,String name); Thread newThread(ThreadGroup group, Runnable r,String name); void setPattern(String pattern); void setIncludeClusterName(boolean includeClusterName); void setClusterName(String channelName); void setAddress(String address); void renameThread(String base_name, Thread thread); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/TimedWriter.java0000644000175000017500000001410611366547366025466 0ustar twernertwerner// $Id: TimedWriter.java,v 1.6 2006/01/19 09:53:39 belaban Exp $ package org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; /** Waits until the buffer has been written to the output stream, or until timeout msecs have elapsed, whichever comes first. TODO: make it more generic, so all sorts of timed commands should be executable. Including return values, exceptions and Timeout exception. Also use ReusableThread instead of creating a new threa each time. @author Bela Ban */ public class TimedWriter { Thread thread=null; long timeout=2000; boolean completed=true; Exception write_ex=null; Socket sock=null; static Log log=LogFactory.getLog(TimedWriter.class); static class Timeout extends Exception { public String toString() { return "TimedWriter.Timeout"; } } class WriterThread extends Thread { DataOutputStream out=null; byte[] buf=null; int i=0; public WriterThread(OutputStream out, byte[] buf) { super(Util.getGlobalThreadGroup(), "TimedWriter.WriterThread"); this.out=new DataOutputStream(out); this.buf=buf; } public WriterThread(OutputStream out, int i) { super(Util.getGlobalThreadGroup(), "TimedWriter.WriterThread"); this.out=new DataOutputStream(out); this.i=i; } public void run() { try { if(buf != null) out.write(buf); else { out.writeInt(i); } } catch(IOException e) { write_ex=e; } completed=true; } } class SocketCreator extends Thread { InetAddress local=null, remote=null; int peer_port=0; public SocketCreator(InetAddress local, InetAddress remote, int peer_port) { this.local=local; this.remote=remote; this.peer_port=peer_port; } public void run() { completed=false; sock=null; try { sock=new Socket(remote, peer_port, local, 0); // 0 means choose any port } catch(IOException io_ex) { write_ex=io_ex; } completed=true; } } void start(InetAddress local, InetAddress remote, int peer_port) { stop(); thread=new SocketCreator(local, remote, peer_port); thread.start(); } void start(OutputStream out, byte[] buf) { stop(); thread=new WriterThread(out, buf); thread.start(); } void start(OutputStream out, int i) { stop(); thread=new WriterThread(out, i); thread.start(); } void stop() { if(thread != null && thread.isAlive()) { thread.interrupt(); try {thread.join(timeout);} catch(Exception e) {} } } /** Writes data to an output stream. If the method does not return within timeout milliseconds, a Timeout exception will be thrown. */ public synchronized void write(OutputStream out, byte[] buf, long timeout) throws Exception, Timeout, InterruptedException { if(out == null || buf == null) { log.error("TimedWriter.write(): output stream or buffer is null, ignoring write"); return; } try { this.timeout=timeout; completed=false; start(out, buf); if(thread == null) return; thread.join(timeout); if(completed == false) { throw new Timeout(); } if(write_ex != null) { Exception tmp=write_ex; write_ex=null; throw tmp; } } finally { // stop the thread in any case stop(); } } public synchronized void write(OutputStream out, int i, long timeout) throws Exception, Timeout, InterruptedException { if(out == null) { log.error("TimedWriter.write(): output stream is null, ignoring write"); return; } try { this.timeout=timeout; completed=false; start(out, i); if(thread == null) return; thread.join(timeout); if(completed == false) { throw new Timeout(); } if(write_ex != null) { Exception tmp=write_ex; write_ex=null; throw tmp; } } finally { // stop the thread in any case stop(); } } /** Tries to create a socket to remote_peer:remote_port. If not sucessful within timeout milliseconds, throws the Timeout exception. Otherwise, returns the socket or throws an IOException. */ public synchronized Socket createSocket(InetAddress local, InetAddress remote, int port, long timeout) throws Exception, Timeout, InterruptedException { try { this.timeout=timeout; completed=false; start(local, remote, port); if(thread == null) return null; thread.join(timeout); if(completed == false) { throw new Timeout(); } if(write_ex != null) { Exception tmp=write_ex; write_ex=null; throw tmp; } return sock; } finally { // stop the thread in any case stop(); } } public static void main(String[] args) { TimedWriter w=new TimedWriter(); InetAddress local=null; InetAddress remote=null; int port=0; Socket sock=null ; if(args.length != 3) { log.error("TimedWriter "); return; } try { local=InetAddress.getByName(args[0]); remote=InetAddress.getByName(args[1]); port=Integer.parseInt(args[2]); } catch(Exception e) { log.error("Could find host " + remote); return; } while(true) { try { sock=w.createSocket(local, remote, port, 3000); if(sock != null) { System.out.println("Connection created"); return; } } catch(TimedWriter.Timeout t) { log.error("Timed out creating socket"); } catch(Exception io_ex) { log.error("Connection could not be created, retrying"); Util.sleep(2000); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Command.java0000644000175000017500000000046211366547366024605 0ustar twernertwerner// $Id: Command.java,v 1.3 2006/04/05 05:33:57 belaban Exp $ package org.jgroups.util; /** * The Command patttern (see Gamma et al.). Implementations would provide their * own execute method. * @author Bela Ban */ public interface Command { boolean execute() throws Exception; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Streamable.java0000644000175000017500000000153411366547366025307 0ustar twernertwernerpackage org.jgroups.util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** * Implementations of Streamable can add their state directly to the output stream, enabling them to bypass costly * serialization * @author Bela Ban * @version $Id: Streamable.java,v 1.2 2005/07/25 16:21:47 belaban Exp $ */ public interface Streamable { /** Write the entire state of the current object (including superclasses) to outstream. * Note that the output stream must not be closed */ void writeTo(DataOutputStream out) throws IOException; /** Read the state of the current object (including superclasses) from instream * Note that the input stream must not be closed */ void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ShutdownRejectedExecutionHandler.java0000644000175000017500000000300411366547366031665 0ustar twernertwernerpackage org.jgroups.util; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import org.apache.commons.logging.LogFactory; /** * ShutdownRejectedExecutionHandler is a decorator RejectedExecutionHandler used * in all JGroups ThreadPoolExecutor(s). Default RejectedExecutionHandler raises * RuntimeException when a task is submitted to ThreadPoolExecutor that has been * shutdown. ShutdownRejectedExecutionHandler instead logs only a warning * message. * * @author Vladimir Blagojevic * @see ThreadPoolExecutor * @see RejectedExecutionHandler * @version $Id: ShutdownRejectedExecutionHandler.java,v 1.101 2008/04/08 * 14:49:05 belaban Exp $ */ public class ShutdownRejectedExecutionHandler implements RejectedExecutionHandler { RejectedExecutionHandler handler; public ShutdownRejectedExecutionHandler(RejectedExecutionHandler handler) { super(); if(handler == null) throw new NullPointerException("RejectedExecutionHandler cannot be null"); this.handler=handler; } public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { if(executor.isShutdown()) { LogFactory.getLog(this.getClass()).warn("ThreadPoolExecutor " + executor + " is shutdown and rejected submitted task " + r); } else { handler.rejectedExecution(r, executor); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/SeqnoTable.java0000644000175000017500000000620511366547366025265 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.Address; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Maintains the highest received and highest delivered seqno per member * @author Bela Ban * @version $Id: SeqnoTable.java,v 1.1.6.1 2008/03/12 10:08:22 belaban Exp $ */ public class SeqnoTable { private long next_to_receive=0; private final ConcurrentMap map=new ConcurrentHashMap(); public SeqnoTable(long next_to_receive) { this.next_to_receive=next_to_receive; } public long getHighestReceived(Address member) { Entry entry=map.get(member); return entry != null? entry.getHighestReceived() : -1; } public long getNextToReceive(Address member) { Entry entry=map.get(member); return entry != null? entry.getNextToReceive() : -1; } public boolean add(Address member, long seqno) { Entry entry=map.get(member); if(entry == null) { entry=new Entry(next_to_receive); Entry entry2=map.putIfAbsent(member, entry); if(entry2 != null) entry=entry2; } // now entry is not null return entry.add(seqno); } public void remove(Address member) { map.remove(member); } public boolean retainAll(Collection
members) { return map.keySet().retainAll(members); } public void clear() { map.clear(); } public String toString() { return map.toString(); } private static class Entry { long highest_received; long next_to_receive; final Set seqnos=new HashSet(); private Entry(long initial_seqno) { this.next_to_receive=this.highest_received=initial_seqno; } public synchronized long getHighestReceived() { return highest_received; } public synchronized long getNextToReceive() { return next_to_receive; } public synchronized boolean add(long seqno) { try { if(seqno == next_to_receive) { next_to_receive++; while(true) { if(seqnos.remove(next_to_receive)) { next_to_receive++; } else break; } return true; } if(seqno < next_to_receive) return false; // seqno > next_to_receive seqnos.add(seqno); return true; } finally { highest_received=Math.max(highest_received, seqno); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(next_to_receive).append(" - ").append(highest_received); if(!seqnos.isEmpty()) sb.append(" ").append(seqnos); return sb.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ExposedBufferedInputStream.java0000644000175000017500000000334611366547366030501 0ustar twernertwernerpackage org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.BufferedInputStream; import java.io.InputStream; /** * @author Bela Ban * @version $Id: ExposedBufferedInputStream.java,v 1.3 2005/07/25 16:57:31 belaban Exp $ */ public class ExposedBufferedInputStream extends BufferedInputStream { private final static Log log=LogFactory.getLog(ExposedBufferedInputStream.class); /** * Creates a BufferedInputStream * and saves its argument, the input stream * in, for later use. An internal * buffer array is created and stored in buf. * * @param in the underlying input stream. */ public ExposedBufferedInputStream(InputStream in) { super(in); } /** * Creates a BufferedInputStream * with the specified buffer size, * and saves its argument, the input stream * in, for later use. An internal * buffer array of length size * is created and stored in buf. * * @param in the underlying input stream. * @param size the buffer size. * @throws IllegalArgumentException if size <= 0. */ public ExposedBufferedInputStream(InputStream in, int size) { super(in, size); } public void reset(int size) { count=pos=marklimit=0; markpos=-1; if(buf != null) { if(size > buf.length) { buf=new byte[size]; } } else { buf=new byte[4096]; if(log.isWarnEnabled()) log.warn("output stream was closed, re-creating it (please don't close it)"); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Range.java0000644000175000017500000000213211366547366024257 0ustar twernertwerner// $Id: Range.java,v 1.5 2004/10/04 20:43:35 belaban Exp $ package org.jgroups.util; import java.io.*; public class Range implements Externalizable, Streamable { public long low=-1; // first msg to be retransmitted public long high=-1; // last msg to be retransmitted /** For externalization */ public Range() { } public Range(long low, long high) { this.low=low; this.high=high; } public String toString() { return "[" + low + " : " + high + ']'; } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(low); out.writeLong(high); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { low=in.readLong(); high=in.readLong(); } public void writeTo(DataOutputStream out) throws IOException { out.writeLong(low); out.writeLong(high); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { low=in.readLong(); high=in.readLong(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ThreadDecorator.java0000644000175000017500000000134511366547366026302 0ustar twernertwernerpackage org.jgroups.util; /** * An object that can alter the state of a thread when it receives a callback from a {@link ThreadManager} notifying * it that the thread has been created or released from use. * * @author Brian Stansberry * @version $Id: ThreadDecorator.java,v 1.1.2.1 2008/05/26 09:14:40 belaban Exp $ */ public interface ThreadDecorator { /** * Notification that thread has just been created. * @param thread the thread */ void threadCreated(Thread thread); /** * Notification that thread has just been released from use * (e.g. returned to a thread pool after executing a task). * @param thread the thread */ void threadReleased(Thread thread); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ThreadManagerThreadPoolExecutor.java0000644000175000017500000000444511366547366031437 0ustar twernertwernerpackage org.jgroups.util; import java.util.concurrent.*; import java.util.concurrent.ThreadFactory; /** * ThreadPoolExecutor subclass that implements @{link ThreadManager}. * @author Brian Stansberry * @version $Id: ThreadManagerThreadPoolExecutor.java,v 1.1.2.1 2008/05/26 09:14:39 belaban Exp $ */ public class ThreadManagerThreadPoolExecutor extends ThreadPoolExecutor implements ThreadManager { private ThreadDecorator decorator; public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); } public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler); } public ThreadManagerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } public ThreadDecorator getThreadDecorator() { return decorator; } public void setThreadDecorator(ThreadDecorator decorator) { this.decorator=decorator; } /** * Invokes {@link ThreadDecorator#threadReleased(Thread)} on the current thread. *

* {@inheritDoc} */ @Override protected void afterExecute(Runnable r, Throwable t) { try { super.afterExecute(r, t); } finally { if(decorator != null) decorator.threadReleased(Thread.currentThread()); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/OutgoingBufferPool.java0000644000175000017500000000363411366547366027012 0ustar twernertwernerpackage org.jgroups.util; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * Maintains a pool of ExposedDataOutputStreams. The main reason is that a ByteArrayOutputStream starts with 1024 * bytes, and almost always increases to 65K (max size of a UDP datagram). We save a few copies when the BAOS increases * its size by pooling those. * @author Bela Ban * @version $Id: OutgoingBufferPool.java,v 1.1 2007/01/07 01:24:52 belaban Exp $ */ public class OutgoingBufferPool { private BlockingQueue buffers; public OutgoingBufferPool(int capacity) { buffers=new ArrayBlockingQueue(capacity); for(int i=0; i < capacity; i++) { ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(1024); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); try { buffers.put(dos); } catch(InterruptedException e) { Thread.currentThread().interrupt(); return; } } } public ExposedDataOutputStream take() throws InterruptedException { return buffers.take(); } public void put(ExposedDataOutputStream buf) throws InterruptedException { ((ExposedByteArrayOutputStream)buf.getOutputStream()).reset(); buf.reset(); buffers.put(buf); } public String dumpStats() { ExposedByteArrayOutputStream stream; StringBuilder sb=new StringBuilder(); sb.append(buffers.size()).append(" elements, capacities:\n"); for(ExposedDataOutputStream buf: buffers) { stream=(ExposedByteArrayOutputStream)buf.getOutputStream(); sb.append("size=").append(stream.size()).append(", capacity=").append(stream.getCapacity()).append(")\n"); } return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/RspList.java0000644000175000017500000001245311366547366024632 0ustar twernertwerner// $Id: RspList.java,v 1.9 2007/07/30 10:40:45 belaban Exp $ package org.jgroups.util; import org.jgroups.Address; import java.util.*; /** * Contains responses from all members. Marks faulty members. * A RspList is a response list used in peer-to-peer protocols. This class is unsynchronized */ public class RspList implements Map { /** Map */ final Map rsps=new HashMap(); public RspList() { } /** Adds a list of responses * @param responses Collection */ public RspList(Collection responses) { if(responses != null) { for(Rsp rsp: responses) { rsps.put(rsp.getSender(), rsp); } } } public boolean isEmpty() { return rsps.isEmpty(); } public boolean containsKey(Object key) { return rsps.containsKey(key); } public boolean containsValue(Object value) { return rsps.containsValue(value); } /** * Returns the Rsp associated with address key * @param key Address (key) * @return Rsp */ public Rsp get(Object key) { return rsps.get(key); } /** * Returns the value associated with address key * @param key * @return Object value */ public Object getValue(Object key) { Rsp rsp=get(key); return rsp != null? rsp.getValue() : null; } public Rsp put(Address key, Rsp value) { return rsps.put(key, value); } public Rsp remove(Object key) { return rsps.remove(key); } public void putAll(Map m) { rsps.putAll(m); } public void clear() { rsps.clear(); } public Set

keySet() { return rsps.keySet(); } public Collection values() { return rsps.values(); } public Set> entrySet() { return rsps.entrySet(); } /** * Clears the response list * @deprecated Use {@link #clear()} instead */ public void reset() { clear(); } public void addRsp(Address sender, Object retval) { Rsp rsp=get(sender); if(rsp != null) { rsp.sender=sender; rsp.retval=retval; rsp.received=true; rsp.suspected=false; return; } rsps.put(sender, new Rsp(sender, retval)); } public void addNotReceived(Address sender) { Rsp rsp=get(sender); if(rsp == null) rsps.put(sender, new Rsp(sender)); } public void addSuspect(Address sender) { Rsp rsp=get(sender); if(rsp != null) { rsp.sender=sender; rsp.retval=null; rsp.received=false; rsp.suspected=true; return; } rsps.put(sender, new Rsp(sender, true)); } public boolean isReceived(Address sender) { Rsp rsp=get(sender); return rsp != null && rsp.received; } public int numSuspectedMembers() { int num=0; Collection values=values(); for(Rsp rsp: values) { if(rsp.wasSuspected()) num++; } return num; } public int numReceived() { int num=0; Collection values=values(); for(Rsp rsp: values) { if(rsp.wasReceived()) num++; } return num; } /** Returns the first value in the response set. This is random, but we try to return a non-null value first */ public Object getFirst() { Collection values=values(); for(Rsp rsp: values) { if(rsp.getValue() != null) return rsp.getValue(); } return null; } /** * Returns the results from non-suspected members that are not null. */ public Vector getResults() { Vector ret=new Vector(); Object val; for(Rsp rsp: values()) { if(rsp.wasReceived() && (val=rsp.getValue()) != null) ret.addElement(val); } return ret; } public Vector
getSuspectedMembers() { Vector
retval=new Vector
(); for(Rsp rsp: values()) { if(rsp.wasSuspected()) retval.addElement(rsp.getSender()); } return retval; } public boolean isSuspected(Address sender) { Rsp rsp=get(sender); return rsp != null && rsp.suspected; } public int size() { return rsps.size(); } /** * Returns the Rsp at index i * @param i The index * @return a Rsp * @throws ArrayIndexOutOfBoundsException * @deprecated Use {@link #entrySet()} or {@link #values()} instead */ public Object elementAt(int i) throws ArrayIndexOutOfBoundsException { Set
keys=new TreeSet
(keySet()); Object[] keys_array=keys.toArray(); Object key=keys_array[i]; return get(key); } public String toString() { StringBuilder ret=new StringBuilder(); for(Rsp rsp: values()) { ret.append("[" + rsp + "]\n"); } return ret.toString(); } boolean contains(Address sender) { return containsKey(sender); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ReusableThread.java0000644000175000017500000002327411366547366026127 0ustar twernertwerner// $Id: ReusableThread.java,v 1.7 2005/01/16 01:04:52 belaban Exp $ package org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Reusable thread class. Instead of creating a new thread per task, this instance can be reused * to run different tasks in turn. This is done by looping and assigning the Runnable task objects * whose run method is then called.
* Tasks are Runnable objects and should be prepared to terminate when they receive an * InterruptedException. This is thrown by the stop() method.
*

* The following situations have to be tested: *

    *
  1. ReusableThread is started. Then, brefore assigning a task, it is stopped again *
  2. ReusableThread is started, assigned a long running task. Then, before task is done, * stop() is called *
  3. ReusableThread is started, assigned a task. Then waitUntilDone() is called, then stop() *
  4. ReusableThread is started, assigned a number of tasks (waitUntilDone() called between tasks), * then stopped *
  5. ReusableThread is started, assigned a task *
* * @author Bela Ban */ public class ReusableThread implements Runnable { volatile Thread thread=null; // thread that works on the task Runnable task=null; // task assigned to thread String thread_name="ReusableThread"; volatile boolean suspended=false; protected static final Log log=LogFactory.getLog(ReusableThread.class); final long TASK_JOIN_TIME=3000; // wait 3 secs for an interrupted thread to terminate public ReusableThread() { } public ReusableThread(String thread_name) { this.thread_name=thread_name; } public boolean done() { return task == null; } public boolean available() { return done(); } public boolean isAlive() { synchronized(this) { return thread != null && thread.isAlive(); } } /** * Will always be called from synchronized method, no need to do our own synchronization */ public void start() { if(thread == null || (thread != null && !thread.isAlive())) { thread=new Thread(this, thread_name); thread.setDaemon(true); thread.start(); } } /** * Stops the thread by setting thread=null and interrupting it. The run() method catches the * InterruptedException and checks whether thread==null. If this is the case, it will terminate */ public void stop() { Thread tmp=null; if(log.isTraceEnabled()) log.trace("entering THIS"); synchronized(this) { if(log.isTraceEnabled()) log.trace("entered THIS (thread=" + printObj(thread) + ", task=" + printObj(task) + ", suspended=" + suspended + ')'); if(thread != null && thread.isAlive()) { tmp=thread; thread=null; // signals the thread to stop task=null; if(log.isTraceEnabled()) log.trace("notifying thread"); notifyAll(); if(log.isTraceEnabled()) log.trace("notifying thread completed"); } thread=null; task=null; } if(tmp != null && tmp.isAlive()) { long s1=System.currentTimeMillis(), s2=0; if(log.isTraceEnabled()) log.trace("join(" + TASK_JOIN_TIME + ')'); tmp.interrupt(); try { tmp.join(TASK_JOIN_TIME); } catch(Exception e) { } s2=System.currentTimeMillis(); if(log.isTraceEnabled()) log.trace("join(" + TASK_JOIN_TIME + ") completed in " + (s2 - s1)); if(tmp.isAlive()) if(log.isErrorEnabled()) log.error("thread is still alive"); tmp=null; } } /** * Suspends the thread. Does nothing if already suspended. If a thread is waiting to be assigned a task, or * is currently running a (possibly long-running) task, then it will be suspended the next time it * waits for suspended==false (second wait-loop in run()) */ public void suspend() { synchronized(this) { if(log.isTraceEnabled()) log.trace("suspended=" + suspended + ", task=" + printObj(task)); if(suspended) return; // already suspended else suspended=true; } } /** * Resumes the thread. Noop if not suspended */ public void resume() { synchronized(this) { suspended=false; notifyAll(); // notifies run(): the wait on suspend() is released } } /** * Assigns a task to the thread. If the thread is not running, it will be started. It it is * already working on a task, it will reject the new task. Returns true if task could be * assigned auccessfully */ public boolean assignTask(Runnable t) { synchronized(this) { start(); // creates and starts the thread if not yet running if(task == null) { task=t; notifyAll(); // signals run() to start working (first wait-loop) return true; } else { if(log.isErrorEnabled()) log.error("already working on a thread: current_task=" + task + ", new task=" + t + ", thread=" + thread + ", is alive=" + (thread != null ? "" + thread.isAlive() : "null")); return false; } } } /** * Delicate piece of code (means very important :-)). Works as follows: loops until stop is true. * Waits in a loop until task is assigned. Then runs the task and notifies waiters that it's done * when task is completed. Then returns to the first loop to wait for more work. Does so until * stop() is called, which sets stop=true and interrupts the thread. If waiting for a task, the * thread terminates. If running a task, the task is interrupted, and the thread terminates. If the * task is not interrupible, the stop() method will wait for 3 secs (join on the thread), then return. * This means that the run() method of the task will complete and only then will the thread be * garbage-collected. */ public void run() { while(thread != null) { // Stop sets thread=null try { if(log.isTraceEnabled()) log.trace("entering ASSIGN"); synchronized(this) { if(log.isTraceEnabled()) log.trace("entered ASSIGN (task=" + printObj(task) + ", thread=" + printObj(thread) + ')'); while(task == null && thread != null) { // first wait-loop: wait for task to be assigned (assignTask()) if(log.isTraceEnabled()) log.trace("wait ASSIGN"); wait(); if(log.isTraceEnabled()) log.trace("wait ASSIGN completed"); } } } catch(InterruptedException ex) { // on assignTask() if(log.isTraceEnabled()) log.trace("interrupt on ASSIGN"); } if(thread == null) return; // we need to terminate try { if(log.isTraceEnabled()) log.trace("entering SUSPEND"); synchronized(this) { if(log.isTraceEnabled()) log.trace("entered SUSPEND (suspended=" + suspended + ", task=" + printObj(task) + ')'); while(suspended && thread != null) { // second wait-loop: wait for thread to resume (resume()) if(log.isTraceEnabled()) log.trace("wait SUSPEND"); wait(); if(log.isTraceEnabled()) log.trace("wait SUSPEND completed"); } } } catch(InterruptedException ex) { // on resume() if(log.isTraceEnabled()) log.trace("interrupt on RESUME"); } if(thread == null) return; // we need to terminate if(task != null) { if(log.isTraceEnabled()) log.trace("running task"); try { task.run(); //here we are actually running the task } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed running task", ex); } if(log.isTraceEnabled()) log.trace("task completed"); } if(log.isTraceEnabled()) log.trace("entering THIS"); synchronized(this) { if(log.isTraceEnabled()) log.trace("entered THIS"); task=null; if(log.isTraceEnabled()) log.trace("notify THIS"); notifyAll(); if(log.isTraceEnabled()) log.trace("notify THIS completed"); } } if(log.isTraceEnabled()) log.trace("terminated"); } String printObj(Object obj) { if(obj == null) return "null"; else return "non-null"; } public void waitUntilDone() { if(log.isTraceEnabled()) log.trace("entering THIS"); synchronized(this) { if(log.isTraceEnabled()) log.trace("entered THIS (task=" + printObj(task) + ')'); while(task != null) { try { if(log.isTraceEnabled()) log.trace("wait THIS"); wait(); if(log.isTraceEnabled()) log.trace("wait THIS completed"); } catch(InterruptedException interrupted) { } } } } public String toString() { return "suspended=" + suspended; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/UnmodifiableVector.java0000644000175000017500000001504511366547366027013 0ustar twernertwernerpackage org.jgroups.util; import java.util.*; /** * Vector which cannot be modified * @author Bela Ban * @version $Id: UnmodifiableVector.java,v 1.3 2006/12/09 22:59:34 belaban Exp $ */ public class UnmodifiableVector extends Vector { Vector v; public UnmodifiableVector(Vector v) { this.v=v; } public synchronized void copyInto(Object[] anArray) { v.copyInto(anArray); } public synchronized void trimToSize() { throw new UnsupportedOperationException(); } public synchronized void ensureCapacity(int minCapacity) { throw new UnsupportedOperationException(); } public synchronized void setSize(int newSize) { throw new UnsupportedOperationException(); } public synchronized int capacity() { return v.capacity(); } public synchronized int size() { return v.size(); } public synchronized boolean isEmpty() { return v.isEmpty(); } public Enumeration elements() { return v.elements(); } public boolean contains(Object elem) { return v.contains(elem); } public int indexOf(Object elem) { return v.indexOf(elem); } public synchronized int indexOf(Object elem, int index) { return v.indexOf(elem, index); } public synchronized int lastIndexOf(Object elem) { return v.lastIndexOf(elem); } public synchronized int lastIndexOf(Object elem, int index) { return v.lastIndexOf(elem, index); } public synchronized Object elementAt(int index) { return v.elementAt(index); } public synchronized Object firstElement() { return v.firstElement(); } public synchronized Object lastElement() { return v.lastElement(); } public synchronized void setElementAt(Object obj, int index) { v.setElementAt(obj, index); } public synchronized void removeElementAt(int index) { throw new UnsupportedOperationException(); } public synchronized void insertElementAt(Object obj, int index) { throw new UnsupportedOperationException(); } public synchronized void addElement(Object obj) { throw new UnsupportedOperationException(); } public synchronized boolean removeElement(Object obj) { throw new UnsupportedOperationException(); } public synchronized void removeAllElements() { throw new UnsupportedOperationException(); } public synchronized Object clone() { return v.clone(); } public synchronized Object[] toArray() { return v.toArray(); } public synchronized Object[] toArray(Object[] a) { return v.toArray(a); } public synchronized Object get(int index) { return v.get(index); } public synchronized Object set(int index, Object element) { throw new UnsupportedOperationException(); } public synchronized boolean add(Object o) { throw new UnsupportedOperationException(); } public boolean remove(Object o) { throw new UnsupportedOperationException(); } public void add(int index, Object element) { throw new UnsupportedOperationException(); } public synchronized Object remove(int index) { throw new UnsupportedOperationException(); } public void clear() { throw new UnsupportedOperationException(); } public synchronized boolean containsAll(Collection c) { return v.containsAll(c); } public synchronized boolean addAll(Collection c) { throw new UnsupportedOperationException(); } public synchronized boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } public synchronized boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } public synchronized boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } public synchronized boolean equals(Object o) { return v.equals(o); } public synchronized int hashCode() { return v.hashCode(); } public synchronized String toString() { return v.toString(); } public synchronized java.util.List subList(int fromIndex, int toIndex) { return v.subList(fromIndex, toIndex); } public ListIterator listIterator() { return new ListIterator() { ListIterator i = v.listIterator(); public boolean hasNext() {return i.hasNext();} public Object next() {return i.next();} public boolean hasPrevious() { return i.hasPrevious(); } public Object previous() { return i.previous(); } public int nextIndex() { return i.nextIndex(); } public int previousIndex() { return i.previousIndex(); } public void remove() { throw new UnsupportedOperationException(); } public void set(Object o) { throw new UnsupportedOperationException(); } public void add(Object o) { throw new UnsupportedOperationException(); } }; } public ListIterator listIterator(final int index) { return new ListIterator() { ListIterator i = v.listIterator(index); public boolean hasNext() {return i.hasNext();} public Object next() {return i.next();} public boolean hasPrevious() { return i.hasPrevious(); } public Object previous() { return i.previous(); } public int nextIndex() { return i.nextIndex(); } public int previousIndex() { return i.previousIndex(); } public void remove() { throw new UnsupportedOperationException(); } public void set(Object o) { throw new UnsupportedOperationException(); } public void add(Object o) { throw new UnsupportedOperationException(); } }; } public Iterator iterator() { return new Iterator() { Iterator i = v.iterator(); public boolean hasNext() {return i.hasNext();} public Object next() {return i.next();} public void remove() { throw new UnsupportedOperationException(); } }; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ThreadPool.java0000644000175000017500000000571211366547366025273 0ustar twernertwerner// $Id: ThreadPool.java,v 1.9.14.1 2008/01/22 10:01:16 belaban Exp $ package org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Maintains a set of ReusableThreads. When a thread is to be returned, all existing threads * are checked: when one is available, it will be returned. Otherwise, a new thread is created * and returned, unless the pool limit is reached, in which case null is returned. * Creates threads only as needed, up to the MAX_NUM limit. However, does not shrink the pool * when more threads become available than are used. * @todo Shrink thread pool if threads are unused after some configurable time. * @author Bela Ban */ public class ThreadPool { int MAX_NUM=255; int current_index=0; /// next available thread ReusableThread[] pool=null; boolean[] available_threads=null; protected static final Log log=LogFactory.getLog(ThreadPool.class); public ThreadPool(int max_num) { MAX_NUM=max_num; pool=new ReusableThread[MAX_NUM]; available_threads=new boolean[MAX_NUM]; for(int i=0; i < pool.length; i++) { pool[i]=null; available_threads[i]=true; } if(log.isDebugEnabled()) log.debug("created a pool of " + MAX_NUM + " threads"); } public ReusableThread getThread() { ReusableThread retval=null, tmp; synchronized(pool) { // check whether a previously created thread can be reused for(int i=0; i < current_index; i++) { tmp=pool[i]; if(tmp.available()) { return tmp; } } // else create a new thread and add it to the pool if(current_index >= MAX_NUM) { if(log.isErrorEnabled()) log.error("could not create new thread because " + "pool's max size reached (" + MAX_NUM + ") !"); return null; } else { retval=new ReusableThread(); pool[current_index++]=retval; return retval; } } } public void destroy() { deletePool(); } public String toString() { StringBuilder ret=new StringBuilder(); synchronized(pool) { ret.append("ThreadPool: capacity=" + pool.length + ", index=" + current_index + '\n'); ret.append("Threads are:\n"); for(int i=0; i < current_index; i++) ret.append("[" + i + ": " + pool[i] + "]\n"); } return ret.toString(); } void deletePool() { ReusableThread t; synchronized(pool) { for(int i=0; i < MAX_NUM; i++) { t=pool[i]; if(t != null) { t.stop(); pool[i]=null; } } current_index=0; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/AgeOutCache.java0000644000175000017500000000550711366547366025344 0ustar twernertwernerpackage org.jgroups.util; import java.util.Collection; import java.util.Map; import java.util.concurrent.*; /** Cache which removes its elements after a certain time * @author Bela Ban * @version $Id: AgeOutCache.java,v 1.5.2.2 2009/09/10 20:49:39 belaban Exp $ */ public class AgeOutCache { private final ScheduledExecutorService timer; private long timeout; private final ConcurrentMap map=new ConcurrentHashMap(); private Handler handler=null; public interface Handler { void expired(K key); } public AgeOutCache(ScheduledExecutorService timer, long timeout) { this.timer=timer; this.timeout=timeout; } public AgeOutCache(ScheduledExecutorService timer, long timeout, Handler handler) { this(timer, timeout); this.handler=handler; } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout=timeout; } public Handler getHandler() { return handler; } public void setHandler(Handler handler) { this.handler=handler; } public void add(final K key) { ScheduledFuture future=timer.schedule(new Runnable() { public void run() { if(handler != null) { try { handler.expired(key); } catch(Throwable t) { } } ScheduledFuture tmp=map.remove(key); if(tmp != null) tmp.cancel(true); } }, timeout, TimeUnit.MILLISECONDS); ScheduledFuture result=map.putIfAbsent(key, future); if(result != null) future.cancel(true); } public boolean contains(K key) { return key != null && map.containsKey(key); } public void remove(K key) { ScheduledFuture future=map.remove(key); if(future != null) future.cancel(true); } public void removeAll(Collection keys) { if(keys != null) { for(K key: keys) remove(key); } } public void clear() { for(ScheduledFuture future: map.values()) future.cancel(true); map.clear(); } public int size() { return map.size(); } public String toString() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: map.entrySet()) { long time_to_expire=entry.getValue().getDelay(TimeUnit.MILLISECONDS); sb.append(entry.getKey()).append(": "); if(time_to_expire > 0) sb.append(time_to_expire).append(" ms to expire\n"); else sb.append("expired"); } return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ExposedByteArrayOutputStream.java0000644000175000017500000001562611366547366031066 0ustar twernertwernerpackage org.jgroups.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Arrays; /** * Extends ByteArrayOutputStream, but exposes the internal buffer. This way we don't need to call * toByteArray() which copies the internal buffer * @author Bela Ban * @version $Id: ExposedByteArrayOutputStream.java,v 1.2.14.4 2008/10/28 14:02:01 belaban Exp $ */ public class ExposedByteArrayOutputStream extends ByteArrayOutputStream { public ExposedByteArrayOutputStream() { super(); } public ExposedByteArrayOutputStream(int size) { super(size); } /** * Resets count and creates a new buf if the current buf is > max_size. This method is not synchronized */ public void reset(int max_size) { reset(); if(buf.length > max_size) { buf=new byte[max_size]; } } public byte[] getRawBuffer() { return buf; } public int getCapacity() { return buf.length; } public void write(int b) { int newcount = count + 1; if (newcount > buf.length) { byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } buf[count] = (byte)b; count = newcount; } /** * Writes len bytes from the specified byte array * starting at offset off to this byte array output stream. * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. */ public void write(byte b[], int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } int newcount = count + len; if (newcount > buf.length) { byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } System.arraycopy(b, off, buf, count, len); count = newcount; } /** * Writes the complete contents of this byte array output stream to * the specified output stream argument, as if by calling the output * stream's write method using out.write(buf, 0, count). * @param out the output stream to which to write the data. * @throws java.io.IOException if an I/O error occurs. */ public synchronized void writeTo(OutputStream out) throws IOException { out.write(buf, 0, count); } /** * Resets the count field of this byte array output * stream to zero, so that all currently accumulated output in the * output stream is discarded. The output stream can be used again, * reusing the already allocated buffer space. * @see java.io.ByteArrayInputStream#count */ public void reset() { count=0; } /** * Creates a newly allocated byte array. Its size is the current * size of this output stream and the valid contents of the buffer * have been copied into it. * @return the current contents of this output stream, as a byte array. * @see java.io.ByteArrayOutputStream#size() */ public synchronized byte toByteArray()[] { byte newbuf[] = new byte[count]; System.arraycopy(buf, 0, newbuf, 0, count); return newbuf; } /** * Returns the current size of the buffer. * @return the value of the count field, which is the number * of valid bytes in this output stream. * @see java.io.ByteArrayOutputStream#count */ public int size() { return count; } /** * Converts the buffer's contents into a string decoding bytes using the * platform's default character set. The length of the new String * is a function of the character set, and hence may not be equal to the * size of the buffer. *

*

This method always replaces malformed-input and unmappable-character * sequences with the default replacement string for the platform's * default character set. The {@linkplain java.nio.charset.CharsetDecoder} * class should be used when more control over the decoding process is * required. * @return String decoded from the buffer's contents. * @since JDK1.1 */ public String toString() { return new String(buf, 0, count); } /** * Converts the buffer's contents into a string by decoding the bytes using * the specified {@link java.nio.charset.Charset charsetName}. The length of * the new String is a function of the charset, and hence may not be * equal to the length of the byte array. *

*

This method always replaces malformed-input and unmappable-character * sequences with this charset's default replacement string. The {@link * java.nio.charset.CharsetDecoder} class should be used when more control * over the decoding process is required. * @param charsetName the name of a supported * {@linkplain java.nio.charset.Charset charset} * @return String decoded from the buffer's contents. * @throws java.io.UnsupportedEncodingException * If the named charset is not supported * @since JDK1.1 */ public String toString(String charsetName) throws UnsupportedEncodingException { return new String(buf, 0, count, charsetName); } /** * Creates a newly allocated string. Its size is the current size of * the output stream and the valid contents of the buffer have been * copied into it. Each character c in the resulting string is * constructed from the corresponding element b in the byte * array such that: *

     *     c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
     * 
* @param hibyte the high byte of each resulting Unicode character. * @return the current contents of the output stream, as a string. * @see java.io.ByteArrayOutputStream#size() * @see java.io.ByteArrayOutputStream#toString(String) * @see java.io.ByteArrayOutputStream#toString() * @deprecated This method does not properly convert bytes into characters. * As of JDK 1.1, the preferred way to do this is via the * toString(String enc) method, which takes an encoding-name * argument, or the toString() method, which uses the * platform's default character encoding. */ @Deprecated public String toString(int hibyte) { return new String(buf, hibyte, 0, count); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/MutableDigest.java0000644000175000017500000001445011366547366025762 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.Address; import java.util.Iterator; import java.util.Map; /** * A mutable version of Digest (which is immutable * @author Bela Ban * @version $Id: MutableDigest.java,v 1.6 2007/05/29 10:10:47 belaban Exp $ */ public class MutableDigest extends Digest { private boolean sealed=false; /** Used for externalization */ public MutableDigest() { super(); } public MutableDigest(int size) { super(size); } public MutableDigest(Map map) { super(map); } public MutableDigest(Digest digest) { super(digest.getSenders()); } public Map getSenders() { return senders; } public void add(Address sender, long low_seqno, long highest_delivered_seqno) { checkSealed(); add(sender, low_seqno, highest_delivered_seqno, -1); } public void add(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { checkSealed(); add(sender, new Digest.Entry(low_seqno, highest_delivered_seqno, highest_received_seqno)); } private void add(Address sender, Entry entry) { if(sender == null || entry == null) { if(log.isErrorEnabled()) log.error("sender (" + sender + ") or entry (" + entry + ")is null, will not add entry"); return; } checkSealed(); Object retval=senders.put(sender, entry); if(retval != null && log.isWarnEnabled()) log.warn("entry for " + sender + " was overwritten with " + entry); } public void add(Digest digest) { if(digest != null) { checkSealed(); Map.Entry entry; Address key; Entry val; for(Iterator> it=digest.senders.entrySet().iterator(); it.hasNext();) { entry=it.next(); key=entry.getKey(); val=entry.getValue(); add(key, val.getLow(), val.getHighestDeliveredSeqno(), val.getHighestReceivedSeqno()); } } } public void replace(Digest d) { if(d != null) { clear(); add(d); } } public boolean set(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { checkSealed(); Entry entry=senders.put(sender, new Entry(low_seqno, highest_delivered_seqno, highest_received_seqno)); return entry == null; } /** * Adds a digest to this digest. This digest must have enough space to add the other digest; otherwise an error * message will be written. For each sender in the other digest, the merge() method will be called. */ public void merge(Digest digest) { if(digest == null) { if(log.isErrorEnabled()) log.error("digest to be merged with is null"); return; } checkSealed(); Map.Entry entry; Address sender; Entry val; for(Iterator> it=digest.senders.entrySet().iterator(); it.hasNext();) { entry=it.next(); sender=entry.getKey(); val=entry.getValue(); if(val != null) { merge(sender, val.getLow(), val.getHighestDeliveredSeqno(), val.getHighestReceivedSeqno()); } } } /** * Similar to add(), but if the sender already exists, its seqnos will be modified (no new entry) as follows: *
    *
  1. this.low_seqno=min(this.low_seqno, low_seqno) *
  2. this.highest_delivered_seqno=max(this.highest_delivered_seqno, highest_delivered_seqno) *
  3. this.highest_received_seqno=max(this.highest_received_seqno, highest_received_seqno) *
* If the sender doesn not exist, a new entry will be added (provided there is enough space) */ public void merge(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { if(sender == null) { if(log.isErrorEnabled()) log.error("sender == null"); return; } checkSealed(); Entry entry=senders.get(sender); if(entry == null) { add(sender, low_seqno, highest_delivered_seqno, highest_received_seqno); } else { Entry new_entry=new Entry(Math.min(entry.getLow(), low_seqno), Math.max(entry.getHighestDeliveredSeqno(), highest_delivered_seqno), Math.max(entry.getHighestReceivedSeqno(), highest_received_seqno)); senders.put(sender, new_entry); } } /** * Increments the sender's high_seqno by 1. */ public void incrementHighestDeliveredSeqno(Address sender) { Entry entry=senders.get(sender); if(entry == null) return; checkSealed(); Entry new_entry=new Entry(entry.getLow(), entry.getHighestDeliveredSeqno() +1, entry.getHighestReceivedSeqno()); senders.put(sender, new_entry); } /** * Resets the seqnos for the sender at 'index' to 0. This happens when a member has left the group, * but it is still in the digest. Resetting its seqnos ensures that no-one will request a message * retransmission from the dead member. */ public void resetAt(Address sender) { Entry entry=senders.get(sender); if(entry != null) checkSealed(); senders.put(sender, new Entry()); } public void clear() { checkSealed(); senders.clear(); } public void setHighestDeliveredAndSeenSeqnos(Address sender, long low_seqno, long highest_delivered_seqno, long highest_received_seqno) { Entry entry=senders.get(sender); if(entry != null) { checkSealed(); Entry new_entry=new Entry(low_seqno, highest_delivered_seqno, highest_received_seqno); senders.put(sender, new_entry); } } /** Seals the instance against modifications */ public boolean seal() { boolean retval=sealed; sealed=true; return retval; } private final void checkSealed() { if(sealed) throw new IllegalAccessError("instance has been sealed and cannot be modified"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ThreadManager.java0000644000175000017500000000127611366547366025735 0ustar twernertwernerpackage org.jgroups.util; /** * An object that manages threads and provides callbacks to a * {@link ThreadDecorator} to allow it to alter their state. * * @author Brian Stansberry * @version $Id: ThreadManager.java,v 1.1.2.1 2008/05/26 09:14:40 belaban Exp $ */ public interface ThreadManager { /** * Gets the ThreadDecorator associated with this manager. * @return the ThreadDecorator, or null if there is none. */ ThreadDecorator getThreadDecorator(); /** * Sets the ThreadDecorator associated this manager should use. * @param decorator the ThreadDecorator, or null. */ void setThreadDecorator(ThreadDecorator decorator); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/Promise.java0000644000175000017500000001020511366547366024641 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.TimeoutException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Allows a thread to submit an asynchronous request and to wait for the result. The caller may choose to check * for the result at a later time, or immediately and it may block or not. Both the caller and responder have to * know the promise. * @author Bela Ban * @version $Id: Promise.java,v 1.12.2.1 2007/11/20 08:37:24 belaban Exp $ */ public class Promise { private final Lock lock=new ReentrantLock(); private final Condition cond=lock.newCondition(); private T result=null; private boolean hasResult=false; public Lock getLock() { return lock; } public Condition getCond() { return cond; } /** * Blocks until a result is available, or timeout milliseconds have elapsed * @param timeout * @return An object * @throws TimeoutException If a timeout occurred (implies that timeout > 0) */ public T getResultWithTimeout(long timeout) throws TimeoutException { lock.lock(); try { return _getResultWithTimeout(timeout); } finally { cond.signalAll(); lock.unlock(); } } /** * Blocks until a result is available, or timeout milliseconds have elapsed. Needs to be called with lock held * @param timeout * @return An object * @throws TimeoutException If a timeout occurred (implies that timeout > 0) */ private T _getResultWithTimeout(long timeout) throws TimeoutException { T ret=null; long time_to_wait=timeout, start; boolean timeout_occurred=false; start=System.currentTimeMillis(); while(hasResult == false) { if(timeout <= 0) { doWait(); } else { if(time_to_wait <= 0) { timeout_occurred=true; break; // terminate the while loop } else { doWait(time_to_wait); time_to_wait=timeout - (System.currentTimeMillis() - start); } } } ret=result; result=null; hasResult=false; if(timeout_occurred) throw new TimeoutException(); else return ret; } public T getResult() { try { return getResultWithTimeout(0); } catch(TimeoutException e) { return null; } } /** * Returns the result, but never throws a TimeoutException; returns null instead. * @param timeout * @return Object */ public T getResult(long timeout) { try { return getResultWithTimeout(timeout); } catch(TimeoutException e) { return null; } } private void doWait() { try {cond.await();} catch(InterruptedException e) {} } private void doWait(long timeout) { try {cond.await(timeout, TimeUnit.MILLISECONDS);} catch(InterruptedException e) {} } /** * Checks whether result is available. Does not block. */ public boolean hasResult() { lock.lock(); try { return hasResult; } finally { lock.unlock(); } } /** * Sets the result and notifies any threads * waiting for it */ public void setResult(T obj) { lock.lock(); try { result=obj; hasResult=true; cond.signalAll(); } finally { lock.unlock(); } } /** * Causes all waiting threads to return */ public void reset() { lock.lock(); try { result=null; hasResult=false; cond.signalAll(); } finally { lock.unlock(); } } public String toString() { return "hasResult=" + Boolean.valueOf(hasResult) + ",result=" + result; } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/ExposedBufferedOutputStream.java0000644000175000017500000000210711366547366030674 0ustar twernertwernerpackage org.jgroups.util; import java.io.BufferedOutputStream; import java.io.OutputStream; /** * @author Bela Ban * @version $Id: ExposedBufferedOutputStream.java,v 1.1 2005/07/25 15:53:36 belaban Exp $ */ public class ExposedBufferedOutputStream extends BufferedOutputStream { /** * Creates a new buffered output stream to write data to the * specified underlying output stream. * * @param out the underlying output stream. */ public ExposedBufferedOutputStream(OutputStream out) { super(out); } /** * Creates a new buffered output stream to write data to the * specified underlying output stream with the specified buffer * size. * * @param out the underlying output stream. * @param size the buffer size. * @throws IllegalArgumentException if size <= 0. */ public ExposedBufferedOutputStream(OutputStream out, int size) { super(out, size); } public void reset(int size) { count=0; if(size > buf.length) { buf=new byte[size]; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/MagicObjectOutputStream.java0000644000175000017500000000341511366547366027774 0ustar twernertwernerpackage org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.conf.ClassConfigurator; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; /** * Uses magic numbers for class descriptors * @author Bela Ban * @version $Id: MagicObjectOutputStream.java,v 1.6 2007/05/01 09:15:17 belaban Exp $ */ public class MagicObjectOutputStream extends ObjectOutputStream { static volatile ClassConfigurator conf=null; static final Log log=LogFactory.getLog(MagicObjectOutputStream.class); public MagicObjectOutputStream(OutputStream out) throws IOException { super(out); if(conf == null) { try { conf=ClassConfigurator.getInstance(false); } catch(ChannelException e) { log.error("ClassConfigurator could not be instantiated", e); } } } protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { short magic_num; if(conf == null) { super.writeShort(-1); super.writeClassDescriptor(desc); return; } magic_num=conf.getMagicNumberFromObjectStreamClass(desc); super.writeShort(magic_num); if(magic_num == -1) { if(log.isTraceEnabled()) // todo: remove log.trace("could not find magic number for '" + desc.getName() + "': writing full class descriptor"); super.writeClassDescriptor(desc); } else { //if(log.isTraceEnabled()) // log.trace("writing descriptor (num=" + magic_num + "): " + desc.getName()); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/FIFOMessageQueue.java0000644000175000017500000001002411366547366026257 0ustar twernertwernerpackage org.jgroups.util; import org.jgroups.Address; import java.util.LinkedList; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * Blocking queue which can only process 1 message per service concurrently, establishing FIFO order per sender. Example: * if message A1, A2, A3, B1, B2 (where A and B are service names for services on top of a Multiplexer) arrive at the * same time, then this class will deliver A1 and B1 concurrently (ie. pass them up to the thread pool for processing). * Only when A1 is done will A2 be processed, same for B2: it will get processed when B1 is done. Thus, messages * for different services are processed concurrently; messages from the same service are processed FIFO. * @author Bela Ban * @version $Id: FIFOMessageQueue.java,v 1.8 2007/06/29 10:57:40 belaban Exp $ */ public class FIFOMessageQueue { /** Used for consolidated takes */ final BlockingQueue queue=new LinkedBlockingQueue(); /** One queue per sender and destination. This is a two level hashmap, with sender's addresses as keys and hashmaps * as values. Those hashmaps have destinations (K) as keys and Entries (list of Vs) as values */ final ConcurrentMap>> queues=new ConcurrentHashMap>>(); private final AtomicInteger size=new AtomicInteger(0); public V take() throws InterruptedException { V retval=queue.take(); if(retval != null) size.decrementAndGet(); return retval; } public V poll(long timeout) throws InterruptedException { V retval=queue.poll(timeout, TimeUnit.MILLISECONDS); if(retval != null) size.decrementAndGet(); return retval; } public void put(Address sender, K dest, V el) throws InterruptedException { if(sender == null) { size.incrementAndGet(); queue.add(el); return; } ConcurrentMap> dests=queues.get(sender); if(dests == null) { dests=new ConcurrentHashMap>(); if(queues.putIfAbsent(sender, dests) != null) // already existed (someone else inserted the key/value mapping) dests=queues.get(sender); } Entry entry=dests.get(dest); if(entry == null) { entry=new Entry(); if(dests.putIfAbsent(dest, entry) != null) entry=dests.get(dest); } synchronized(entry) { size.incrementAndGet(); if(entry.ready) { entry.ready=false; queue.add(el); } else { entry.list.add(el); } } } public void done(Address sender, K dest) { if(sender == null) return; Map> dests=queues.get(sender); if(dests == null) return; Entry entry=dests.get(dest); if(entry != null) { V el=null; synchronized(entry) { if(!entry.list.isEmpty()) { el=entry.list.removeFirst(); queue.add(el); } else { entry.ready=true; } } } } public int size() { return size.get(); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("queue: ").append(queue).append("\nqueues:\n"); for(ConcurrentMap.Entry>> entry: queues.entrySet()) { sb.append("sender ").append(entry.getKey()).append(":\n"); for(Map.Entry> entry2: entry.getValue().entrySet()) { sb.append(entry2.getKey()).append(": ").append(entry2.getValue().list).append("\n"); } } return sb.toString(); } static class Entry { boolean ready=true; LinkedList list=new LinkedList(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/TimeScheduler.java0000644000175000017500000002302611366547366025765 0ustar twernertwerner package org.jgroups.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Global; import java.util.concurrent.*; /** * Fixed-delay & fixed-rate single thread scheduler *

* The scheduler supports varying scheduling intervals by asking the task * every time for its next preferred scheduling interval. Scheduling can * either be fixed-delay or fixed-rate. The notions are * borrowed from java.util.Timer and retain the same meaning. * I.e. in fixed-delay scheduling, the task's new schedule is calculated * as:
* new_schedule = time_task_starts + scheduling_interval *

* In fixed-rate scheduling, the next schedule is calculated as:
* new_schedule = time_task_was_supposed_to_start + scheduling_interval *

* The scheduler internally holds a queue of tasks sorted in ascending order * according to their next execution time. A task is removed from the queue * if it is cancelled, i.e. if TimeScheduler.Task.isCancelled() * returns true. *

* The scheduler internally uses a java.util.SortedSet to keep tasks * sorted. java.util.Timer uses an array arranged as a binary heap * that doesn't shrink. It is likely that the latter arrangement is faster. *

* Initially, the scheduler is in SUSPENDed mode, start() * need not be called: if a task is added, the scheduler gets started * automatically. Calling start() starts the scheduler if it's * suspended or stopped else has no effect. Once stop() is called, * added tasks will not restart it: start() has to be called to * restart the scheduler. * @author Bela Ban * @version $Id: TimeScheduler.java,v 1.23.4.9 2009/09/29 04:39:37 belaban Exp $ */ public class TimeScheduler extends ScheduledThreadPoolExecutor implements ThreadManager { /** The interface that submitted tasks must implement */ public interface Task extends Runnable { /** @return the next schedule interval. If <= 0 the task will not be re-scheduled */ long nextInterval(); } /** How many core threads */ private static int TIMER_DEFAULT_NUM_THREADS=3; protected static final Log log=LogFactory.getLog(TimeScheduler.class); static { String tmp; try { tmp=System.getProperty(Global.TIMER_NUM_THREADS); if(tmp != null) TIMER_DEFAULT_NUM_THREADS=Integer.parseInt(tmp); } catch(Exception e) { log.error("could not set number of timer threads", e); } } private ThreadDecorator threadDecorator=null; /** * Create a scheduler that executes tasks in dynamically adjustable intervals */ public TimeScheduler() { this(TIMER_DEFAULT_NUM_THREADS); } public TimeScheduler(ThreadFactory factory) { this(factory, TIMER_DEFAULT_NUM_THREADS); } public TimeScheduler(ThreadFactory factory, int max_threads) { super(max_threads, factory); setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(getRejectedExecutionHandler())); } public TimeScheduler(int corePoolSize) { super(corePoolSize); setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(getRejectedExecutionHandler())); } public ThreadDecorator getThreadDecorator() { return threadDecorator; } public void setThreadDecorator(ThreadDecorator threadDecorator) { this.threadDecorator=threadDecorator; } public String dumpTaskQueue() { return getQueue().toString(); } /** * Schedule a task for execution at varying intervals. After execution, the task will get rescheduled after * {@link org.jgroups.util.TimeScheduler.Task#nextInterval()} milliseconds. The task is neve done until nextInterval() * return a value <= 0 or the task is cancelled. * @param task the task to execute * @param relative scheduling scheme: true:
* Task is rescheduled relative to the last time it actually started execution

* false:
Task is scheduled relative to its last execution schedule. This has the effect * that the time between two consecutive executions of the task remains the same.

* Note that relative is always true; we always schedule the next execution relative to the last *actual* * (not scheduled) execution */ public ScheduledFuture scheduleWithDynamicInterval(Task task) { if(task == null) throw new NullPointerException(); if (isShutdown()) return null; TaskWrapper task_wrapper=new TaskWrapper(task); task_wrapper.doSchedule(); // calls schedule() in ScheduledThreadPoolExecutor return task_wrapper; } public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return super.scheduleWithFixedDelay(new RobustRunnable(command), initialDelay, delay, unit); } /** * Answers the number of tasks currently in the queue. * @return The number of tasks currently in the queue. */ public int size() { return getQueue().size(); } /** * Stop the scheduler if it's running. Switch to stopped, if it's * suspended. Clear the task queue, cancelling all un-executed tasks * * @throws InterruptedException if interrupted while waiting for thread * to return */ public void stop() throws InterruptedException { java.util.List tasks=shutdownNow(); for(Runnable task: tasks) { if(task instanceof Future) { Future future=(Future)task; future.cancel(true); } } getQueue().clear(); awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } @Override protected void afterExecute(Runnable r, Throwable t) { try { super.afterExecute(r, t); } finally { if(threadDecorator != null) threadDecorator.threadReleased(Thread.currentThread()); } } /** * Class which catches exceptions in run() - https://jira.jboss.org/jira/browse/JGRP-1062 */ static class RobustRunnable implements Runnable { final Runnable command; public RobustRunnable(Runnable command) { this.command=command; } public void run() { if(command != null) { try { command.run(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("exception executing task " + command, t); } } } } private class TaskWrapper implements Runnable, ScheduledFuture { private final Task task; private volatile ScheduledFuture future; // cannot be null ! private volatile boolean cancelled=false; public TaskWrapper(Task task) { this.task=task; } public ScheduledFuture getFuture() { return future; } public void run() { try { if(cancelled) { if(future != null) future.cancel(true); return; } if(future != null && future.isCancelled()) return; task.run(); } catch(Throwable t) { log.error("failed running task " + task, t); } if(cancelled) { if(future != null) future.cancel(true); return; } if(future != null && future.isCancelled()) return; doSchedule(); } public void doSchedule() { long next_interval=task.nextInterval(); if(next_interval <= 0) { if(log.isTraceEnabled()) log.trace("task will not get rescheduled as interval is " + next_interval); } else { future=schedule(this, next_interval, TimeUnit.MILLISECONDS); if(cancelled) future.cancel(true); } } public int compareTo(Delayed o) { long my_delay=future.getDelay(TimeUnit.MILLISECONDS), their_delay=o.getDelay(TimeUnit.MILLISECONDS); return my_delay < their_delay? -1 : my_delay > their_delay? 1 : 0; } public boolean equals(Object obj) { Delayed other=(Delayed)obj; return compareTo(other) == 0; } public long getDelay(TimeUnit unit) { return future != null? future.getDelay(unit) : -1; } public boolean cancel(boolean mayInterruptIfRunning) { cancelled=true; if(future != null) future.cancel(mayInterruptIfRunning); return cancelled; } public boolean isCancelled() { return cancelled || (future != null && future.isCancelled()); } public boolean isDone() { return future == null || future.isDone(); } public V get() throws InterruptedException, ExecutionException { return null; } public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return null; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/util/DefaultThreadFactory.java0000644000175000017500000001056311366547366027276 0ustar twernertwernerpackage org.jgroups.util; /** * Thread factory mainly responsible for naming of threads. Can be replaced by * user. If use_numbering is set, a thread THREAD will be called THREAD-1, * THREAD-2, and so on.

If a pattern has been set (through setPattern()), * then the cluster name and local address will also be added, e.g. * THREAD-5,MyCluster,192.168.1.5:63754 or THREAD,MyCluster,192.168.1.5:63754 * * @author Vladimir Blagojevic * @author Bela Ban * @version $Id: DefaultThreadFactory.java,v 1.3.2.4 2008/05/26 09:14:38 belaban * Exp $ */ public class DefaultThreadFactory implements ThreadFactory, ThreadManager { protected final ThreadGroup group; protected final String baseName; protected final boolean createDaemons; protected short counter=0; // if numbering is enabled protected final boolean use_numbering; protected boolean includeClusterName=false; protected boolean includeLocalAddress=false; protected String clusterName=null; protected String address=null; protected ThreadDecorator threadDecorator=null; public DefaultThreadFactory(ThreadGroup group,String baseName,boolean createDaemons) { this(group, baseName, createDaemons, false); } public DefaultThreadFactory(ThreadGroup group, String baseName, boolean createDaemons, boolean use_numbering) { this.group=group; this.baseName=baseName; this.createDaemons=createDaemons; this.use_numbering=use_numbering; } public void setPattern(String pattern) { if(pattern != null) { includeClusterName=pattern.contains("c"); includeLocalAddress=pattern.contains("l"); } } public void setIncludeClusterName(boolean includeClusterName) { this.includeClusterName=includeClusterName; } public void setClusterName(String channelName) { clusterName=channelName; } public void setAddress(String address) { this.address=address; } public ThreadDecorator getThreadDecorator() { return threadDecorator; } public void setThreadDecorator(ThreadDecorator threadDecorator) { this.threadDecorator=threadDecorator; } public Thread newThread(Runnable r, String name) { return newThread(group, r, name); } public Thread newThread(Runnable r) { return newThread(group, r, baseName); } public Thread newThread(ThreadGroup group, Runnable r, String name) { return newThread(group, r, name, null, null); } protected Thread newThread(ThreadGroup group, Runnable r, String name, String address, String cluster_name) { Thread retval=new Thread(group, r, name); retval.setDaemon(createDaemons); renameThread(retval, address, cluster_name); if(threadDecorator != null) threadDecorator.threadCreated(retval); return retval; } public void renameThread(String base_name, Thread thread) { renameThread(base_name, thread, address, clusterName); } public void renameThread(String base_name, Thread thread, String address, String cluster_name) { if(thread == null) return; StringBuilder sb=new StringBuilder(base_name != null? base_name : thread.getName()); if(use_numbering) { short id; synchronized(this) { id=++counter; } sb.append("-" + id); } if(includeClusterName) { sb.append(','); if(cluster_name != null) sb.append(cluster_name); else sb.append(this.clusterName); } if(includeLocalAddress) { sb.append(','); if(address != null) sb.append(address); else sb.append(this.address); } if(use_numbering || includeClusterName || includeLocalAddress) thread.setName(sb.toString()); } protected void renameThread(Thread thread, String address, String cluster_name) { renameThread(null, thread, address, cluster_name); } public void renameThread(Thread thread) { renameThread(null, thread); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ViewId.java0000644000175000017500000000735111366547366023445 0ustar twernertwerner// $Id: ViewId.java,v 1.11 2006/08/14 16:05:09 belaban Exp $ package org.jgroups; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; /** * ViewIds are used for ordering views (each view has a ViewId and a list of members). * Ordering between views is important for example in a virtual synchrony protocol where * all views seen by a member have to be ordered. */ public class ViewId implements Externalizable, Comparable, Cloneable, Streamable { Address coord_addr=null; // Address of the issuer of this view long id=0; // Lamport time of the view public ViewId() { // used for externalization } /** * Creates a ViewID with the coordinator address and a Lamport timestamp of 0. * * @param coord_addr the address of the member that issued this view */ public ViewId(Address coord_addr) { this.coord_addr=coord_addr; } /** * Creates a ViewID with the coordinator address and the given Lamport timestamp. * * @param coord_addr - the address of the member that issued this view * @param id - the Lamport timestamp of the view */ public ViewId(Address coord_addr, long id) { this.coord_addr=coord_addr; this.id=id; } /** * returns the lamport time of the view * * @return the lamport time timestamp */ public long getId() { return id; } /** * returns the address of the member that issued this view * * @return the Address of the the issuer */ public Address getCoordAddress() { return coord_addr; } public String toString() { return "[" + coord_addr + '|' + id + ']'; } /** * Cloneable interface * Returns a new ViewID object containing the same address and lamport timestamp as this view */ public Object clone() { return new ViewId(coord_addr, id); } /** * Old Copy method, deprecated because it is substituted by clone() */ public ViewId copy() { return (ViewId)clone(); } /** * Establishes an order between 2 ViewIds. First compare on id. Compare on coord_addr * only if necessary (i.e. ids are equal) ! * * @return 0 for equality, value less than 0 if smaller, greater than 0 if greater. */ public int compareTo(Object other) { if(other == null) return 1; //+++ Maybe necessary to throw an exception if(!(other instanceof ViewId)) { throw new ClassCastException("ViewId.compareTo(): view id is not comparable with different Objects"); } return id > ((ViewId)other).id ? 1 : id < ((ViewId)other).id ? -1 : 0; } /** * Old Compare */ public int compare(Object o) { return compareTo(o); } public boolean equals(Object other_view) { return compareTo(other_view) == 0; } public int hashCode() { return (int)id; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(coord_addr); out.writeLong(id); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { coord_addr=(Address)in.readObject(); id=in.readLong(); } public void writeTo(DataOutputStream out) throws IOException { Util.writeAddress(coord_addr, out); out.writeLong(id); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { coord_addr=Util.readAddress(in); id=in.readLong(); } public int serializedSize() { int retval=Global.LONG_SIZE; // for the id retval+=Util.size(coord_addr); return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/StreamingGetStateEvent.java0000644000175000017500000000436011366547366026647 0ustar twernertwernerpackage org.jgroups; import java.io.OutputStream; /** * * Represents an event returned by channel.receive(), as a result * of another channel instance requesting a state from this channel. Other channel * has to invoke channel.getState() indicating intent of state * retrieval. * *

* * Allows applications using a channel in a pull mode to receive * StreamingGetStateEvent event and thus provide state to requsting * channel instance. Channels have to be configured with * STREAMING_STATE_TRANSFER protocol rather than the default * STATE_TRANSFER protocol in order to receive this event * *

* * The following code demonstrates how to pull events from a channel, processing * StreamingGetStateEvent and sending hypothetical state through * OutputStream reference. * *

 *  Object obj=channel.receive(0);
 *  if(obj instanceof StreamingGetStateEvent) {
 *   	StreamingGetStateEvent evt=(StreamingGetStateEvent)obj;
 *    	OutputStream oos = null;
 *		try {			
 *			oos = new ObjectOutputStream(evt.getArg());			
 *			oos.writeObject(state);   
 *	    	oos.flush();
 *		} catch (Exception e) {} 
 *		finally
 *		{
 *			try {				
 *				oos.close();
 *			} catch (IOException e) {
 *				System.err.println(e);
 *			}
 *		}                
 *   }
 * 
* * * @author Vladimir Blagojevic * @see org.jgroups.JChannel#getState(Address, long) * @see org.jgroups.StreamingMessageListener#getState(OutputStream) * @since 2.4 * */ public class StreamingGetStateEvent { OutputStream os; String state_id; public StreamingGetStateEvent(OutputStream os,String state_id) { super(); this.os=os; this.state_id=state_id; } /** * Returns OutputStream used for writing of a state. * * @return the OutputStream */ public OutputStream getArg() { return os; } /** * Returns id of the partial state if partial state was requested. * If full state transfer was requested this method will return null. * * @see JChannel#getState(Address, long) * @see JChannel#getState(Address, String, long) * @return partial state id */ public String getStateId() { return state_id; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/SuspectedException.java0000644000175000017500000000077511366547366026077 0ustar twernertwerner// $Id: SuspectedException.java,v 1.4 2006/04/29 03:25:32 belaban Exp $ package org.jgroups; /** * Thrown if a message is sent to a suspected member. */ public class SuspectedException extends Exception { final Object suspect; private static final long serialVersionUID=-6663279911010545655L; public SuspectedException() {this.suspect=null;} public SuspectedException(Object suspect) {this.suspect=suspect;} public String toString() {return "SuspectedException";} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/0000755000175000017500000000000011621261110022630 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/NotificationBus.java0000644000175000017500000003323711366547366026635 0ustar twernertwerner// $Id: NotificationBus.java,v 1.16 2007/08/30 10:07:37 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.util.Promise; import org.jgroups.util.Util; import java.io.Serializable; import java.util.Vector; /** * This class provides notification sending and handling capability. * Producers can send notifications to all registered consumers. * Provides hooks to implement shared group state, which allows an * application programmer to maintain a local cache which is replicated * by all instances. NotificationBus sits on * top of a channel, however it creates its channel itself, so the * application programmers do not have to provide their own channel. * * @author Bela Ban */ public class NotificationBus implements Receiver { final Vector members=new Vector(); Channel channel=null; Address local_addr=null; Consumer consumer=null; // only a single consumer allowed String bus_name="notification_bus"; final Promise get_cache_promise=new Promise(); final Object cache_mutex=new Object(); protected final Log log=LogFactory.getLog(getClass()); String props=null; public interface Consumer { void handleNotification(Serializable n); /** Called on the coordinator to obtains its cache */ Serializable getCache(); void memberJoined(Address mbr); void memberLeft(Address mbr); } public NotificationBus() throws Exception { this((Channel)null, null); } public NotificationBus(String bus_name) throws Exception { this(bus_name, null); } public NotificationBus(String bus_name, String properties) throws Exception { if(bus_name != null) this.bus_name=bus_name; if(properties != null) props=properties; channel=new JChannel(props); channel.setReceiver(this); } public NotificationBus(Channel channel, String bus_name) throws Exception { if(bus_name != null) this.bus_name=bus_name; this.channel=channel; if(channel != null) channel.setReceiver(this); } public void setConsumer(Consumer c) { consumer=c; } public Address getLocalAddress() { if(local_addr != null) return local_addr; if(channel != null) local_addr=channel.getLocalAddress(); return local_addr; } /** * Returns a reference to the real membership: don't modify. * If you need to modify, make a copy first ! * @return Vector of Address objects */ public Vector getMembership() { return members; } /** * Answers the Channel. * Used to operate on the underlying channel directly, e.g. perform operations that are not * provided using only NotificationBus. Should be used sparingly. * @return underlying Channel */ public Channel getChannel() { return channel; } public boolean isCoordinator() { Object first_mbr=null; synchronized(members) { first_mbr=!members.isEmpty()? members.elementAt(0) : null; if(first_mbr == null) return true; } return getLocalAddress() != null && getLocalAddress().equals(first_mbr); } public void start() throws Exception { channel.connect(bus_name); } public void stop() { if(channel != null) { channel.close(); // disconnects from channel and closes it channel=null; } } /** Pack the argument in a Info, serialize that one into the message buffer and send the message */ public void sendNotification(Serializable n) { sendNotification(null, n); } /** Pack the argument in a Info, serialize that one into the message buffer and send the message */ public void sendNotification(Address dest, Serializable n) { Message msg=null; byte[] data=null; Info info; try { if(n == null) return; info=new Info(Info.NOTIFICATION, n); data=Util.objectToByteBuffer(info); msg=new Message(dest, null, data); if(channel == null) { if(log.isErrorEnabled()) log.error("channel is null. Won't send notification"); return; } channel.send(msg); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("error sending notification", ex); } } /** Determines the coordinator and asks it for its cache. If there is no coordinator (because we are first member), null will be returned. Used only internally by NotificationBus. @param timeout Max number of msecs until the call returns @param max_tries Max number of attempts to fetch the cache from the coordinator */ public Serializable getCacheFromCoordinator(long timeout, int max_tries) { return getCacheFromMember(null, timeout, max_tries); } /** Determines the coordinator and asks it for its cache. If there is no coordinator (because we are first member), null will be returned. Used only internally by NotificationBus. @param mbr The address of the member from which to fetch the state. If null, the current coordinator will be asked for the state @param timeout Max number of msecs until the call returns - if timeout elapses null will be returned @param max_tries Max number of attempts to fetch the cache from the coordinator (will be set to 1 if < 1) */ public Serializable getCacheFromMember(Address mbr, long timeout, int max_tries) { Serializable cache=null; int num_tries=0; Info info; Message msg; Address dst=mbr; // member from which to fetch the cache long start, stop; // +++ remove if(max_tries < 1) max_tries=1; get_cache_promise.reset(); while(num_tries <= max_tries) { if(mbr == null) { // mbr == null means get cache from coordinator dst=determineCoordinator(); if(dst == null || dst.equals(getLocalAddress())) { // we are the first member --> empty cache if(log.isInfoEnabled()) log.info("[" + getLocalAddress() + "] no coordinator found --> first member (cache is empty)"); return null; } } // +++ remove if(log.isInfoEnabled()) log.info("[" + getLocalAddress() + "] dst=" + dst + ", timeout=" + timeout + ", max_tries=" + max_tries + ", num_tries=" + num_tries); info=new Info(Info.GET_CACHE_REQ); msg=new Message(dst, null, info); try { channel.send(msg); } catch(Exception e) { log.error("failed sending message", e); return null; } start=System.currentTimeMillis(); cache=get_cache_promise.getResult(timeout); stop=System.currentTimeMillis(); if(cache != null) { if(log.isInfoEnabled()) log.info("got cache from " + dst + ": cache is valid (waited " + (stop - start) + " msecs on get_cache_promise)"); return cache; } else { if(log.isErrorEnabled()) log.error("received null cache; retrying (waited " + (stop - start) + " msecs on get_cache_promise)"); } Util.sleep(500); ++num_tries; } if(cache == null) if(log.isErrorEnabled()) log.error("[" + getLocalAddress() + "] cache is null (num_tries=" + num_tries + ')'); return cache; } /** Don't multicast this to all members, just apply it to local consumers. */ public void notifyConsumer(Serializable n) { if(consumer != null && n != null) consumer.handleNotification(n); } /* -------------------------------- Interface MessageListener -------------------------------- */ public void receive(Message msg) { Info info=null; Object obj; if(msg == null || msg.getLength() == 0) return; try { obj=msg.getObject(); if(!(obj instanceof Info)) { if(log.isErrorEnabled()) log.error("expected an instance of Info (received " + obj.getClass().getName() + ')'); return; } info=(Info) obj; switch(info.type) { case Info.NOTIFICATION: notifyConsumer(info.data); break; case Info.GET_CACHE_REQ: handleCacheRequest(msg.getSrc()); break; case Info.GET_CACHE_RSP: // +++ remove if(log.isDebugEnabled()) log.debug("[GET_CACHE_RSP] cache was received from " + msg.getSrc()); get_cache_promise.setResult(info.data); break; default: if(log.isErrorEnabled()) log.error("type " + info.type + " unknown"); break; } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } public byte[] getState() { return null; } public void setState(byte[] state) { } /* ----------------------------- End of Interface MessageListener ---------------------------- */ /* ------------------------------- Interface MembershipListener ------------------------------ */ public synchronized void viewAccepted(View new_view) { Vector joined_mbrs, left_mbrs, tmp; Object tmp_mbr; if(new_view == null) return; tmp=new_view.getMembers(); synchronized(members) { // get new members joined_mbrs=new Vector(); for(int i=0; i < tmp.size(); i++) { tmp_mbr=tmp.elementAt(i); if(!members.contains(tmp_mbr)) joined_mbrs.addElement(tmp_mbr); } // get members that left left_mbrs=new Vector(); for(int i=0; i < members.size(); i++) { tmp_mbr=members.elementAt(i); if(!tmp.contains(tmp_mbr)) left_mbrs.addElement(tmp_mbr); } // adjust our own membership members.removeAllElements(); members.addAll(tmp); } if(consumer != null) { if(!joined_mbrs.isEmpty()) for(int i=0; i < joined_mbrs.size(); i++) consumer.memberJoined((Address) joined_mbrs.elementAt(i)); if(!left_mbrs.isEmpty()) for(int i=0; i < left_mbrs.size(); i++) consumer.memberLeft((Address) left_mbrs.elementAt(i)); } } public void suspect(Address suspected_mbr) { } public void block() { } /* ----------------------------- End of Interface MembershipListener ------------------------- */ /* ------------------------------------- Private Methods ------------------------------------- */ Address determineCoordinator() { Vector v=channel != null ? channel.getView().getMembers() : null; return v != null ? (Address) v.elementAt(0) : null; } void handleCacheRequest(Address sender) throws ChannelException { Serializable cache=null; Message msg; Info info; if(sender == null) { // +++ remove // if(log.isErrorEnabled()) log.error("sender is null"); return; } synchronized(cache_mutex) { cache=getCache(); // get the cache from the consumer info=new Info(Info.GET_CACHE_RSP, cache); msg=new Message(sender, null, info); if(log.isInfoEnabled()) log.info("[" + getLocalAddress() + "] returning cache to " + sender); channel.send(msg); } } public Serializable getCache() { return consumer != null ? consumer.getCache() : null; } /* --------------------------------- End of Private Methods ---------------------------------- */ private static class Info implements Serializable { public final static int NOTIFICATION=1; public final static int GET_CACHE_REQ=2; public final static int GET_CACHE_RSP=3; int type=0; Serializable data=null; // if type == NOTIFICATION data is notification, if type == GET_CACHE_RSP, data is cache private static final long serialVersionUID = -7198723001828406107L; public Info(int type) { this.type=type; } public Info(int type, Serializable data) { this.type=type; this.data=data; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("type= "); if(type == NOTIFICATION) sb.append("NOTIFICATION"); else if(type == GET_CACHE_REQ) sb.append("GET_CACHE_REQ"); else if(type == GET_CACHE_RSP) sb.append("GET_CACHE_RSP"); else sb.append(""); if(data != null) { if(type == NOTIFICATION) sb.append(", notification=" + data); else if(type == GET_CACHE_RSP) sb.append(", cache=" + data); } return sb.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/MembershipListenerAdapter.java0000644000175000017500000000654211366547366030636 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.MembershipListener; import org.jgroups.View; import org.jgroups.ExtendedMembershipListener; import java.util.HashSet; /** * This class provides multiplexing possibilities for {@link MembershipListener} * instances. Usually, we have more than one instance willing to listen to * membership messages. {@link PullPushAdapter} allows only one instance of * {@link MembershipListener} to be registered for message notification. With * help of this class you can overcome this limitation. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public class MembershipListenerAdapter implements MembershipListener { protected final HashSet membershipListeners = new HashSet(); protected MembershipListener[] membershipListenersCache = new MembershipListener[0]; /** * Notify membership listeners to temporarily stop sending messages into * a channel. This method in turn calls same method of all registered * membership listener. */ public void block() { for(int i = 0; i < membershipListenersCache.length; i++) membershipListenersCache[i].block(); } public void unblock() { for(int i = 0; i < membershipListenersCache.length; i++) { if(membershipListenersCache[i] instanceof ExtendedMembershipListener) ((ExtendedMembershipListener)membershipListenersCache[i]).unblock(); } } /** * Notify membership listener that some node was suspected. This method * in turn passes suspected member address to all registered membership * listeners. */ public void suspect(Address suspected_mbr) { for(int i = 0; i < membershipListenersCache.length; i++) membershipListenersCache[i].suspect(suspected_mbr); } /** * Notify membership listener that new view was accepted. This method in * turn passes new view to all registered membership listeners. */ public void viewAccepted(View new_view) { for(int i = 0; i < membershipListenersCache.length; i++) membershipListenersCache[i].viewAccepted(new_view); } /** * Add membership listener to this adapter. This method registers * listener to be notified when membership event is generated. * * @param listener instance of {@link MembershipListener} that should be * added to this adapter. */ public synchronized void addMembershipListener(MembershipListener listener) { if (membershipListeners.add(listener)) membershipListenersCache = (MembershipListener[])membershipListeners.toArray( new MembershipListener[membershipListeners.size()]); } /** * Remove membership listener from this adapter. This method deregisters * listener from notification when membership event is generated. * * @param listener instance of {@link MembershipListener} that should be * removed from this adapter. */ public synchronized void removeMembershipListener(MembershipListener listener) { if (membershipListeners.remove(listener)) membershipListenersCache = (MembershipListener[])membershipListeners.toArray( new MembershipListener[membershipListeners.size()]); } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/PullPushAdapter.java0000644000175000017500000004103211366547366026602 0ustar twernertwerner// $Id: PullPushAdapter.java,v 1.25 2007/05/01 10:55:18 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.util.Util; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; /** * Allows a client of {@link org.jgroups.Channel} to be notified when messages have been received * instead of having to actively poll the channel for new messages. Typically used in the * client role (receive()). As this class does not implement interface * {@link org.jgroups.Transport}, but uses it for receiving messages, an underlying object * has to be used to send messages (e.g. the channel on which an object of this class relies).

* Multiple MembershipListeners can register with the PullPushAdapter; when a view is received, they * will all be notified. There is one main message listener which sends and receives message. In addition, * MessageListeners can register with a certain tag (identifier), and then send messages tagged with this * identifier. When a message with such an identifier is received, the corresponding MessageListener will be * looked up and the message dispatched to it. If no tag is found (default), the main MessageListener will * receive the message. * @author Bela Ban * @version $Revision * @deprecated Use {@link org.jgroups.Receiver} instead, this class will be removed in JGroups 3.0 */ public class PullPushAdapter implements Runnable, ChannelListener { protected Transport transport=null; protected MessageListener listener=null; // main message receiver protected final List membership_listeners=new ArrayList(); protected Thread receiver_thread=null; protected final HashMap listeners=new HashMap(); // keys=identifier (Serializable), values=MessageListeners protected final Log log=LogFactory.getLog(getClass()); static final String PULL_HEADER="PULL_HEADER"; public PullPushAdapter(Transport transport) { this.transport=transport; start(); } public PullPushAdapter(Transport transport, MessageListener l) { this.transport=transport; setListener(l); start(); } public PullPushAdapter(Transport transport, MembershipListener ml) { this.transport=transport; addMembershipListener(ml); start(); } public PullPushAdapter(Transport transport, MessageListener l, MembershipListener ml) { this.transport=transport; setListener(l); addMembershipListener(ml); start(); } public PullPushAdapter(Transport transport, MessageListener l, MembershipListener ml, boolean start) { this.transport=transport; setListener(l); addMembershipListener(ml); if(start) start(); } public Transport getTransport() { return transport; } public final void start() { if(receiver_thread == null || !receiver_thread.isAlive()) { receiver_thread=new Thread(this, "PullPushAdapterThread"); receiver_thread.setDaemon(true); receiver_thread.start(); } if(transport instanceof JChannel) ((JChannel)transport).addChannelListener(this); } public void stop() { Thread tmp=null; if(receiver_thread != null && receiver_thread.isAlive()) { tmp=receiver_thread; receiver_thread=null; tmp.interrupt(); try { tmp.join(1000); } catch(Exception ex) { } } receiver_thread=null; } /** * Sends a message to the group - listeners to this identifier will receive the messages. * @param identifier the key that the proper listeners are listenting on * @param msg the Message to be sent * @see #registerListener */ public void send(Serializable identifier, Message msg) throws Exception { if(msg == null) { if(log.isErrorEnabled()) log.error("msg is null"); return; } if(identifier == null) transport.send(msg); else { msg.putHeader(PULL_HEADER, new PullHeader(identifier)); transport.send(msg); } } /** * Sends a message with no identifier; listener member will get this message on the other group members. * @param msg the Message to be sent * @throws Exception */ public void send(Message msg) throws Exception { send(null, msg); } public final void setListener(MessageListener l) { listener=l; } /** * Sets a listener to messages with a given identifier. * Messages sent with this identifier in their headers will be routed to this listener. * Note: there can be only one listener for one identifier; * if you want to register a different listener to an already registered identifier, then unregister first. * @param identifier - messages sent on the group with this object will be received by this listener * @param l - the listener that will get the message */ public void registerListener(Serializable identifier, MessageListener l) { if(l == null || identifier == null) { if(log.isErrorEnabled()) log.error("message listener or identifier is null"); return; } if(listeners.containsKey(identifier)) { if(log.isErrorEnabled()) log.error("listener with identifier=" + identifier + " already exists, choose a different identifier or unregister current listener"); // we do not want to overwrite the listener return; } listeners.put(identifier, l); } /** * Removes a message listener to a given identifier from the message listeners map. * @param identifier - the key to whom we do not want to listen any more */ public void unregisterListener(Serializable identifier) { listeners.remove(identifier); } /** @deprecated Use {@link #addMembershipListener} */ public void setMembershipListener(MembershipListener ml) { addMembershipListener(ml); } public final void addMembershipListener(MembershipListener l) { if(l != null && !membership_listeners.contains(l)) membership_listeners.add(l); } public void removeMembershipListener(MembershipListener l) { if(l != null && membership_listeners.contains(l)) membership_listeners.remove(l); } /** * Reentrant run(): message reception is serialized, then the listener is notified of the * message reception */ public void run() { Object obj; while(receiver_thread != null && Thread.currentThread().equals(receiver_thread)) { try { obj=transport.receive(0); if(obj == null) continue; if(obj instanceof Message) { handleMessage((Message)obj); } else if(obj instanceof GetStateEvent) { byte[] retval=null; GetStateEvent evt=(GetStateEvent)obj; String state_id=evt.getStateId(); if(listener != null) { try { if(listener instanceof ExtendedMessageListener && state_id!=null) { retval=((ExtendedMessageListener)listener).getState(state_id); } else { retval=listener.getState(); } } catch(Throwable t) { log.error("getState() from application failed, will return empty state", t); } } else { log.warn("no listener registered, returning empty state"); } if(transport instanceof Channel) { ((Channel)transport).returnState(retval, state_id); } else { if(log.isErrorEnabled()) log.error("underlying transport is not a Channel, but a " + transport.getClass().getName() + ": cannot return state using returnState()"); } } else if(obj instanceof SetStateEvent) { SetStateEvent evt=(SetStateEvent)obj; String state_id=evt.getStateId(); if(listener != null) { try { if(listener instanceof ExtendedMessageListener && state_id!=null) { ((ExtendedMessageListener)listener).setState(state_id, evt.getArg()); } else { listener.setState(evt.getArg()); } } catch(ClassCastException cast_ex) { if(log.isErrorEnabled()) log.error("received SetStateEvent, but argument " + ((SetStateEvent)obj).getArg() + " is not serializable ! Discarding message."); } } } else if(obj instanceof StreamingGetStateEvent) { StreamingGetStateEvent evt=(StreamingGetStateEvent)obj; if(listener instanceof ExtendedMessageListener) { if(evt.getStateId()==null) { ((ExtendedMessageListener)listener).getState(evt.getArg()); } else { ((ExtendedMessageListener)listener).getState(evt.getStateId(),evt.getArg()); } } } else if(obj instanceof StreamingSetStateEvent) { StreamingSetStateEvent evt=(StreamingSetStateEvent)obj; if(listener instanceof ExtendedMessageListener) { if(evt.getStateId()==null) { ((ExtendedMessageListener)listener).setState(evt.getArg()); } else { ((ExtendedMessageListener)listener).setState(evt.getStateId(),evt.getArg()); } } } else if(obj instanceof View) { notifyViewChange((View)obj); } else if(obj instanceof SuspectEvent) { notifySuspect((Address)((SuspectEvent)obj).getMember()); } else if(obj instanceof BlockEvent) { notifyBlock(); if(transport instanceof Channel) { ((Channel)transport).blockOk(); } } else if(obj instanceof UnblockEvent) { notifyUnblock(); } } catch(ChannelNotConnectedException conn) { Address local_addr=((Channel)transport).getLocalAddress(); if(log.isTraceEnabled()) log.trace('[' + (local_addr == null ? "" : local_addr.toString()) + "] channel not connected, exception is " + conn); Util.sleep(1000); receiver_thread=null; break; } catch(ChannelClosedException closed_ex) { Address local_addr=((Channel)transport).getLocalAddress(); if(log.isTraceEnabled()) log.trace('[' + (local_addr == null ? "" : local_addr.toString()) + "] channel closed, exception is " + closed_ex); // Util.sleep(1000); receiver_thread=null; break; } catch(Throwable e) { } } } /** * Check whether the message has an identifier. If yes, lookup the MessageListener associated with the * given identifier in the hashtable and dispatch to it. Otherwise just use the main (default) message * listener */ protected void handleMessage(Message msg) { PullHeader hdr=(PullHeader)msg.getHeader(PULL_HEADER); Serializable identifier; MessageListener l; if(hdr != null && (identifier=hdr.getIdentifier()) != null) { l=(MessageListener)listeners.get(identifier); if(l == null) { if(log.isErrorEnabled()) log.error("received a messages tagged with identifier=" + identifier + ", but there is no registration for that identifier. Will drop message"); } else l.receive(msg); } else { if(listener != null) listener.receive(msg); } } protected void notifyViewChange(View v) { MembershipListener l; if(v == null) return; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); try { l.viewAccepted(v); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of view(" + v + ")", ex); } } } protected void notifySuspect(Address suspected_mbr) { MembershipListener l; if(suspected_mbr == null) return; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); try { l.suspect(suspected_mbr); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of suspect(" + suspected_mbr + ")", ex); } } } protected void notifyBlock() { MembershipListener l; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); try { l.block(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of block()", ex); } } } protected void notifyUnblock() { MembershipListener l; for(Iterator it=membership_listeners.iterator(); it.hasNext();) { l=(MembershipListener)it.next(); if(l instanceof ExtendedMembershipListener){ try { ((ExtendedMembershipListener)l).unblock(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception notifying " + l + " of unblock()", ex); } } } } public void channelConnected(Channel channel) { if(log.isTraceEnabled()) log.trace("channel is connected"); } public void channelDisconnected(Channel channel) { if(log.isTraceEnabled()) log.trace("channel is disconnected"); } public void channelClosed(Channel channel) { } public void channelShunned() { if(log.isTraceEnabled()) log.trace("channel is shunned"); } public void channelReconnected(Address addr) { start(); } public static final class PullHeader extends Header { Serializable identifier=null; public PullHeader() { ; // used by externalization } public PullHeader(Serializable identifier) { this.identifier=identifier; } public Serializable getIdentifier() { return identifier; } public int size() { if(identifier == null) return 12; else return 64; } public String toString() { return "PullHeader"; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(identifier); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { identifier=(Serializable)in.readObject(); } } /** * @return Returns the listener. */ public MessageListener getListener() { return listener; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/ReplicatedHashMap.java0000644000175000017500000010115511366547366027046 0ustar twernertwernerpackage org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.persistence.PersistenceFactory; import org.jgroups.persistence.PersistenceManager; import org.jgroups.util.Promise; import org.jgroups.util.Util; import java.io.*; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; /** * Subclass of a {@link java.util.concurrent.ConcurrentHashMap} with replication of the contents across a cluster. * Any change to the hashmap (clear(), put(), remove() etc) will transparently be * propagated to all replicas in the group. All read-only methods will always access the local replica.

* Keys and values added to the hashmap must be serializable, the reason * being that they will be sent across the network to all replicas of the group. Having said * this, it is now for example possible to add RMI remote objects to the hashtable as they * are derived from java.rmi.server.RemoteObject which in turn is serializable. * This allows to lookup shared distributed objects by their name and invoke methods on them, * regardless of one's onw location. A ReplicatedHashMap thus allows to * implement a distributed naming service in just a couple of lines.

* An instance of this class will contact an existing member of the group to fetch its * initial state.

* This class combines both {@link org.jgroups.blocks.ReplicatedHashtable} (asynchronous replication) and * {@link org.jgroups.blocks.DistributedHashtable} (synchronous replication) into one class * @author Bela Ban * @version $Id: ReplicatedHashMap.java,v 1.12.2.3 2008/05/13 12:02:23 vlada Exp $ */ public class ReplicatedHashMap extends ConcurrentHashMap implements ExtendedReceiver, ReplicatedMap { private static final long serialVersionUID=-5317720987340048547L; public interface Notification { void entrySet(K key, V value); void entryRemoved(K key); void viewChange(View view, Vector

new_mbrs, Vector
old_mbrs); void contentsSet(Map new_entries); void contentsCleared(); } private static final short PUT = 1; private static final short PUT_IF_ABSENT = 2; private static final short PUT_ALL = 3; private static final short REMOVE = 4; private static final short REMOVE_IF_EQUALS = 5; private static final short REPLACE_IF_EXISTS = 6; private static final short REPLACE_IF_EQUALS = 7; private static final short CLEAR = 8; protected static Map methods; static { try { methods=new HashMap(8); methods.put(PUT, ReplicatedHashMap.class.getMethod("_put", Serializable.class, Serializable.class)); methods.put(PUT_IF_ABSENT, ReplicatedHashMap.class.getMethod("_putIfAbsent", Serializable.class, Serializable.class)); methods.put(PUT_ALL, ReplicatedHashMap.class.getMethod("_putAll", Map.class)); methods.put(REMOVE, ReplicatedHashMap.class.getMethod("_remove", Object.class)); methods.put(REMOVE_IF_EQUALS, ReplicatedHashMap.class.getMethod("_remove", Object.class, Object.class)); methods.put(REPLACE_IF_EXISTS, ReplicatedHashMap.class.getMethod("_replace", Serializable.class, Serializable.class)); methods.put(REPLACE_IF_EQUALS, ReplicatedHashMap.class.getMethod("_replace", Serializable.class, Serializable.class, Serializable.class)); methods.put(CLEAR, ReplicatedHashMap.class.getMethod("_clear")); } catch(NoSuchMethodException e) { throw new RuntimeException(e); } } private transient Channel channel; protected transient RpcDispatcher disp=null; private String cluster_name=null; // to be notified when mbrship changes private final transient Set notifs=new CopyOnWriteArraySet(); private final Vector
members=new Vector
(); // keeps track of all DHTs private transient boolean persistent=false; // whether to use PersistenceManager to save state private transient PersistenceManager persistence_mgr=null; /** * Determines when the updates have to be sent across the network, avoids sending unnecessary * messages when there are no member in the group */ private transient boolean send_message=false; protected final transient Promise state_promise=new Promise(); /** * Whether updates across the cluster should be asynchronous (default) or synchronous) */ protected int update_mode=GroupRequest.GET_NONE; /** * For blocking updates only: the max time to wait (0 == forever) */ protected long timeout=5000; protected final Log log=LogFactory.getLog(this.getClass()); /** * Creates a ReplicatedHashMap * @param clustername The name of the group to join * @param factory The ChannelFactory which will be used to create a channel * @param properties The property string to be used to define the channel. This will override the properties of * the factory. If null, then the factory properties will be used * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. */ public ReplicatedHashMap(String clustername, ChannelFactory factory, String properties, long state_timeout) throws ChannelException { this.cluster_name=clustername; if(factory != null) { channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); } else { channel=new JChannel(properties); } disp=new RpcDispatcher(channel, this, this, this); disp.setMethodLookup(new MethodLookup() { public Method findMethod(short id) { return methods.get(id); } }); channel.connect(clustername); start(state_timeout); } /** * Creates a ReplicatedHashMap. Optionally the contents can be saved to * persistemt storage using the {@link org.jgroups.persistence.PersistenceManager}. * @param clustername Name of the group to join * @param factory Instance of a ChannelFactory to create the channel * @param properties Protocol stack properties. This will override the properties of the factory. If * null, then the factory properties will be used * @param persistent Whether the contents should be persisted * @param state_timeout Max number of milliseconds to wait until the state is retrieved */ public ReplicatedHashMap(String clustername, ChannelFactory factory, String properties, boolean persistent, long state_timeout) throws ChannelException { this.cluster_name=clustername; this.persistent=persistent; if(factory != null) { channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); } else { channel=new JChannel(properties); } disp=new RpcDispatcher(channel, this, this, this); disp.setMethodLookup(new MethodLookup() { public Method findMethod(short id) { return methods.get(id); } }); channel.connect(clustername); start(state_timeout); } public ReplicatedHashMap(Channel channel) { this(channel, false); } public ReplicatedHashMap(Channel channel, boolean persistent) { this.cluster_name=channel.getClusterName(); this.channel=channel; this.persistent=persistent; init(); } protected final void init() { disp=new RpcDispatcher(channel, this, this, this); disp.setMethodLookup(new MethodLookup() { public Method findMethod(short id) { return methods.get(id); } }); // Changed by bela (jan 20 2003): start() has to be called by user (only when providing // own channel). First, Channel.connect() has to be called, then start(). // start(state_timeout); } public boolean isBlockingUpdates() { return update_mode == GroupRequest.GET_ALL; } /** * Whether updates across the cluster should be asynchronous (default) or synchronous) * @param blocking_updates */ public void setBlockingUpdates(boolean blocking_updates) { this.update_mode=blocking_updates? GroupRequest.GET_ALL : GroupRequest.GET_NONE; } /** * The timeout (in milliseconds) for blocking updates */ public long getTimeout() { return timeout; } /** * Sets the cluster call timeout (until all acks have been received) * @param timeout The timeout (in milliseconds) for blocking updates */ public void setTimeout(long timeout) { this.timeout=timeout; } /** * Fetches the state * @param state_timeout * @throws org.jgroups.ChannelClosedException * @throws org.jgroups.ChannelNotConnectedException * */ public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { boolean rc; if(persistent) { if(log.isInfoEnabled()) log.info("fetching state from database"); try { persistence_mgr=PersistenceFactory.getInstance().createManager(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + "turning persistency off. Exception: " + Util.printStackTrace(ex)); persistent=false; } } state_promise.reset(); rc=channel.getState(null, state_timeout); if(rc) { if(log.isInfoEnabled()) log.info("state was retrieved successfully, waiting for setState()"); Boolean result=state_promise.getResult(state_timeout); if(result == null) { if(log.isErrorEnabled()) log.error("setState() never got called"); } else { if(log.isInfoEnabled()) log.info("setState() was called"); } } else { if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); if(persistent) { if(log.isInfoEnabled()) log.info("fetching state from database"); try { Map m=persistence_mgr.retrieveAll(); if(m != null) { K key; V val; for(Map.Entry entry: m.entrySet()) { key=entry.getKey(); val=entry.getValue(); if(log.isTraceEnabled()) log.trace("inserting " + key + " --> " + val); put(key, val); // will replicate key and value } } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + "turning persistency off. Exception: " + Util.printStackTrace(ex)); persistent=false; } } } } public Address getLocalAddress() { return channel != null? channel.getLocalAddress() : null; } public String getClusterName() { return cluster_name; } public Channel getChannel() { return channel; } public boolean getPersistent() { return persistent; } public void setPersistent(boolean p) { persistent=p; } public void setDeadlockDetection(boolean flag) { if(disp != null) disp.setDeadlockDetection(flag); } public void addNotifier(Notification n) { if(n != null) notifs.add(n); } public void removeNotifier(Notification n) { if(n != null) notifs.remove(n); } public void stop() { if(disp != null) { disp.stop(); disp=null; } if(channel != null) { channel.close(); channel=null; } } /** * Maps the specified key to the specified value in this table. * Neither the key nor the value can be null. *

*

The value can be retrieved by calling the get method * with a key that is equal to the original key. * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with key, or * null if there was no mapping for key * @throws NullPointerException if the specified key or value is null */ public V put(K key, V value) { V prev_val=get(key); if(send_message == true) { try { MethodCall call=new MethodCall(PUT, new Object[]{key, value}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("put(" + key + ", " + value + ") failed", e); } } else { return _put(key, value); } return prev_val; } /** * {@inheritDoc} * @return the previous value associated with the specified key, * or null if there was no mapping for the key * @throws NullPointerException if the specified key or value is null */ public V putIfAbsent(K key, V value) { V prev_val=get(key); if(send_message == true) { try { MethodCall call=new MethodCall(PUT_IF_ABSENT, new Object[]{key, value}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("putIfAbsent(" + key + ", " + value + ") failed", e); } } else { return _putIfAbsent(key, value); } return prev_val; } /** * Copies all of the mappings from the specified map to this one. * These mappings replace any mappings that this map had for any of the * keys currently in the specified map. * @param m mappings to be stored in this map */ public void putAll(Map m) { if(send_message == true) { try { MethodCall call=new MethodCall(PUT_ALL, new Object[]{m}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Throwable t) { throw new RuntimeException("putAll() failed", t); } } else { _putAll(m); } } /** * Removes all of the mappings from this map. */ public void clear() { //Changes done by //if true, propagate action to the group if(send_message == true) { try { MethodCall call=new MethodCall(CLEAR, null); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("clear() failed", e); } } else { _clear(); } } /** * Removes the key (and its corresponding value) from this map. * This method does nothing if the key is not in the map. * @param key the key that needs to be removed * @return the previous value associated with key, or * null if there was no mapping for key * @throws NullPointerException if the specified key is null */ public V remove(Object key) { V retval=get(key); if(send_message == true) { try { MethodCall call=new MethodCall(REMOVE, new Object[]{key}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("remove(" + key + ") failed", e); } } else { return _remove(key); } return retval; } /** * {@inheritDoc} * @throws NullPointerException if the specified key is null */ public boolean remove(Object key, Object value) { Object val=get(key); boolean removed=val != null && value != null && val.equals(value); if(send_message == true) { try { MethodCall call=new MethodCall(REMOVE_IF_EQUALS, new Object[]{key, value}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("remove(" + key + ", " + value + ") failed", e); } } else { return _remove(key, value); } return removed; } /** * {@inheritDoc} * @throws NullPointerException if any of the arguments are null */ public boolean replace(K key, V oldValue, V newValue) { Object val=get(key); boolean replaced=val != null && oldValue != null && val.equals(oldValue); if(send_message == true) { try { MethodCall call=new MethodCall(REPLACE_IF_EQUALS, new Object[]{key, oldValue, newValue}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("replace(" + key + ", " + oldValue + ", " + newValue + ") failed", e); } } else { return _replace(key, oldValue,newValue); } return replaced; } /** * {@inheritDoc} * @return the previous value associated with the specified key, * or null if there was no mapping for the key * @throws NullPointerException if the specified key or value is null */ public V replace(K key, V value) { V retval=get(key); if(send_message == true) { try { MethodCall call=new MethodCall(REPLACE_IF_EXISTS, new Object[]{key, value}); disp.callRemoteMethods(null, call, update_mode, timeout); } catch(Exception e) { throw new RuntimeException("replace(" + key + ", " + value + ") failed", e); } } else { return _replace(key, value); } return retval; } /*------------------------ Callbacks -----------------------*/ public V _put(K key, V value) { V retval=super.put(key, value); if(persistent) { try { persistence_mgr.save(key, value); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed persisting " + key + " + " + value, t); } } for(Notification notif: notifs) notif.entrySet(key, value); return retval; } public V _putIfAbsent(K key, V value) { V retval=super.putIfAbsent(key, value); if(persistent) { try { persistence_mgr.save(key, value); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed persisting " + key + " + " + value, t); } } for(Notification notif: notifs) notif.entrySet(key, value); return retval; } /** * @see java.util.Map#putAll(java.util.Map) */ public void _putAll(Map map) { if(map == null) return; // Calling the method below seems okay, but would result in ... deadlock ! // The reason is that Map.putAll() calls put(), which we override, which results in // lock contention for the map. // ---> super.putAll(m); <--- CULPRIT !!!@#$%$ // That said let's do it the stupid way: for(Map.Entry entry: map.entrySet()) { super.put(entry.getKey(), entry.getValue()); } if(persistent && !map.isEmpty()) { try { persistence_mgr.saveAll(map); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed persisting contents", t); } } if(!map.isEmpty()) { for(Notification notif: notifs) notif.contentsSet(map); } } public void _clear() { super.clear(); if(persistent) { try { persistence_mgr.clear(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed clearing contents", t); } } for(Notification notif: notifs) notif.contentsCleared(); } public V _remove(Object key) { V retval=super.remove(key); if(persistent && retval != null) { try { persistence_mgr.remove((Serializable)key); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed removing " + key, t); } } if(retval != null) { for(Notification notif: notifs) notif.entryRemoved((K)key); } return retval; } public boolean _remove(Object key, Object value) { boolean removed=super.remove(key, value); if(persistent && removed) { try { persistence_mgr.remove((Serializable)key); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed removing " + key, t); } } if(removed) { for(Notification notif: notifs) notif.entryRemoved((K)key); } return removed; } public boolean _replace(K key, V oldValue, V newValue) { boolean replaced=super.replace(key, oldValue, newValue); if(persistent && replaced) { try { persistence_mgr.save(key, newValue); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed saving " + key + ", " + newValue, t); } } if(replaced) { for(Notification notif: notifs) notif.entrySet(key, newValue); } return replaced; } public V _replace(K key, V value) { V retval=super.replace(key, value); if(persistent) { try { persistence_mgr.save(key, value); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed saving " + key + ", " + value, t); } } for(Notification notif: notifs) notif.entrySet(key, value); return retval; } /*----------------------------------------------------------*/ /*-------------------- State Exchange ----------------------*/ public void receive(Message msg) { } public byte[] getState() { K key; V val; Map copy=new HashMap(); for(Map.Entry entry: entrySet()) { key=entry.getKey(); val=entry.getValue(); copy.put(key, val); } try { return Util.objectToByteBuffer(copy); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); return null; } } public void setState(byte[] new_state) { HashMap new_copy; try { new_copy=(HashMap)Util.objectFromByteBuffer(new_state); if(new_copy == null) return; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); return; } _putAll(new_copy); state_promise.setResult(Boolean.TRUE); } /*------------------- Membership Changes ----------------------*/ public void viewAccepted(View new_view) { Vector

new_mbrs=new_view.getMembers(); if(new_mbrs != null) { sendViewChangeNotifications(new_view, new_mbrs, new Vector
(members)); // notifies observers (joined, left) members.clear(); members.addAll(new_mbrs); } //if size is bigger than one, there are more peers in the group //otherwise there is only one server. send_message=members.size() > 1; } /** * Called when a member is suspected */ public void suspect(Address suspected_mbr) { ; } /** * Block sending and receiving of messages until ViewAccepted is called */ public void block() { } void sendViewChangeNotifications(View view, Vector
new_mbrs, Vector
old_mbrs) { Vector
joined, left; if((notifs.isEmpty()) || (old_mbrs == null) || (new_mbrs == null) || (old_mbrs.isEmpty()) || (new_mbrs.isEmpty())) return; // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs joined=new Vector
(); for(Address mbr: new_mbrs) { if(!old_mbrs.contains(mbr)) joined.addElement(mbr); } // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs left=new Vector
(); for(Address mbr: old_mbrs) { if(!new_mbrs.contains(mbr)) { left.addElement(mbr); } } for(Notification notif: notifs) { notif.viewChange(view, joined, left); } } public byte[] getState(String state_id) { // not implemented return null; } public void getState(OutputStream ostream) { K key; V val; HashMap copy=new HashMap(); ObjectOutputStream oos=null; for(Map.Entry entry: entrySet()) { key=entry.getKey(); val=entry.getValue(); copy.put(key, val); } try { oos=new ObjectOutputStream(ostream); oos.writeObject(copy); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); } finally { Util.close(oos); } } public void getState(String state_id, OutputStream ostream) { } public void setState(String state_id, byte[] state) { } public void setState(InputStream istream) { HashMap new_copy=null; ObjectInputStream ois=null; try { ois=new ObjectInputStream(istream); new_copy=(HashMap)ois.readObject(); ois.close(); } catch(Throwable e) { e.printStackTrace(); if(log.isErrorEnabled()) log.error("exception marshalling state: " + e); } finally { Util.close(ois); } if(new_copy != null) _putAll(new_copy); state_promise.setResult(Boolean.TRUE); } public void setState(String state_id, InputStream istream) { } public void unblock() { } /** * Creates a synchronized facade for a ReplicatedMap. All methods which change state are invoked through a monitor. * This is similar to {@Collections.synchronizedMap()}, but also includes the replication methods (starting * with an underscore). * @param map * @return */ public static ReplicatedMap synchronizedMap(ReplicatedMap map) { return new SynchronizedReplicatedMap(map); } private static class SynchronizedReplicatedMap implements ReplicatedMap { private final ReplicatedMap map; private final Object mutex; private SynchronizedReplicatedMap(ReplicatedMap map) { this.map=map; this.mutex=this; } public int size() { synchronized(mutex) { return map.size(); } } public boolean isEmpty() { synchronized(mutex) { return map.isEmpty(); } } public boolean containsKey(Object key) { synchronized(mutex) { return map.containsKey(key); } } public boolean containsValue(Object value) { synchronized(mutex) { return map.containsValue(value); } } public V get(Object key) { synchronized(mutex) { return map.get(key); } } public V put(K key, V value) { synchronized(mutex) { return map.put(key, value); } } public void putAll(Map m) { synchronized(mutex) { map.putAll(m); } } public void clear() { synchronized(mutex) { map.clear(); } } public V putIfAbsent(K key, V value) { synchronized(mutex) { return map.putIfAbsent(key, value); } } public boolean remove(Object key, Object value) { synchronized(mutex) { return map.remove(key, value); } } public boolean replace(K key, V oldValue, V newValue) { synchronized(mutex) { return map.replace(key, oldValue, newValue); } } public V replace(K key, V value) { synchronized(mutex) { return map.replace(key, value); } } private Set keySet=null; private Set> entrySet=null; private Collection values=null; public Set keySet() { synchronized(mutex) { if (keySet == null) keySet=Collections.synchronizedSet(map.keySet()); return keySet; } } public Collection values() { synchronized(mutex) { if (values == null) values=Collections.synchronizedCollection(map.values()); return values; } } public Set> entrySet() { synchronized(mutex) { if (entrySet == null) entrySet=Collections.synchronizedSet(map.entrySet()); return entrySet; } } public V remove(Object key) { synchronized(mutex) { return map.remove(key); } } public V _put(K key, V value) { synchronized(mutex) { return map._put(key, value); } } public void _putAll(Map map) { synchronized(mutex) { this.map._putAll(map); } } public void _clear() { synchronized(mutex) { map._clear(); } } public V _remove(Object key) { synchronized(mutex) { return map._remove(key); } } public V _putIfAbsent(K key, V value) { synchronized(mutex) { return map._putIfAbsent(key, value); } } public boolean _remove(Object key, Object value) { synchronized(mutex) { return map._remove(key, value); } } public boolean _replace(K key, V oldValue, V newValue) { synchronized(mutex) { return map._replace(key, oldValue, newValue); } } public V _replace(K key, V value) { synchronized(mutex) { return map._replace(key, value); } } public String toString() { synchronized(mutex) { return map.toString(); } } public int hashCode() { synchronized(mutex) { return map.hashCode(); } } public boolean equals(Object obj) { synchronized(mutex) { return map.equals(obj); } } } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/RspFilter.java0000644000175000017500000000247211366547366025444 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.Address; /** * Interface defining when a group request is done. This allows for termination of a group request based on * logic implemented by the caller. Example: caller uses mode GET_FIRST plus a RspFilter implementation. Here, the * request will not return (assuming timeout is 0) when the first response has been received, but when the filter * passed * @author Bela Ban * @version $Id: RspFilter.java,v 1.1.2.1 2008/11/25 10:06:21 belaban Exp $ */ public interface RspFilter { /** * Determines whether a response from a given sender should be added to the response list of the request * @param response The response (usually a serializable value) * @param sender The sender of response * @return True if we should add the response to the response list ({@link org.jgroups.util.RspList}) of a request, * otherwise false. In the latter case, we don't add the response to the response list. */ boolean isAcceptable(Object response, Address sender); /** * Right after calling {@link #isAcceptable(Object, org.jgroups.Address)}, this method is called to see whether * we are done with the request and can unblock the caller * @return False if the request is done, otherwise true */ boolean needMoreResponses(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/BasicConnectionTable.java0000644000175000017500000007324611366547366027552 0ustar twernertwernerpackage org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Version; import org.jgroups.stack.IpAddress; import org.jgroups.util.DefaultThreadFactory; import org.jgroups.util.PortsManager; import org.jgroups.util.ThreadFactory; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Shared class for TCP connection tables. * @author Scott Marlow * @author Bela Ban */ public abstract class BasicConnectionTable { private ThreadFactory factory; final Map conns=new HashMap(); // keys: Addresses (peer address), values: Connection Receiver receiver=null; boolean use_send_queues=false; // max number of messages in a send queue int send_queue_size=10000; InetAddress bind_addr=null; Address local_addr=null; // bind_addr + port of srv_sock int srv_port=7800; int recv_buf_size=120000; int send_buf_size=60000; final Vector conn_listeners=new Vector(); // listeners to be notified when a conn is established/torn down Reaper reaper=null; // closes conns that have been idle for more than n secs long reaper_interval=60000; // reap unused conns once a minute long conn_expire_time=300000; // connections can be idle for 5 minutes before they are reaped int sock_conn_timeout=1000; // max time in millis to wait for Socket.connect() to return int peer_addr_read_timeout=2000; // max time in milliseconds to block on reading peer address final ThreadGroup thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "ConnectionTable"); protected final Log log= LogFactory.getLog(getClass()); final byte[] cookie={'b', 'e', 'l', 'a'}; boolean use_reaper=false; // by default we don't reap idle conns static final int backlog=20; // 20 conn requests are queued by ServerSocket (addtl will be discarded) volatile ServerSocket srv_sock=null; boolean tcp_nodelay=false; int linger=-1; protected PortsManager pm=null; /** * The address which will be broadcast to the group (the externally visible address which this host should * be contacted on). If external_addr is null, it will default to the same address that the server socket is bound to. */ InetAddress external_addr=null; int max_port=0; // maximum port to bind to (if < srv_port, no limit) Thread acceptor=null; // continuously calls srv_sock.accept() boolean running=false; /** Total number of Connections created for this connection table */ static AtomicInteger conn_creations=new AtomicInteger(0); final static long MAX_JOIN_TIMEOUT=Global.THREAD_SHUTDOWN_WAIT_TIME; protected BasicConnectionTable() { factory = new DefaultThreadFactory(new ThreadGroup(Util.getGlobalThreadGroup(),"ConnectionTable"),"Connection Table", false); } public final void setReceiver(Receiver r) { receiver=r; } public void addConnectionListener(ConnectionListener l) { if(l != null && !conn_listeners.contains(l)) conn_listeners.addElement(l); } public void removeConnectionListener(ConnectionListener l) { if(l != null) conn_listeners.removeElement(l); } public Address getLocalAddress() { if(local_addr == null) local_addr=bind_addr != null ? new IpAddress(bind_addr, srv_port) : null; return local_addr; } public int getSendBufferSize() { return send_buf_size; } public void setSendBufferSize(int send_buf_size) { this.send_buf_size=send_buf_size; } public int getReceiveBufferSize() { return recv_buf_size; } public void setReceiveBufferSize(int recv_buf_size) { this.recv_buf_size=recv_buf_size; } public int getSocketConnectionTimeout() { return sock_conn_timeout; } public void setSocketConnectionTimeout(int sock_conn_timeout) { this.sock_conn_timeout=sock_conn_timeout; } public int getPeerAddressReadTimeout() { return peer_addr_read_timeout; } public void setPeerAddressReadTimeout(int peer_addr_read_timeout) { this.peer_addr_read_timeout=peer_addr_read_timeout; } public int getNumConnections() { return conns.size(); } public static int getNumberOfConnectionCreations() { return conn_creations.intValue(); } public boolean getTcpNodelay() { return tcp_nodelay; } public void setTcpNodelay(boolean tcp_nodelay) { this.tcp_nodelay=tcp_nodelay; } public int getLinger() { return linger; } public void setLinger(int linger) { this.linger=linger; } public void setThreadFactory(ThreadFactory factory){ this.factory = factory; } public ThreadFactory getThreadFactory(){ return factory; } public boolean getUseSendQueues() {return use_send_queues;} public void setUseSendQueues(boolean flag) {this.use_send_queues=flag;} public int getSendQueueSize() { return send_queue_size; } public void setSendQueueSize(int send_queue_size) { this.send_queue_size=send_queue_size; } public void start() throws Exception { running=true; } public void stop() { running=false; // 1. Stop the reaper if(reaper != null) reaper.stop(); // 2. close the server socket (this also stops the acceptor thread) if(srv_sock != null) { try { if(pm != null) { int tmp_port=srv_sock.getLocalPort(); pm.updatePort(tmp_port); } ServerSocket tmp=srv_sock; srv_sock=null; tmp.close(); if(acceptor != null) Util.interruptAndWaitToDie(acceptor); } catch(Exception e) { } } // 3. then close the connections Collection connsCopy=null; synchronized(conns) { connsCopy=new LinkedList(conns.values()); conns.clear(); } for(Connection conn:connsCopy) { conn.destroy(); } connsCopy.clear(); local_addr=null; } /** Remove addrfrom connection table. This is typically triggered when a member is suspected. */ public void removeConnection(Address addr) { Connection conn; synchronized(conns) { conn=conns.remove(addr); } if(conn != null) { try { conn.destroy(); // won't do anything if already destroyed } catch(Exception e) { } } if(log.isTraceEnabled()) log.trace("removed " + addr + ", connections are " + toString()); } /** * Calls the receiver callback. We do not serialize access to this method, and it may be called concurrently * by several Connection handler threads. Therefore the receiver needs to be reentrant. */ public void receive(Address sender, byte[] data, int offset, int length) { if(receiver != null) { receiver.receive(sender, data, offset, length); } else if(log.isErrorEnabled()) log.error("receiver is null (not set) !"); } public String toString() { StringBuilder ret=new StringBuilder(); Address key; Connection val; Entry entry; HashMap copy; synchronized(conns) { copy=new HashMap(conns); } ret.append("local_addr=" + local_addr).append("\n"); ret.append("connections (" + copy.size() + "):\n"); for(Iterator> it=copy.entrySet().iterator(); it.hasNext();) { entry=it.next(); key=entry.getKey(); val=entry.getValue(); ret.append("key: " + key + ": " + val + '\n'); } ret.append('\n'); return ret.toString(); } void notifyConnectionOpened(Address peer) { if(peer == null) return; for(int i=0; i < conn_listeners.size(); i++) conn_listeners.elementAt(i).connectionOpened(peer); } void notifyConnectionClosed(Address peer) { if(peer == null) return; for(int i=0; i < conn_listeners.size(); i++) conn_listeners.elementAt(i).connectionClosed(peer); } void addConnection(Address peer, Connection c) { synchronized (conns) { conns.put(peer, c); } if(reaper != null && !reaper.isRunning()) reaper.start(); } public void send(Address dest, byte[] data, int offset, int length) throws Exception { Connection conn; if(dest == null) { if(log.isErrorEnabled()) log.error("destination is null"); return; } if(data == null) { log.warn("data is null; discarding packet"); return; } if(!running) { if(log.isWarnEnabled()) log.warn("connection table is not running, discarding message to " + dest); return; } if(dest.equals(local_addr)) { receive(local_addr, data, offset, length); return; } // 1. Try to obtain correct Connection (or create one if not yet existent) try { conn=getConnection(dest); if(conn == null) return; } catch(Throwable ex) { throw new Exception("connection to " + dest + " could not be established", ex); } // 2. Send the message using that connection try { conn.send(data, offset, length); } catch(Throwable ex) { if(log.isTraceEnabled()) log.trace("sending msg to " + dest + " failed (" + ex.getClass().getName() + "); removing from connection table", ex); removeConnection(dest); } } abstract Connection getConnection(Address dest) throws Exception; /** * Removes all connections from ConnectionTable which are not in current_mbrs * @param current_mbrs */ public void retainAll(Collection
current_mbrs) { if(current_mbrs == null) return; HashMap copy; synchronized(conns) { copy=new HashMap(conns); conns.keySet().retainAll(current_mbrs); } copy.keySet().removeAll(current_mbrs); //destroy orphaned connection i.e. connections //to members that are not in current view for(Connection orphanConnection:copy.values()){ if (log.isTraceEnabled()) log.trace("At " + local_addr + " destroying orphan to " + orphanConnection.getPeerAddress()); orphanConnection.destroy(); } copy.clear(); } /** Used for message reception. */ public interface Receiver { void receive(Address sender, byte[] data, int offset, int length); } /** Used to be notified about connection establishment and teardown. */ public interface ConnectionListener { void connectionOpened(Address peer_addr); void connectionClosed(Address peer_addr); } class Connection implements Runnable { Socket sock=null; // socket to/from peer (result of srv_sock.accept() or new Socket()) String sock_addr=null; // used for Thread.getName() DataOutputStream out=null; // for sending messages DataInputStream in=null; // for receiving messages Thread receiverThread=null; // thread for receiving messages Address peer_addr=null; // address of the 'other end' of the connection final Lock send_lock=new ReentrantLock(); // serialize send() long last_access=System.currentTimeMillis(); // last time a message was sent or received /** Bounded queue of data to be sent to the peer of this connection */ BlockingQueue send_queue=null; Sender sender=null; boolean is_running=false; private String getSockAddress() { if(sock_addr != null) return sock_addr; if(sock != null) { StringBuilder sb; sb=new StringBuilder(); sb.append(sock.getLocalAddress().getHostAddress()).append(':').append(sock.getLocalPort()); sb.append(" - ").append(sock.getInetAddress().getHostAddress()).append(':').append(sock.getPort()); sock_addr=sb.toString(); } return sock_addr; } Connection(Socket s, Address peer_addr) { sock=s; this.peer_addr=peer_addr; if(use_send_queues) { send_queue=new LinkedBlockingQueue(send_queue_size); sender=new Sender(); } try { // out=new DataOutputStream(sock.getOutputStream()); // in=new DataInputStream(sock.getInputStream()); // The change to buffered input and output stream yielded a 400% performance gain ! // bela Sept 7 2006 out=new DataOutputStream(new BufferedOutputStream(sock.getOutputStream())); in=new DataInputStream(new BufferedInputStream(sock.getInputStream())); if(sender != null) sender.start(); conn_creations.incrementAndGet(); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception is " + ex); } } boolean established() { return receiverThread != null; } void setPeerAddress(Address peer_addr) { this.peer_addr=peer_addr; } Address getPeerAddress() {return peer_addr;} void updateLastAccessed() { last_access=System.currentTimeMillis(); } void init() { is_running=true; if(receiverThread == null || !receiverThread.isAlive()) { // Roland Kurmann 4/7/2003, put in thread_group receiverThread=getThreadFactory().newThread(thread_group,this, "ConnectionTable.Connection.Receiver [" + getSockAddress() + "]"); receiverThread.start(); if(log.isTraceEnabled()) log.trace("receiver started: " + receiverThread); } } /** * Returns true if underlying socket to peer is closed * * @return */ boolean isSocketClosed() { return !(sock != null && sock.isConnected()); } void destroy() { if(log.isTraceEnabled()) log.trace("destroyed " + this); is_running=false; closeSocket(); // should terminate handler as well if(sender != null) sender.stop(); Thread tmp=receiverThread; receiverThread=null; if(tmp != null) { Util.interruptAndWaitToDie(tmp); } conn_creations.decrementAndGet(); } /** * * @param data Guaranteed to be non null * @param offset * @param length */ void send(byte[] data, int offset, int length) { if(!is_running) { if(log.isWarnEnabled()) log.warn("Connection is not running, discarding message"); return; } if(use_send_queues) { try { // we need to copy the byte[] buffer here because the original buffer might get changed meanwhile byte[] tmp=new byte[length]; System.arraycopy(data, offset, tmp, 0, length); send_queue.put(tmp); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } else _send(data, offset, length, true); } /** * Sends data using the 'out' output stream of the socket * @param data * @param offset * @param length * @param acquire_lock */ private void _send(byte[] data, int offset, int length, boolean acquire_lock) { if(acquire_lock) send_lock.lock(); try { doSend(data, offset, length); updateLastAccessed(); } catch(InterruptedException iex) { Thread.currentThread().interrupt(); // set interrupt flag again } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed sending data to " + peer_addr + ": " + ex); } finally { if(acquire_lock) send_lock.unlock(); } } void doSend(byte[] data, int offset, int length) throws Exception { try { // we're using 'double-writes', sending the buffer to the destination in 2 pieces. this would // ensure that, if the peer closed the connection while we were idle, we would get an exception. // this won't happen if we use a single write (see Stevens, ch. 5.13). if(out != null) { out.writeInt(length); // write the length of the data buffer first Util.doubleWrite(data, offset, length, out); out.flush(); // may not be very efficient (but safe) } } catch(Exception ex) { removeConnection(peer_addr); throw ex; } } /** * Reads the peer's address. First a cookie has to be sent which has to match my own cookie, otherwise * the connection will be refused */ Address readPeerAddress(Socket client_sock) throws Exception { Address client_peer_addr=null; byte[] input_cookie=new byte[cookie.length]; int client_port=client_sock != null? client_sock.getPort() : 0; short version; InetAddress client_addr=client_sock != null? client_sock.getInetAddress() : null; int timeout=client_sock.getSoTimeout(); client_sock.setSoTimeout(peer_addr_read_timeout); try { if(in != null) { initCookie(input_cookie); // read the cookie first in.readFully(input_cookie, 0, input_cookie.length); if(!matchCookie(input_cookie)) throw new SocketException("ConnectionTable.Connection.readPeerAddress(): cookie sent by " + client_peer_addr + " does not match own cookie; terminating connection"); // then read the version version=in.readShort(); if(Version.isBinaryCompatible(version) == false) { if(log.isWarnEnabled()) log.warn(new StringBuilder("packet from ").append(client_addr).append(':').append(client_port). append(" has different version (").append(Version.print(version)).append(") from ours ("). append(Version.printVersion()).append("). This may cause problems")); } client_peer_addr=new IpAddress(); client_peer_addr.readFrom(in); updateLastAccessed(); } return client_peer_addr; } finally { client_sock.setSoTimeout(timeout); } } /** * Send the cookie first, then the our port number. If the cookie doesn't match the receiver's cookie, * the receiver will reject the connection and close it. */ void sendLocalAddress(Address local_addr) { if(local_addr == null) { if(log.isWarnEnabled()) log.warn("local_addr is null"); return; } if(out != null) { try { // write the cookie out.write(cookie, 0, cookie.length); // write the version out.writeShort(Version.version); local_addr.writeTo(out); out.flush(); // needed ? updateLastAccessed(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("exception is " + t); } } } void initCookie(byte[] c) { if(c != null) for(int i=0; i < c.length; i++) c[i]=0; } boolean matchCookie(byte[] input) { if(input == null || input.length < cookie.length) return false; for(int i=0; i < cookie.length; i++) if(cookie[i] != input[i]) return false; return true; } String printCookie(byte[] c) { if(c == null) return ""; return new String(c); } public void run() { while(receiverThread != null && receiverThread.equals(Thread.currentThread()) && is_running) { try { if(in == null) { if(log.isErrorEnabled()) log.error("input stream is null !"); break; } int len=in.readInt(); final byte[] buf=new byte[len]; in.readFully(buf, 0, len); updateLastAccessed(); receive(peer_addr, buf, 0, len); // calls receiver.receive(msg) } catch(OutOfMemoryError mem_ex) { if(log.isWarnEnabled()) log.warn("dropped invalid message, closing connection"); break; // continue; } catch(IOException io_ex) { //this is very common occurrence, hence log under trace level if(log.isTraceEnabled()) log.trace("Exception while read blocked for data from peer ", io_ex); notifyConnectionClosed(peer_addr); break; } catch(Throwable e) { if(log.isWarnEnabled()) log.warn("Problem encountered while receiving message from peer " + peer_addr, e); } } if(log.isTraceEnabled()) log.trace("ConnectionTable.Connection.Receiver terminated"); receiverThread=null; closeSocket(); // remove(peer_addr); } public String toString() { StringBuilder ret=new StringBuilder(); InetAddress local=null, remote=null; String local_str, remote_str; Socket tmp_sock=sock; if(tmp_sock == null) ret.append(""); else { //since the sock variable gets set to null we want to make //make sure we make it through here without a nullpointer exception local=tmp_sock.getLocalAddress(); remote=tmp_sock.getInetAddress(); local_str=local != null ? Util.shortName(local) : ""; remote_str=remote != null ? Util.shortName(remote) : ""; ret.append('<' + local_str + ':' + tmp_sock.getLocalPort() + " --> " + remote_str + ':' + tmp_sock.getPort() + "> (" + ((System.currentTimeMillis() - last_access) / 1000) + " secs old)"); } tmp_sock=null; return ret.toString(); } void closeSocket() { Util.close(sock); // should actually close in/out (so we don't need to close them explicitly) sock=null; Util.close(out); // flushes data // removed 4/22/2003 (request by Roland Kurmann) // out=null; Util.close(in); } class Sender implements Runnable { Thread senderThread; private boolean is_it_running=false; void start() { if(senderThread == null || !senderThread.isAlive()) { senderThread=getThreadFactory().newThread(thread_group,this, "ConnectionTable.Connection.Sender local_addr=" + local_addr + " [" + getSockAddress() + "]"); senderThread.setDaemon(true); is_it_running=true; senderThread.start(); if(log.isTraceEnabled()) log.trace("sender thread started: " + senderThread); } } void stop() { is_it_running=false; if(send_queue != null) send_queue.clear(); if(senderThread != null) { Thread tmp=senderThread; senderThread=null; Util.interruptAndWaitToDie(tmp); } } boolean isRunning() { return is_it_running && senderThread != null; } public void run() { byte[] data; while(senderThread != null && senderThread.equals(Thread.currentThread()) && is_it_running) { try { data=send_queue.take(); if(data == null) continue; // we don't need to serialize access to 'out' as we're the only thread sending messages _send(data, 0, data.length, false); } catch(InterruptedException e) { ; } } is_it_running=false; if(log.isTraceEnabled()) log.trace("ConnectionTable.Connection.Sender thread terminated"); } } } class Reaper implements Runnable { Thread t=null; Reaper() { ; } // return true if we have zero connections private boolean haveZeroConnections() { synchronized(conns) { return conns.isEmpty(); } } public void start() { if(haveZeroConnections()) return; if(t != null && !t.isAlive()) t=null; if(t == null) { //RKU 7.4.2003, put in threadgroup t=getThreadFactory().newThread(thread_group, this, "ConnectionTable.ReaperThread"); t.setDaemon(true); // will allow us to terminate if all remaining threads are daemons t.start(); } } public void stop() { Thread tmp=t; if(t != null) t=null; if(tmp != null) { Util.interruptAndWaitToDie(tmp); } } public boolean isRunning() { return t != null; } public void run() { Connection connection; Entry entry; long curr_time; if(log.isDebugEnabled()) log.debug("connection reaper thread was started. Number of connections=" + conns.size() + ", reaper_interval=" + reaper_interval + ", conn_expire_time=" + conn_expire_time); while(!haveZeroConnections() && t != null && t.equals(Thread.currentThread())) { Util.sleep(reaper_interval); if(t == null || !Thread.currentThread().equals(t)) break; synchronized(conns) { curr_time=System.currentTimeMillis(); for(Iterator> it=conns.entrySet().iterator(); it.hasNext();) { entry=it.next(); connection=entry.getValue(); if(log.isTraceEnabled()) log.trace("connection is " + ((curr_time - connection.last_access) / 1000) + " seconds old (curr-time=" + curr_time + ", last_access=" + connection.last_access + ')'); if(connection.last_access + conn_expire_time < curr_time) { if(log.isTraceEnabled()) log.trace("connection " + connection + " has been idle for too long (conn_expire_time=" + conn_expire_time + "), will be removed"); connection.destroy(); it.remove(); } } } } if(log.isDebugEnabled()) log.debug("reaper terminated"); t=null; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/LockMultiLockedException.java0000644000175000017500000000127311366547366030434 0ustar twernertwernerpackage org.jgroups.blocks; /** * Thrown by the {@link org.jgroups.blocks.DistributedLockManager#unlock(Object, Object, boolean)} method if a lock is only locally released, because it is locked * by multiple DistributedLockManagers. This can happen after a merge for example. * * @author Robert Schaffar-Taurok (robert@fusion.at) * @version $Id: LockMultiLockedException.java,v 1.3 2006/11/13 17:42:10 bstansberry Exp $ */ public class LockMultiLockedException extends Exception { private static final long serialVersionUID = 3719208228960070835L; public LockMultiLockedException() { super(); } public LockMultiLockedException(String s) { super(s); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/DistributedHashtable.java0000644000175000017500000006001211366547366027622 0ustar twernertwerner// $Id: DistributedHashtable.java,v 1.33.2.1 2008/02/28 07:31:56 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.persistence.CannotPersistException; import org.jgroups.persistence.CannotRemoveException; import org.jgroups.persistence.PersistenceFactory; import org.jgroups.persistence.PersistenceManager; import org.jgroups.util.Promise; import org.jgroups.util.Util; import java.io.*; import java.util.*; /** * Provides the abstraction of a java.util.Hashtable that is replicated at several * locations. Any change to the hashtable (clear, put, remove etc) will transparently be * propagated to all replicas in the group. All read-only methods will always access the * local replica.

* Both keys and values added to the hashtable must be serializable, the reason * being that they will be sent across the network to all replicas of the group. Having said * this, it is now for example possible to add RMI remote objects to the hashtable as they * are derived from java.rmi.server.RemoteObject which in turn is serializable. * This allows to lookup shared distributed objects by their name and invoke methods on them, * regardless of one's onw location. A DistributedHashtable thus allows to * implement a distributed naming service in just a couple of lines.

* An instance of this class will contact an existing member of the group to fetch its * initial state (using the state exchange funclet StateExchangeFunclet. * @author Bela Ban * @author Alfonso Olias-Sanz * @version $Id: DistributedHashtable.java,v 1.33.2.1 2008/02/28 07:31:56 belaban Exp $ * @deprecated Use {@link org.jgroups.blocks.ReplicatedHashMap} instead */ public class DistributedHashtable extends Hashtable implements ExtendedMessageListener, ExtendedMembershipListener { private static final long serialVersionUID=7910133360803785134L; public interface Notification { void entrySet(Object key, Object value); void entryRemoved(Object key); void viewChange(Vector new_mbrs, Vector old_mbrs); void contentsSet(Map new_entries); void contentsCleared(); } private transient Channel channel; protected transient RpcDispatcher disp=null; private String groupname=null; private final transient Vector notifs=new Vector(); // to be notified when mbrship changes private final Vector members=new Vector(); // keeps track of all DHTs private transient Class[] put_signature=null; private transient Class[] putAll_signature=null; private transient Class[] clear_signature=null; private transient Class[] remove_signature=null; private transient boolean persistent=false; // whether to use PersistenceManager to save state private transient PersistenceManager persistence_mgr=null; /** Determines when the updates have to be sent across the network, avoids sending unnecessary * messages when there are no member in the group */ private transient boolean send_message = false; protected final transient Promise state_promise=new Promise(); protected final Log log=LogFactory.getLog(this.getClass()); /** * Creates a DistributedHashtable * @param groupname The name of the group to join * @param factory The ChannelFactory which will be used to create a channel * @param properties The property string to be used to define the channel. This will override the properties of * the factory. If null, then the factory properties will be used * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. */ public DistributedHashtable(String groupname, ChannelFactory factory, String properties, long state_timeout) throws ChannelException { this.groupname=groupname; initSignatures(); if(factory != null) { channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); } else { channel=new JChannel(properties); } disp=new RpcDispatcher(channel, this, this, this); channel.connect(groupname); start(state_timeout); } /** * Creates a DisttributedHashtable. Optionally the contents can be saved to * persistemt storage using the {@link PersistenceManager}. * @param groupname Name of the group to join * @param factory Instance of a ChannelFactory to create the channel * @param properties Protocol stack properties. This will override the properties of the factory. If * null, then the factory properties will be used * @param persistent Whether the contents should be persisted * @param state_timeout Max number of milliseconds to wait until state is * retrieved */ public DistributedHashtable(String groupname, ChannelFactory factory, String properties, boolean persistent, long state_timeout) throws ChannelException { this.groupname=groupname; this.persistent=persistent; initSignatures(); if(factory != null) { channel=properties != null? factory.createChannel((Object)properties) : factory.createChannel(); } else { channel=new JChannel(properties); } disp=new RpcDispatcher(channel, this, this, this); channel.connect(groupname); start(state_timeout); } public DistributedHashtable(Channel channel, long state_timeout) { this(channel, false, state_timeout); } public DistributedHashtable(Channel channel, boolean persistent, long state_timeout) { this.groupname = channel.getClusterName(); this.channel = channel; this.persistent=persistent; init(state_timeout); } /** * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be * used to register under that id. This is typically used when another building block is already using * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the * first block created on PullPushAdapter. * @param adapter The PullPushAdapter which to use as underlying transport * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between * requests/responses for different building blocks on top of PullPushAdapter. * @param state_timeout Max number of milliseconds to wait until state is * retrieved */ public DistributedHashtable(PullPushAdapter adapter, Serializable id, long state_timeout) throws ChannelNotConnectedException, ChannelClosedException { initSignatures(); this.channel = (Channel)adapter.getTransport(); this.groupname = this.channel.getClusterName(); disp=new RpcDispatcher(adapter, id, this, this, this); start(state_timeout); } public DistributedHashtable(PullPushAdapter adapter, Serializable id) { initSignatures(); this.channel = (Channel)adapter.getTransport(); this.groupname = this.channel.getClusterName(); disp=new RpcDispatcher(adapter, id, this, this, this); } protected final void init(long state_timeout) { initSignatures(); disp = new RpcDispatcher(channel, this, this, this); // Changed by bela (jan 20 2003): start() has to be called by user (only when providing // own channel). First, Channel.connect() has to be called, then start(). // start(state_timeout); } /** * Fetches the state * @param state_timeout * @throws ChannelClosedException * @throws ChannelNotConnectedException */ public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { boolean rc; if(persistent) { if(log.isInfoEnabled()) log.info("fetching state from database"); try { persistence_mgr=PersistenceFactory.getInstance().createManager(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + "turning persistency off. Exception: " + Util.printStackTrace(ex)); persistent=false; } } state_promise.reset(); rc=channel.getState(null, state_timeout); if(rc) { if(log.isInfoEnabled()) log.info("state was retrieved successfully, waiting for setState()"); Boolean result=state_promise.getResult(state_timeout); if(result == null) { if(log.isErrorEnabled()) log.error("setState() never got called"); } else { if(log.isInfoEnabled()) log.info("setState() was called"); } } else { if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); if(persistent) { if(log.isInfoEnabled()) log.info("fetching state from database"); try { Map m=persistence_mgr.retrieveAll(); if(m != null) { Map.Entry entry; Object key, val; for(Iterator it=m.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); val=entry.getValue(); if(log.isInfoEnabled()) log.info("inserting " + key + " --> " + val); put(key, val); // will replicate key and value } } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed creating PersistenceManager, " + "turning persistency off. Exception: " + Util.printStackTrace(ex)); persistent=false; } } } } public Address getLocalAddress() {return channel != null ? channel.getLocalAddress() : null;} public String getGroupName() {return groupname;} public Channel getChannel() {return channel;} public boolean getPersistent() {return persistent;} public void setPersistent(boolean p) {persistent=p;} public void setDeadlockDetection(boolean flag) { if(disp != null) disp.setDeadlockDetection(flag); } public void addNotifier(Notification n) { if(!notifs.contains(n)) notifs.addElement(n); } public void removeNotifier(Notification n) { if(notifs.contains(n)) notifs.removeElement(n); } public void stop() { if(disp != null) { disp.stop(); disp=null; } if(channel != null) { channel.close(); channel=null; } } /** * Maps the specified key to the specified value in the hashtable. Neither of both parameters can be null * @param key - the hashtable key * @param value - the value * @return the previous value of the specified key in this hashtable, or null if it did not have one */ public Object put(Object key, Object value) { Object prev_val=get(key); //Changes done by //if true, propagate action to the group if(send_message == true) { try { disp.callRemoteMethods( null, "_put", new Object[]{key,value}, put_signature, GroupRequest.GET_ALL, 0); } catch(Exception e) { //return null; } } else { _put(key, value); //don't have to do prev_val = super.put(..) as is done at the beginning } return prev_val; } /** * Copies all of the mappings from the specified Map to this Hashtable These mappings will replace any mappings that this Hashtable had for any of the keys currently in the specified Map. * @param m - Mappings to be stored in this map */ public void putAll(Map m) { //Changes done by //if true, propagate action to the group if(send_message == true) { try { disp.callRemoteMethods( null, "_putAll", new Object[]{m}, putAll_signature, GroupRequest.GET_ALL, 0); } catch(Throwable t) { } } else { _putAll(m); } } /** * Clears this hashtable so that it contains no keys */ public void clear() { //Changes done by //if true, propagate action to the group if(send_message == true) { try { disp.callRemoteMethods( null, "_clear", null, clear_signature, GroupRequest.GET_ALL, 0); } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e); } } else { _clear(); } } /** * Removes the key (and its corresponding value) from the Hashtable. * @param key - the key to be removed. * @return the value to which the key had been mapped in this hashtable, or null if the key did not have a mapping. */ public Object remove(Object key) { Object retval = get(key); //Changes done by //if true, propagate action to the group if(send_message == true) { try { disp.callRemoteMethods( null, "_remove", new Object[]{key}, remove_signature, GroupRequest.GET_ALL, 0); //return retval; } catch(Exception e) { //return null; } } else { _remove(key); //don't have to do retval = super.remove(..) as is done at the beginning } return retval; } /*------------------------ Callbacks -----------------------*/ public Object _put(Object key, Object value) { Object retval=super.put(key, value); if(persistent) { try { persistence_mgr.save((Serializable)key, (Serializable)value); } catch(CannotPersistException cannot_persist_ex) { if(log.isErrorEnabled()) log.error("failed persisting " + key + " + " + value + ", exception=" + cannot_persist_ex); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed persisting " + key + " + " + value + ", exception=" + Util.printStackTrace(t)); } } for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entrySet(key, value); return retval; } /** * @see java.util.Map#putAll(java.util.Map) */ public void _putAll(Map m) { if (m == null) return; // Calling the method below seems okay, but would result in ... deadlock ! // The reason is that Map.putAll() calls put(), which we override, which results in // lock contention for the map. // ---> super.putAll(m); <--- CULPRIT !!!@#$%$ // That said let's do it the stupid way: Map.Entry entry; for(Iterator it=m.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); super.put(entry.getKey(), entry.getValue()); } if (persistent) { try { persistence_mgr.saveAll(m); } catch (CannotPersistException persist_ex) { if(log.isErrorEnabled()) log.error("failed persisting contents: " + persist_ex); } catch (Throwable t) { if(log.isErrorEnabled()) log.error("failed persisting contents: " + t); } } for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsSet(m); } public void _clear() { super.clear(); if(persistent) { try { persistence_mgr.clear(); } catch(CannotRemoveException cannot_remove_ex) { if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + cannot_remove_ex); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + t); } } for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsCleared(); } public Object _remove(Object key) { Object retval=super.remove(key); if(persistent) { try { persistence_mgr.remove((Serializable)key); } catch(CannotRemoveException cannot_remove_ex) { if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + cannot_remove_ex); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed clearing contents, exception=" + t); } } for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryRemoved(key); return retval; } /*----------------------------------------------------------*/ /*-------------------- State Exchange ----------------------*/ public void receive(Message msg) { } public byte[] getState() { Object key, val; Hashtable copy=new Hashtable(); for(Enumeration e=keys(); e.hasMoreElements();) { key=e.nextElement(); val=get(key); copy.put(key, val); } try { return Util.objectToByteBuffer(copy); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); return null; } } public void setState(byte[] new_state) { Hashtable new_copy; try { new_copy=(Hashtable)Util.objectFromByteBuffer(new_state); if(new_copy == null) return; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); return; } _putAll(new_copy); state_promise.setResult(Boolean.TRUE); } /*------------------- Membership Changes ----------------------*/ public void viewAccepted(View new_view) { Vector new_mbrs=new_view.getMembers(); if(new_mbrs != null) { sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) members.removeAllElements(); for(int i=0; i < new_mbrs.size(); i++) members.addElement(new_mbrs.elementAt(i)); } //if size is bigger than one, there are more peers in the group //otherwise there is only one server. send_message=members.size() > 1; } /** Called when a member is suspected */ public void suspect(Address suspected_mbr) { ; } /** Block sending and receiving of messages until ViewAccepted is called */ public void block() {} void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { Vector joined, left; Object mbr; Notification n; if(notifs.size() == 0 || old_mbrs == null || new_mbrs == null || old_mbrs.size() == 0 || new_mbrs.size() == 0) return; // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs joined=new Vector(); for(int i=0; i < new_mbrs.size(); i++) { mbr=new_mbrs.elementAt(i); if(!old_mbrs.contains(mbr)) joined.addElement(mbr); } // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs left=new Vector(); for(int i=0; i < old_mbrs.size(); i++) { mbr=old_mbrs.elementAt(i); if(!new_mbrs.contains(mbr)) { left.addElement(mbr); } } for(int i=0; i < notifs.size(); i++) { n=(Notification)notifs.elementAt(i); n.viewChange(joined, left); } } final void initSignatures() { try { if(put_signature == null) { put_signature=new Class[] {Object.class,Object.class}; } if(putAll_signature == null) { putAll_signature=new Class[] {Map.class}; } if(clear_signature == null) clear_signature=new Class[0]; if(remove_signature == null) { remove_signature=new Class[] {Object.class}; } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } public static void main(String[] args) { try { // The setup here is kind of weird: // 1. Create a channel // 2. Create a DistributedHashtable (on the channel) // 3. Connect the channel (so the HT gets a VIEW_CHANGE) // 4. Start the HT // // A simpler setup is // DistributedHashtable ht = new DistributedHashtable("demo", null, // "file://c:/JGroups-2.0/conf/state_transfer.xml", 5000); JChannel c = new JChannel("file:/c:/JGroups-2.0/conf/state_transfer.xml"); DistributedHashtable ht = new DistributedHashtable(c, false, 5000); c.connect("demo"); ht.start(5000); ht.put("name", "Michelle Ban"); Object old_key = ht.remove("name"); System.out.println("old key was " + old_key); ht.put("newkey", "newvalue"); Map m = new HashMap(); m.put("k1", "v1"); m.put("k2", "v2"); ht.putAll(m); System.out.println("hashmap is " + ht); } catch (Throwable t) { t.printStackTrace(); } } public byte[] getState(String state_id) { // not implemented return null; } public void getState(OutputStream ostream) { Object key, val; Hashtable copy=new Hashtable(); ObjectOutputStream oos = null; for(Enumeration e=keys(); e.hasMoreElements();) { key=e.nextElement(); val=get(key); copy.put(key, val); } try { oos = new ObjectOutputStream(ostream); oos.writeObject(copy); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); } finally{ Util.close(oos); } } public void getState(String state_id, OutputStream ostream) { } public void setState(String state_id, byte[] state) { } public void setState(InputStream istream) { Hashtable new_copy = null; ObjectInputStream ois = null; try{ ois = new ObjectInputStream(istream); new_copy = (Hashtable) ois.readObject(); ois.close(); }catch(Throwable e){ e.printStackTrace(); if(log.isErrorEnabled()) log.error("exception marshalling state: " + e); }finally{ Util.close(ois); } if(new_copy != null) _putAll(new_copy); state_promise.setResult(Boolean.TRUE); } public void setState(String state_id, InputStream istream) { } public void unblock() { } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/RequestHandler.java0000644000175000017500000000031111366547366026446 0ustar twernertwerner// $Id: RequestHandler.java,v 1.1.1.1 2003/09/09 01:24:08 belaban Exp $ package org.jgroups.blocks; import org.jgroups.Message; public interface RequestHandler { Object handle(Message msg); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/UpdateException.java0000644000175000017500000000044111366547366026625 0ustar twernertwerner// $Id: UpdateException.java,v 1.2 2006/11/13 17:42:10 bstansberry Exp $ package org.jgroups.blocks; public class UpdateException extends Exception { private static final long serialVersionUID = -4196360091623991749L; public UpdateException(String msg) { super(msg); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/VoteResponseProcessor.java0000644000175000017500000000174711366547366030072 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.ChannelException; import org.jgroups.util.RspList; /** * VoteResultProcessor * Applications that use the VotingAdapter and/or TwoPhaseVotingAdapter can pass an implementation of this down the vote * calls, to intercept processing of the VoteResults returned by other nodes. * See the source of {@link org.jgroups.blocks.DistributedLockManager} for an example implementation. * * @author Robert Schaffar-Taurok (robert@fusion.at) * @version $Id: VoteResponseProcessor.java,v 1.2 2005/07/17 11:36:40 chrislott Exp $ */ public interface VoteResponseProcessor { /** * Processes the responses returned by the other nodes. * @param responses The responses * @param consensusType The consensusType of the vote * @param decree The vote decree * @return boolean * @throws ChannelException */ public boolean processResponses(RspList responses, int consensusType, Object decree) throws ChannelException; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/MessageDispatcher.java0000644000175000017500000010441711366547366027127 0ustar twernertwerner package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import org.jgroups.util.Util; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.TreeSet; import java.util.Vector; /** * Provides synchronous and asynchronous message sending with request-response * correlation; i.e., matching responses with the original request. * It also offers push-style message reception (by internally using the PullPushAdapter). *

* Channels are simple patterns to asynchronously send a receive messages. * However, a significant number of communication patterns in group communication * require synchronous communication. For example, a sender would like to send a * message to the group and wait for all responses. Or another application would * like to send a message to the group and wait only until the majority of the * receivers have sent a response, or until a timeout occurred. MessageDispatcher * offers a combination of the above pattern with other patterns. *

* Used on top of channel to implement group requests. Client's handle() * method is called when request is received. Is the equivalent of RpcProtocol on * the application instead of protocol level. * * @author Bela Ban * @version $Id: MessageDispatcher.java,v 1.74.2.5 2008/12/05 14:49:54 belaban Exp $ */ public class MessageDispatcher implements RequestHandler { protected Channel channel=null; protected RequestCorrelator corr=null; protected MessageListener msg_listener=null; protected MembershipListener membership_listener=null; protected RequestHandler req_handler=null; protected ProtocolAdapter prot_adapter=null; protected TransportAdapter transport_adapter=null; protected final Collection members=new TreeSet(); protected Address local_addr=null; protected boolean deadlock_detection=false; protected PullPushAdapter adapter=null; protected PullPushHandler handler=null; protected Serializable id=null; protected final Log log=LogFactory.getLog(getClass()); /** * Process items on the queue concurrently (RequestCorrelator). The default is to wait until the processing of an * item has completed before fetching the next item from the queue. Note that setting this to true may destroy the * properties of a protocol stack, e.g total or causal order may not be guaranteed. Set this to true only if you * know what you're doing ! */ protected boolean concurrent_processing=false; public MessageDispatcher() { } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2) { this.channel=channel; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getLocalAddress(); } setMessageListener(l); setMembershipListener(l2); if(channel != null) { channel.setUpHandler(prot_adapter); } start(); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, boolean deadlock_detection) { this.channel=channel; this.deadlock_detection=deadlock_detection; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getLocalAddress(); } setMessageListener(l); setMembershipListener(l2); if(channel != null) { channel.setUpHandler(prot_adapter); } start(); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, boolean deadlock_detection, boolean concurrent_processing) { this.channel=channel; this.deadlock_detection=deadlock_detection; this.concurrent_processing=concurrent_processing; prot_adapter=new ProtocolAdapter(); if(channel != null) { local_addr=channel.getLocalAddress(); } setMessageListener(l); setMembershipListener(l2); if(channel != null) { channel.setUpHandler(prot_adapter); } start(); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler) { this(channel, l, l2); setRequestHandler(req_handler); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler, boolean deadlock_detection) { this(channel, l, l2, deadlock_detection, false); setRequestHandler(req_handler); } public MessageDispatcher(Channel channel, MessageListener l, MembershipListener l2, RequestHandler req_handler, boolean deadlock_detection, boolean concurrent_processing) { this(channel, l, l2, deadlock_detection, concurrent_processing); setRequestHandler(req_handler); } /* * Uses a user-provided PullPushAdapter rather than a Channel as transport. If id is non-null, it will be * used to register under that id. This is typically used when another building block is already using * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the * first block created on PullPushAdapter. * @param adapter The PullPushAdapter which to use as underlying transport * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between * requests/responses for different building blocks on top of PullPushAdapter. */ public MessageDispatcher(PullPushAdapter adapter, Serializable id, MessageListener l, MembershipListener l2) { this.adapter=adapter; this.id=id; setMembers(((Channel) adapter.getTransport()).getView().getMembers()); setMessageListener(l); setMembershipListener(l2); handler=new PullPushHandler(); transport_adapter=new TransportAdapter(); adapter.addMembershipListener(handler); // remove in stop() if(id == null) { // no other building block around, let's become the main consumer of this PullPushAdapter adapter.setListener(handler); } else { adapter.registerListener(id, handler); } Transport tp; if((tp=adapter.getTransport()) instanceof Channel) { local_addr=((Channel) tp).getLocalAddress(); } start(); } /* * Uses a user-provided PullPushAdapter rather than a Channel as transport. If id is non-null, it will be * used to register under that id. This is typically used when another building block is already using * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the * first block created on PullPushAdapter. * @param adapter The PullPushAdapter which to use as underlying transport * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between * requests/responses for different building blocks on top of PullPushAdapter. * @param req_handler The object implementing RequestHandler. It will be called when a request is received */ public MessageDispatcher(PullPushAdapter adapter, Serializable id, MessageListener l, MembershipListener l2, RequestHandler req_handler) { this.adapter=adapter; this.id=id; setMembers(((Channel) adapter.getTransport()).getView().getMembers()); setRequestHandler(req_handler); setMessageListener(l); setMembershipListener(l2); handler=new PullPushHandler(); transport_adapter=new TransportAdapter(); adapter.addMembershipListener(handler); if(id == null) { // no other building block around, let's become the main consumer of this PullPushAdapter adapter.setListener(handler); } else { adapter.registerListener(id, handler); } Transport tp; if((tp=adapter.getTransport()) instanceof Channel) { local_addr=((Channel) tp).getLocalAddress(); // fixed bug #800774 } start(); } public MessageDispatcher(PullPushAdapter adapter, Serializable id, MessageListener l, MembershipListener l2, RequestHandler req_handler, boolean concurrent_processing) { this.concurrent_processing=concurrent_processing; this.adapter=adapter; this.id=id; setMembers(((Channel) adapter.getTransport()).getView().getMembers()); setRequestHandler(req_handler); setMessageListener(l); setMembershipListener(l2); handler=new PullPushHandler(); transport_adapter=new TransportAdapter(); adapter.addMembershipListener(handler); if(id == null) { // no other building block around, let's become the main consumer of this PullPushAdapter adapter.setListener(handler); } else { adapter.registerListener(id, handler); } Transport tp; if((tp=adapter.getTransport()) instanceof Channel) { local_addr=((Channel) tp).getLocalAddress(); // fixed bug #800774 } start(); } /** Returns a copy of members */ protected Collection getMembers() { synchronized(members) { return new ArrayList(members); } } /** * If this dispatcher is using a user-provided PullPushAdapter, then need to set the members from the adapter * initially since viewChange has most likely already been called in PullPushAdapter. */ private void setMembers(Vector new_mbrs) { if(new_mbrs != null) { synchronized(members) { members.clear(); members.addAll(new_mbrs); } } } public boolean getDeadlockDetection() {return deadlock_detection;} public void setDeadlockDetection(boolean flag) { deadlock_detection=flag; if(corr != null) corr.setDeadlockDetection(flag); } public boolean getConcurrentProcessing() {return concurrent_processing;} public void setConcurrentProcessing(boolean flag) { this.concurrent_processing=flag; } public final void start() { if(corr == null) { if(transport_adapter != null) { corr=new RequestCorrelator("MsgDisp", transport_adapter, this, deadlock_detection, local_addr, concurrent_processing); } else { corr=new RequestCorrelator("MsgDisp", prot_adapter, this, deadlock_detection, local_addr, concurrent_processing); } } correlatorStarted(); corr.start(); if(channel != null) { Vector tmp_mbrs=channel.getView() != null ? channel.getView().getMembers() : null; setMembers(tmp_mbrs); if(channel instanceof JChannel) { TP transport=((JChannel)channel).getProtocolStack().getTransport(); corr.registerProbeHandler(transport); } } } protected void correlatorStarted() { ; } public void stop() { if(corr != null) { corr.stop(); } if(channel instanceof JChannel) { TP transport=((JChannel)channel).getProtocolStack().getTransport(); corr.unregisterProbeHandler(transport); } // fixes leaks of MembershipListeners (http://jira.jboss.com/jira/browse/JGRP-160) if(adapter != null && handler != null) { adapter.removeMembershipListener(handler); } } public final void setMessageListener(MessageListener l) { msg_listener=l; } /** * Gives access to the currently configured MessageListener. Returns null if there is no * configured MessageListener. */ public MessageListener getMessageListener() { return msg_listener; } public final void setMembershipListener(MembershipListener l) { membership_listener=l; } public final void setRequestHandler(RequestHandler rh) { req_handler=rh; } /** * Offers access to the underlying Channel. * @return a reference to the underlying Channel. */ public Channel getChannel() { return channel; } public void setChannel(Channel ch) { if(ch == null) return; this.channel=ch; local_addr=channel.getLocalAddress(); if(prot_adapter == null) prot_adapter=new ProtocolAdapter(); channel.setUpHandler(prot_adapter); } public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { if(channel != null) { channel.send(msg); } else if(adapter != null) { try { if(id != null) { adapter.send(id, msg); } else { adapter.send(msg); } } catch(Throwable ex) { if(log.isErrorEnabled()) { log.error("exception=" + Util.print(ex)); } } } else { if(log.isErrorEnabled()) { log.error("channel == null"); } } } public RspList castMessage(final Vector dests, Message msg, int mode, long timeout) { return castMessage(dests, msg, mode, timeout, false); } /** * Cast a message to all members, and wait for mode responses. The responses are returned in a response * list, where each response is associated with its sender.

Uses GroupRequest. * * @param dests The members to which the message is to be sent. If it is null, then the message is sent to all * members * @param msg The message to be sent to n members * @param mode Defined in GroupRequest. The number of responses to wait for:

  1. GET_FIRST: * return the first response received.
  2. GET_ALL: wait for all responses (minus the ones from * suspected members)
  3. GET_MAJORITY: wait for a majority of all responses (relative to the grp * size)
  4. GET_ABS_MAJORITY: wait for majority (absolute, computed once)
  5. GET_N: wait for n * responses (may block if n > group size)
  6. GET_NONE: wait for no responses, return immediately * (non-blocking)
* @param timeout If 0: wait forever. Otherwise, wait for mode responses or timeout time. * @return RspList A list of responses. Each response is an Object and associated to its sender. */ public RspList castMessage(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting) { return castMessage(dests, msg, mode, timeout, use_anycasting, null); } public RspList castMessage(final Vector dests, Message msg, int mode, long timeout, boolean use_anycasting, RspFilter filter) { GroupRequest _req=null; Vector real_dests; Channel tmp; // we need to clone because we don't want to modify the original // (we remove ourselves if LOCAL is false, see below) ! // real_dests=dests != null ? (Vector) dests.clone() : (members != null ? new Vector(members) : null); if(dests != null) { real_dests=(Vector)dests.clone(); real_dests.retainAll(this.members); } else { synchronized(members) { real_dests=new Vector(members); } } // if local delivery is off, then we should not wait for the message from the local member. // therefore remove it from the membership tmp=channel; if(tmp == null) { if(adapter != null && adapter.getTransport() instanceof Channel) { tmp=(Channel) adapter.getTransport(); } } if(tmp != null && tmp.getOpt(Channel.LOCAL).equals(Boolean.FALSE)) { if(local_addr == null) { local_addr=tmp.getLocalAddress(); } if(local_addr != null) { real_dests.removeElement(local_addr); } } // don't even send the message if the destination list is empty if(log.isTraceEnabled()) log.trace("real_dests=" + real_dests); if(real_dests.isEmpty()) { if(log.isTraceEnabled()) log.trace("destination list is empty, won't send message"); return new RspList(); // return empty response list } _req=new GroupRequest(msg, corr, real_dests, mode, timeout, 0); _req.setCaller(this.local_addr); _req.setResponseFilter(filter); try { _req.execute(use_anycasting); } catch(Exception ex) { throw new RuntimeException("failed executing request " + _req, ex); } return _req.getResults(); } /** * Multicast a message request to all members in dests and receive responses via the RspCollector * interface. When done receiving the required number of responses, the caller has to call done(req_id) on the * underlyinh RequestCorrelator, so that the resources allocated to that request can be freed. * * @param dests The list of members from which to receive responses. Null means all members * @param req_id The ID of the request. Used by the underlying RequestCorrelator to correlate responses with * requests * @param msg The request to be sent * @param coll The sender needs to provide this interface to collect responses. Call will return immediately if * this is null */ public void castMessage(final Vector dests, long req_id, Message msg, RspCollector coll) { Vector real_dests; Channel tmp; if(msg == null) { if(log.isErrorEnabled()) log.error("request is null"); return; } if(coll == null) { if(log.isErrorEnabled()) log.error("response collector is null (must be non-null)"); return; } // we need to clone because we don't want to modify the original // (we remove ourselves if LOCAL is false, see below) ! //real_dests=dests != null ? (Vector) dests.clone() : (Vector) members.clone(); if(dests != null) { real_dests=(Vector)dests.clone(); real_dests.retainAll(this.members); } else { synchronized(members) { real_dests=new Vector(members); } } // if local delivery is off, then we should not wait for the message from the local member. // therefore remove it from the membership tmp=channel; if(tmp == null) { if(adapter != null && adapter.getTransport() instanceof Channel) { tmp=(Channel) adapter.getTransport(); } } if(tmp != null && tmp.getOpt(Channel.LOCAL).equals(Boolean.FALSE)) { if(local_addr == null) { local_addr=tmp.getLocalAddress(); } if(local_addr != null) { real_dests.removeElement(local_addr); } } // don't even send the message if the destination list is empty if(real_dests.isEmpty()) { if(log.isDebugEnabled()) log.debug("destination list is empty, won't send message"); return; } try { corr.sendRequest(req_id, real_dests, msg, coll); } catch(Exception e) { throw new RuntimeException("failure sending request " + req_id + " to " + real_dests, e); } } public void done(long req_id) { corr.done(req_id); } /** * Sends a message to a single member (destination = msg.dest) and returns the response. The message's destination * must be non-zero ! */ public Object sendMessage(Message msg, int mode, long timeout) throws TimeoutException, SuspectedException { Vector mbrs=new Vector(); RspList rsp_list=null; Object dest=msg.getDest(); Rsp rsp; GroupRequest _req=null; if(dest == null) { if(log.isErrorEnabled()) log.error("the message's destination is null, cannot send message"); return null; } mbrs.addElement(dest); // dummy membership (of destination address) _req=new GroupRequest(msg, corr, mbrs, mode, timeout, 0); _req.setCaller(local_addr); try { _req.execute(); } catch(Exception t) { throw new RuntimeException("failed executing request " + _req, t); } if(mode == GroupRequest.GET_NONE) { return null; } rsp_list=_req.getResults(); if(rsp_list.isEmpty()) { if(log.isWarnEnabled()) log.warn(" response list is empty"); return null; } if(rsp_list.size() > 1) { if(log.isWarnEnabled()) log.warn("response list contains more that 1 response; returning first response !"); } rsp=(Rsp)rsp_list.elementAt(0); if(rsp.wasSuspected()) { throw new SuspectedException(dest); } if(!rsp.wasReceived()) { throw new TimeoutException("timeout sending message to " + dest); } return rsp.getValue(); } /* ------------------------ RequestHandler Interface ---------------------- */ public Object handle(Message msg) { if(req_handler != null) { return req_handler.handle(msg); } else { return null; } } /* -------------------- End of RequestHandler Interface ------------------- */ class ProtocolAdapter extends Protocol implements UpHandler { /* ------------------------- Protocol Interface --------------------------- */ public String getName() { return "MessageDispatcher"; } private Object handleUpEvent(Event evt) { switch(evt.getType()) { case Event.MSG: if(msg_listener != null) { msg_listener.receive((Message) evt.getArg()); } break; case Event.GET_APPLSTATE: // reply with GET_APPLSTATE_OK StateTransferInfo info=(StateTransferInfo)evt.getArg(); String state_id=info.state_id; byte[] tmp_state=null; if(msg_listener != null) { try { if(msg_listener instanceof ExtendedMessageListener && state_id!=null) { tmp_state=((ExtendedMessageListener)msg_listener).getState(state_id); } else { tmp_state=msg_listener.getState(); } } catch(Throwable t) { this.log.error("failed getting state from message listener (" + msg_listener + ')', t); } } return new StateTransferInfo(null, state_id, 0L, tmp_state); case Event.GET_STATE_OK: if(msg_listener != null) { try { info=(StateTransferInfo)evt.getArg(); String id=info.state_id; if(msg_listener instanceof ExtendedMessageListener && id!=null) { ((ExtendedMessageListener)msg_listener).setState(id, info.state); } else { msg_listener.setState(info.state); } } catch(ClassCastException cast_ex) { if(this.log.isErrorEnabled()) this.log.error("received SetStateEvent, but argument " + evt.getArg() + " is not serializable. Discarding message."); } } break; case Event.STATE_TRANSFER_OUTPUTSTREAM: StateTransferInfo sti=(StateTransferInfo)evt.getArg(); OutputStream os=sti.outputStream; if(msg_listener instanceof ExtendedMessageListener) { if(os != null && msg_listener instanceof ExtendedMessageListener) { if(sti.state_id == null) ((ExtendedMessageListener)msg_listener).getState(os); else ((ExtendedMessageListener)msg_listener).getState(sti.state_id, os); } return new StateTransferInfo(null, os, sti.state_id); } else if(msg_listener instanceof MessageListener){ if(log.isWarnEnabled()){ log.warn("Channel has STREAMING_STATE_TRANSFER, however," + " application does not implement ExtendedMessageListener. State is not transfered"); Util.close(os); } } break; case Event.STATE_TRANSFER_INPUTSTREAM: sti=(StateTransferInfo)evt.getArg(); InputStream is=sti.inputStream; if(msg_listener instanceof ExtendedMessageListener) { if(is!=null && msg_listener instanceof ExtendedMessageListener) { if(sti.state_id == null) ((ExtendedMessageListener)msg_listener).setState(is); else ((ExtendedMessageListener)msg_listener).setState(sti.state_id, is); } } else if(msg_listener instanceof MessageListener){ if(log.isWarnEnabled()){ log.warn("Channel has STREAMING_STATE_TRANSFER, however," + " application does not implement ExtendedMessageListener. State is not transfered"); Util.close(is); } } break; case Event.VIEW_CHANGE: View v=(View) evt.getArg(); Vector new_mbrs=v.getMembers(); setMembers(new_mbrs); if(membership_listener != null) { membership_listener.viewAccepted(v); } break; case Event.SET_LOCAL_ADDRESS: if(log.isTraceEnabled()) log.trace("setting local_addr (" + local_addr + ") to " + evt.getArg()); local_addr=(Address)evt.getArg(); break; case Event.SUSPECT: if(membership_listener != null) { membership_listener.suspect((Address) evt.getArg()); } break; case Event.BLOCK: if(membership_listener != null) { membership_listener.block(); } channel.blockOk(); break; case Event.UNBLOCK: if(membership_listener instanceof ExtendedMembershipListener) { ((ExtendedMembershipListener)membership_listener).unblock(); } break; } return null; } /** * Called by channel (we registered before) when event is received. This is the UpHandler interface. */ public Object up(Event evt) { if(corr != null) { if(!corr.receive(evt)) { return handleUpEvent(evt); } } else { if(log.isErrorEnabled()) { //Something is seriously wrong, correlator should not be null since latch is not locked! log.error("correlator is null, event will be ignored (evt=" + evt + ")"); } } return null; } public Object down(Event evt) { if(channel != null) { return channel.downcall(evt); } else if(this.log.isWarnEnabled()) { this.log.warn("channel is null, discarding event " + evt); } return null; } /* ----------------------- End of Protocol Interface ------------------------ */ } class TransportAdapter implements Transport { public void send(Message msg) throws Exception { if(channel != null) { channel.send(msg); } else if(adapter != null) { try { if(id != null) { adapter.send(id, msg); } else { adapter.send(msg); } } catch(Throwable ex) { if(log.isErrorEnabled()) { log.error("exception=" + Util.print(ex)); } } } else { if(log.isErrorEnabled()) { log.error("channel == null"); } } } public Object receive(long timeout) throws Exception { return null; // not supported and not needed } } class PullPushHandler implements ExtendedMessageListener, MembershipListener { /* ------------------------- MessageListener interface ---------------------- */ public void receive(Message msg) { boolean consumed=false; if(corr != null) { consumed=corr.receiveMessage(msg); } if(!consumed) { // pass on to MessageListener if(msg_listener != null) { msg_listener.receive(msg); } } } public byte[] getState() { return msg_listener != null ? msg_listener.getState() : null; } public byte[] getState(String state_id) { if(msg_listener == null) return null; if(msg_listener instanceof ExtendedMessageListener && state_id!=null) { return ((ExtendedMessageListener)msg_listener).getState(state_id); } else { return msg_listener.getState(); } } public void setState(byte[] state) { if(msg_listener != null) { msg_listener.setState(state); } } public void setState(String state_id, byte[] state) { if(msg_listener != null) { if(msg_listener instanceof ExtendedMessageListener && state_id!=null) { ((ExtendedMessageListener)msg_listener).setState(state_id, state); } else { msg_listener.setState(state); } } } public void getState(OutputStream ostream) { if (msg_listener instanceof ExtendedMessageListener) { ((ExtendedMessageListener) msg_listener).getState(ostream); } } public void getState(String state_id, OutputStream ostream) { if (msg_listener instanceof ExtendedMessageListener && state_id!=null) { ((ExtendedMessageListener) msg_listener).getState(state_id,ostream); } } public void setState(InputStream istream) { if (msg_listener instanceof ExtendedMessageListener) { ((ExtendedMessageListener) msg_listener).setState(istream); } } public void setState(String state_id, InputStream istream) { if (msg_listener instanceof ExtendedMessageListener && state_id != null) { ((ExtendedMessageListener) msg_listener).setState(state_id,istream); } } /* * --------------------- End of MessageListener interface * ------------------- */ /* ------------------------ MembershipListener interface -------------------- */ public void viewAccepted(View v) { if(corr != null) { corr.receiveView(v); } Vector new_mbrs=v.getMembers(); setMembers(new_mbrs); if(membership_listener != null) { membership_listener.viewAccepted(v); } } public void suspect(Address suspected_mbr) { if(corr != null) { corr.receiveSuspect(suspected_mbr); } if(membership_listener != null) { membership_listener.suspect(suspected_mbr); } } public void block() { if(membership_listener != null) { membership_listener.block(); } } /* --------------------- End of MembershipListener interface ---------------- */ // @todo: receive SET_LOCAL_ADDR event and call corr.setLocalAddress(addr) } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/MessageListenerAdapter.java0000644000175000017500000001330111366547366030116 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.Message; import org.jgroups.MessageListener; import org.jgroups.ExtendedMessageListener; import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; /** * This class provides multiplexing possibilities for {@link MessageListener} * instances. Usually, we have more than one instance willing to listen to * incoming messages, but only one that can produce state for group. * {@link PullPushAdapter} allows only one instance of {@link MessageListener} * to be registered for message notification. With help of this class you * can overcome this limitation. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public class MessageListenerAdapter implements ExtendedMessageListener { protected MessageListener stateListener; protected final HashSet messageListeners = new HashSet(); // we need this cache, because every call to messageListeners.iterator() // would generate few new objects, but iteration over the cache would not. protected MessageListener[] messageListenersCache = new MessageListener[0]; /** * Create default instance of this class. Newly created instance will have * no message or state listeners. You have to use * {@link #addMessageListener(MessageListener)} or * {@link #removeMessageListener(MessageListener)} to add or remove message * listeners, and {@link #setStateListener(MessageListener)} to set listener * that will participate in state transfer. */ public MessageListenerAdapter() { this(null); } /** * Create instance of this class. mainListener is a main * listener instance that received message notifications and can get and * set group state. * * @param mainListener instance of {@link MessageListener} that will * provide state messages. */ public MessageListenerAdapter(MessageListener mainListener) { if (mainListener != null) { stateListener = mainListener; addMessageListener(mainListener); } } /** * Get state from state listener if present. * * @return current state of the group state or null if no state * listeners were registered. */ public byte[] getState() { if (stateListener != null) return stateListener.getState(); else return null; } public byte[] getState(String state_id) { if(stateListener == null) return null; if(stateListener instanceof ExtendedMessageListener) { return ((ExtendedMessageListener)stateListener).getState(state_id); } else { return stateListener.getState(); } } public void getState(OutputStream ostream) { if (stateListener instanceof ExtendedMessageListener) { ((ExtendedMessageListener) stateListener).getState(ostream); } } public void getState(String state_id, OutputStream ostream) { if (stateListener instanceof ExtendedMessageListener && state_id!=null) { ((ExtendedMessageListener) stateListener).getState(state_id,ostream); } } /** * Receive message from group. This method will send this message to each * message listener that was registered in this adapter. * * @param msg message to distribute within message listeners. */ public void receive(Message msg) { for (int i = 0; i < messageListenersCache.length; i++) messageListenersCache[i].receive(msg); } /** * Set state of ths group. This method will delegate call to state listener * if it was previously registered. */ public void setState(byte[] state) { if (stateListener != null) stateListener.setState(state); } public void setState(String state_id, byte[] state) { if(stateListener != null) { if(stateListener instanceof ExtendedMessageListener) { ((ExtendedMessageListener)stateListener).setState(state_id, state); } else { stateListener.setState(state); } } } public void setState(InputStream istream) { if (stateListener instanceof ExtendedMessageListener) { ((ExtendedMessageListener) stateListener).setState(istream); } } public void setState(String state_id, InputStream istream) { if (stateListener instanceof ExtendedMessageListener && state_id != null) { ((ExtendedMessageListener) stateListener).setState(state_id,istream); } } /** * Add message listener to this adapter. This method registers * listener for message notification. *

* Note, state notification will not be used. */ public final synchronized void addMessageListener(MessageListener listener) { if (messageListeners.add(listener)) messageListenersCache = (MessageListener[])messageListeners.toArray( new MessageListener[messageListeners.size()]); } /** * Remove message listener from this adapter. This method deregisters * listener from message notification. */ public synchronized void removeMessageListener(MessageListener listener) { if (messageListeners.remove(listener)) messageListenersCache = (MessageListener[])messageListeners.toArray( new MessageListener[messageListeners.size()]); } /** * Register listener for state notification events. There can * be only one state listener per adapter. */ public void setStateListener(MessageListener listener) { stateListener = listener; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/LockNotReleasedException.java0000644000175000017500000000070611366547366030425 0ustar twernertwernerpackage org.jgroups.blocks; /** * This exception indicated that lock manager refused to release a lock on * some resource. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public class LockNotReleasedException extends Exception { private static final long serialVersionUID = -350403929687059570L; public LockNotReleasedException() { super(); } public LockNotReleasedException(String s) { super(s); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/package.html0000644000175000017500000000132011366547366025137 0ustar twernertwerner Provides building blocks that are layered on top of channels. Most of them do not even need a channel, all they need is a class that implements interface Transport (channels do). This enables them to work on any type of group transport that obeys this interface. Building blocks can be used instead of channels whenever a higher-level interface is required. Whereas channels are simple socket-like constructs, building blocks may offer a far more sophisticated interface. In some cases, building blocks offer access to the underlying channel, so that - if the building block at hand does not offer a certain functionality - the channel can be accessed directly. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/TwoPhaseVotingListener.java0000644000175000017500000000177211366547366030163 0ustar twernertwernerpackage org.jgroups.blocks; /** * Implementations of this interface can participate in two-phase voting process. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public interface TwoPhaseVotingListener { /** * This is voting if the decree is acceptable to the party. * @return true if the decree is acceptable. * @throws VoteException if the decree type is unknown or listener * does not want to vote on it. */ boolean prepare(Object decree) throws VoteException; /** * This is voting on the commiting the decree. * @return true is the decree is commited. * @throws VoteException if the decree type is unknown or listener * does not want to vote on it. */ boolean commit(Object decree) throws VoteException; /** * This is unconditional abort of the previous voting on the decree. * @throws VoteException if the listener ignores the abort. */ void abort(Object decree) throws VoteException; }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/TwoPhaseVotingAdapter.java0000644000175000017500000001307011366547366027750 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.ChannelException; /** * This adapter introduces simple two-phase voting on a specified decree. All * nodes in the group receive a decree in "prepare" phase where they expres * their opinion on the decree. If all nodes voted positively on decree, next * phase "commit" fixes changes that were made in "prepare" phase, otherwise * changes are canceled in "abort" phase. * * @author Roman Rokytskyy (rrokytskyy@acm.org) * @author Robert Schaffar-Taurok (robert@fusion.at) * @version $Id: TwoPhaseVotingAdapter.java,v 1.4 2005/06/08 15:56:54 publicnmi Exp $ */ public class TwoPhaseVotingAdapter { private final VotingAdapter voteChannel; /** * Creats an instance of the class. * @param voteChannel the channel that will be used for voting. */ public TwoPhaseVotingAdapter(VotingAdapter voteChannel) { this.voteChannel = voteChannel; } /** * Wraps actual listener with the VoteChannelListener and adds to the * voteChannel */ public void addListener(TwoPhaseVotingListener listener) { voteChannel.addVoteListener(new TwoPhaseVoteWrapper(listener)); } /** * Removes the listener from the voteChannel */ public void removeListener(TwoPhaseVotingListener listener) { voteChannel.removeVoteListener(new TwoPhaseVoteWrapper(listener)); } /** * Performs the two-phase voting on the decree. After the voting each * group member remains in the same state as others. */ public boolean vote(Object decree, long timeout) throws ChannelException { return vote(decree, timeout, null); } /** * Performs the two-phase voting on the decree. After the voting each * group member remains in the same state as others. */ public boolean vote(Object decree, long timeout, VoteResponseProcessor voteResponseProcessor) throws ChannelException { // wrap real decree TwoPhaseWrapper wrappedDecree = new TwoPhaseWrapper(decree); // check the decree acceptance try { if (voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor)) { wrappedDecree.commit(); // try to commit decree if (!voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor)) { // strange, should fail during prepare... abort all wrappedDecree.abort(); voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor); return false; } else return true; } else { // somebody is not accepting the decree... abort wrappedDecree.abort(); voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor); return false; } } catch(ChannelException chex) { wrappedDecree.abort(); voteChannel.vote(wrappedDecree, timeout / 3, voteResponseProcessor); throw chex; } } /** * @return Returns the voteChannel. */ public VotingAdapter getVoteChannel() { return voteChannel; } public static class TwoPhaseVoteWrapper implements VotingListener { private final TwoPhaseVotingListener listener; public TwoPhaseVoteWrapper(TwoPhaseVotingListener listener) { this.listener = listener; } public boolean vote(Object decree) throws VoteException { if (!(decree instanceof TwoPhaseWrapper)) throw new VoteException("Not my type of decree. Ignore me."); TwoPhaseWrapper wrapper = (TwoPhaseWrapper)decree; // invoke the corresponding operation if (wrapper.isPrepare()) return listener.prepare(wrapper.getDecree()); else if (wrapper.isCommit()) return listener.commit(wrapper.getDecree()); else { listener.abort(wrapper.getDecree()); return false; } } /* This wrapper is completely equal to the object it wraps. Therefore the hashCode():int and equals(Object):boolean are simply delegated to the wrapped code. */ public int hashCode() { return listener.hashCode(); } public boolean equals(Object other) { return listener.equals(other); } } /** * Wrapper of the decree to voting decree. */ public static class TwoPhaseWrapper implements java.io.Serializable { private static final int PREPARE = 0; private static final int COMMIT = 1; private static final int ABORT = 2; public TwoPhaseWrapper(Object decree) { setDecree(decree); setType(PREPARE); } private Object decree; private int type; public Object getDecree(){ return decree; } public void setDecree(Object decree){ this.decree = decree; } private int getType() { return type; } private void setType(int type) { this.type = type; } private boolean isType(int type) { return this.type == type; } public boolean isPrepare() { return isType(PREPARE); } public boolean isCommit() { return isType(COMMIT); } public boolean isAbort() { return isType(ABORT); } public void commit() { setType(COMMIT); } public void abort() { setType(ABORT); } public String toString() { return decree.toString(); } } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/LockManager.java0000644000175000017500000000707411366547366025720 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.ChannelException; /** * LockManager represents generic lock manager that allows * obtaining and releasing locks on objects. * * @author Roman Rokytskyy (rrokytskyy@acm.org) * @author Robert Schaffar-Taurok (robert@fusion.at) * @version $Id: LockManager.java,v 1.2 2005/06/08 15:56:54 publicnmi Exp $ */ public interface LockManager { /** * Obtain lock on obj for specified owner. * Implementation should try to obtain lock few times within the * specified timeout. * * @param obj obj to lock, usually not full object but object's ID. * @param owner object identifying entity that will own the lock. * @param timeout maximum time that we grant to obtain a lock. * * @throws LockNotGrantedException if lock is not granted within * specified period. * * @throws ClassCastException if obj and/or * owner is not of type that implementation expects to get * (for example, when distributed lock manager obtains non-serializable * obj or owner). * * @throws ChannelException if something bad happened to communication * channel. */ void lock(Object obj, Object owner, int timeout) throws LockNotGrantedException, ClassCastException, ChannelException; /** * Release lock on obj owned by specified owner. * * since 2.2.9 this method is only a wrapper for * unlock(Object lockId, Object owner, boolean releaseMultiLocked). * Use that with releaseMultiLocked set to true if you want to be able to * release multiple locked locks (for example after a merge) * * @param obj obj to lock, usually not full object but object's ID. * @param owner object identifying entity that will own the lock. * * @throws LockOwnerMismatchException if lock is owned by another object. * * @throws ClassCastException if obj and/or * owner is not of type that implementation expects to get * (for example, when distributed lock manager obtains non-serializable * obj or owner). * * @throws ChannelException if something bad happened to communication * channel. */ void unlock(Object obj, Object owner) throws LockNotReleasedException, ClassCastException, ChannelException; /** * Release lock on obj owned by specified owner. * * @param obj obj to lock, usually not full object but object's ID. * @param owner object identifying entity that will own the lock. * @param releaseMultiLocked force unlocking of the lock if the local * lockManager owns the lock even if another lockManager owns the same lock * * @throws LockOwnerMismatchException if lock is owned by another object. * * @throws ClassCastException if obj and/or * owner is not of type that implementation expects to get * (for example, when distributed lock manager obtains non-serializable * obj or owner). * * @throws ChannelException if something bad happened to communication * channel. * * @throws LockMultiLockedException if the lock was unlocked, but another * node already held the lock */ void unlock(Object obj, Object owner, boolean releaseMultiLocked) throws LockNotReleasedException, ClassCastException, ChannelException, LockMultiLockedException; }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/VotingListener.java0000644000175000017500000000145011366547366026501 0ustar twernertwernerpackage org.jgroups.blocks; /** * Implemetations of this interface are able to participate in voting process. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public interface VotingListener { /** * Each member is able to vote with true or false * messages. If the member does not know what to do with the * decree it should throw VoteException. Doing * this he will be excluded from voting process and will not influence * the result. * * @param decree object representing the decree of current voting. * * @throws VoteException if listener does not know the meaning of the * decree and wants to be excluded from this voting. */ boolean vote(Object decree) throws VoteException; }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/DistributedTree.java0000644000175000017500000005401111366547366026630 0ustar twernertwerner// $Id: DistributedTree.java,v 1.17.4.2 2008/09/19 08:23:30 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.util.Util; import java.io.Serializable; import java.util.StringTokenizer; import java.util.Vector; /** * A tree-like structure that is replicated across several members. Updates will be multicast to all group * members reliably and in the same order. * @author Bela Ban * @author Alfonso Olias-Sanz */ public class DistributedTree implements MessageListener, MembershipListener { private Node root=null; final Vector listeners=new Vector(); final Vector view_listeners=new Vector(); final Vector members=new Vector(); protected Channel channel=null; protected RpcDispatcher disp=null; // rc is global and protected so that extensions can detect when // state has been transferred protected boolean rc = false; String groupname="DistributedTreeGroup"; String channel_properties="udp.xml"; static final long state_timeout=5000; // wait 5 secs max to obtain state /** Determines when the updates have to be sent across the network, avoids sending unnecessary * messages when there are no member in the group */ // Make this protected so that extensions // can control whether or not to send protected boolean send_message = false; protected static final Log log=LogFactory.getLog(DistributedTree.class); public interface DistributedTreeListener { void nodeAdded(String fqn, Serializable element); void nodeRemoved(String fqn); void nodeModified(String fqn, Serializable old_element, Serializable new_element); } public interface ViewListener { void viewChange(Vector new_mbrs, Vector old_mbrs); } public DistributedTree() { } public DistributedTree(String groupname, String channel_properties) { this.groupname=groupname; if(channel_properties != null) this.channel_properties=channel_properties; } /* * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be * used to register under that id. This is typically used when another building block is already using * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the * first block created on PullPushAdapter. * @param adapter The PullPushAdapter which to use as underlying transport * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between * requests/responses for different building blocks on top of PullPushAdapter. * @param state_timeout Max number of milliseconds to wait until state is * retrieved */ public DistributedTree(PullPushAdapter adapter, Serializable id, long state_timeout) throws ChannelException { channel = (Channel)adapter.getTransport(); disp=new RpcDispatcher(adapter, id, this, this, this); boolean flag = channel.getState(null, state_timeout); if(flag) { if(log.isInfoEnabled()) log.info("state was retrieved successfully"); } else if(log.isInfoEnabled()) log.info("state could not be retrieved (must be first member in group)"); } public Object getLocalAddress() { return channel != null? channel.getLocalAddress() : null; } public void setDeadlockDetection(boolean flag) { if(disp != null) disp.setDeadlockDetection(flag); } public void start() throws Exception { start(8000); } public void start(long timeout) throws Exception { if(channel != null) // already started return; channel=new JChannel(channel_properties); disp=new RpcDispatcher(channel, this, this, this); channel.connect(groupname); rc=channel.getState(null, timeout); if(rc) { if(log.isInfoEnabled()) log.info("state was retrieved successfully"); } else if(log.isInfoEnabled()) log.info("state could not be retrieved (must be first member in group)"); } public void stop() { if(channel != null) { channel.close(); disp.stop(); } channel=null; disp=null; } public void addDistributedTreeListener(DistributedTreeListener listener) { if(!listeners.contains(listener)) listeners.addElement(listener); } public void removeDistributedTreeListener(DistributedTreeListener listener) { listeners.removeElement(listener); } public void addViewListener(ViewListener listener) { if(!view_listeners.contains(listener)) view_listeners.addElement(listener); } public void removeViewListener(ViewListener listener) { view_listeners.removeElement(listener); } public void add(String fqn) { //Changes done by //if true, propagate action to the group if(send_message == true) { try { MethodCall call = new MethodCall("_add", new Object[] {fqn}, new String[] {String.class.getName()}); disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, 0); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } else { _add(fqn); } } public void add(String fqn, Serializable element) { add(fqn, element, 0); } /** resets an existing node, useful after a merge when you want to tell other * members of your state, but do not wish to remove and then add as two separate calls */ public void reset(String fqn, Serializable element) { reset(fqn, element, 0); } public void remove(String fqn) { remove(fqn, 0); } public void add(String fqn, Serializable element, int timeout) { //Changes done by //if true, propagate action to the group if(send_message == true) { try { MethodCall call = new MethodCall("_add", new Object[] {fqn, element}, new String[] {String.class.getName(), Serializable.class.getName()}); disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } else { _add(fqn, element); } } /** resets an existing node, useful after a merge when you want to tell other * members of your state, but do not wish to remove and then add as two separate calls */ public void reset(String fqn, Serializable element, int timeout) { //Changes done by //if true, propagate action to the group if(send_message == true) { try { MethodCall call = new MethodCall("_reset", new Object[] {fqn, element}, new String[] {String.class.getName(), Serializable.class.getName()}); disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } else { _add(fqn, element); } } public void remove(String fqn, int timeout) { //Changes done by //if true, propagate action to the group if(send_message == true) { try { MethodCall call = new MethodCall("_remove", new Object[] {fqn}, new String[] {String.class.getName()}); disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } else { _remove(fqn); } } public boolean exists(String fqn) { return fqn != null && (findNode(fqn) != null); } public Serializable get(String fqn) { Node n=null; if(fqn == null) return null; n=findNode(fqn); if(n != null) { return n.element; } return null; } public void set(String fqn, Serializable element) { set(fqn, element, 0); } public void set(String fqn, Serializable element, int timeout) { //Changes done by //if true, propagate action to the group if(send_message == true) { try { MethodCall call = new MethodCall("_set", new Object[] {fqn, element}, new String[] {String.class.getName(), Serializable.class.getName()}); disp.callRemoteMethods(null, call, GroupRequest.GET_ALL, timeout); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception=" + ex); } } else { _set(fqn, element); } } /** Returns all children of a Node as strings */ public Vector getChildrenNames(String fqn) { Vector ret=new Vector(); Node n; if(fqn == null) return ret; n=findNode(fqn); if(n == null || n.children == null) return ret; for(int i=0; i < n.children.size(); i++) ret.addElement(((Node)n.children.elementAt(i)).name); return ret; } public String print() { StringBuilder sb=new StringBuilder(); int indent=0; if(root == null) return "/"; sb.append(root.print(indent)); return sb.toString(); } /** Returns all children of a Node as Nodes */ Vector getChildren(String fqn) { Node n; if(fqn == null) return null; n=findNode(fqn); if(n == null) return null; return n.children; } /** * Returns the name of the group that the DistributedTree is connected to * @return String */ public String getGroupName() {return groupname;} /** * Returns the Channel the DistributedTree is connected to * @return Channel */ public Channel getChannel() {return channel;} /** * Returns the number of current members joined to the group * @return int */ public int getGroupMembersNumber() {return members.size();} /*--------------------- Callbacks --------------------------*/ public void _add(String fqn) { _add(fqn, null); } public void _add(String fqn, Serializable element) { Node curr, n; StringTokenizer tok; String child_name; String tmp_fqn=""; if(root == null) { root=new Node("/", null); notifyNodeAdded("/", null); } if(fqn == null) return; curr=root; tok=new StringTokenizer(fqn, "/"); while(tok.hasMoreTokens()) { child_name=tok.nextToken(); tmp_fqn=tmp_fqn + '/' + child_name; n=curr.findChild(child_name); if(n == null) { n=new Node(child_name, null); curr.addChild(n); if(!tok.hasMoreTokens()) { n.element=element; notifyNodeAdded(tmp_fqn, element); return; } else notifyNodeAdded(tmp_fqn, null); } curr=n; } // If the element is not null, we install it and notify the // listener app that the node is modified. if(element != null){ curr.element=element; notifyNodeModified(fqn, null, element); } } public void _remove(String fqn) { Node curr, n; StringTokenizer tok; String child_name=null; if(fqn == null || root == null) return; curr=root; tok=new StringTokenizer(fqn, "/"); while(tok.countTokens() > 1) { child_name=tok.nextToken(); n=curr.findChild(child_name); if(n == null) // node does not exist return; curr=n; } try { child_name=tok.nextToken(); if(child_name != null) { n=curr.removeChild(child_name); if(n != null) notifyNodeRemoved(fqn); } } catch(Exception ex) { } } public void _set(String fqn, Serializable element) { Node n; Serializable old_el=null; if(fqn == null || element == null) return; n=findNode(fqn); if(n == null) { if(log.isErrorEnabled()) log.error("node " + fqn + " not found"); return; } old_el=n.element; n.element=element; notifyNodeModified(fqn, old_el, element); } /** similar to set, but does not error if node does not exist, but rather does an add instead */ public void _reset(String fqn, Serializable element) { Node n; Serializable old_el=null; if(fqn == null || element == null) return; n=findNode(fqn); if(n == null) { _add(fqn, element); } else { old_el=n.element; n.element=element; } notifyNodeModified(fqn, old_el, element); } /*----------------- End of Callbacks ----------------------*/ /*-------------------- State Exchange ----------------------*/ public void receive(Message msg) { } /** Return a copy of the tree */ public byte[] getState() { Object copy=root != null? root.copy() : null; try { return Util.objectToByteBuffer(copy); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); return null; } } public void setState(byte[] data) { Object new_state; try { new_state=Util.objectFromByteBuffer(data); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); return; } if(new_state == null) return; if(!(new_state instanceof Node)) { if(log.isErrorEnabled()) log.error("object is not of type 'Node'"); return; } root=((Node)new_state).copy(); // State transfer needs to notify listeners in the new // cluster member about everything that exists. This // is working ok now. this.notifyAllNodesCreated(root, ""); } /*------------------- Membership Changes ----------------------*/ public void viewAccepted(View new_view) { Vector new_mbrs=new_view.getMembers(); if(new_mbrs != null) { sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) members.removeAllElements(); for(int i=0; i < new_mbrs.size(); i++) members.addElement(new_mbrs.elementAt(i)); } //if size is bigger than one, there are more peers in the group //otherwise there is only one server. send_message=true; send_message=members.size() > 1; } /** Called when a member is suspected */ public void suspect(Address suspected_mbr) { } /** Block sending and receiving of messages until ViewAccepted is called */ public void block() { } void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { Vector joined, left; Object mbr; if(view_listeners.isEmpty() || old_mbrs == null || new_mbrs == null) return; // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs joined=new Vector(); for(int i=0; i < new_mbrs.size(); i++) { mbr=new_mbrs.elementAt(i); if(!old_mbrs.contains(mbr)) joined.addElement(mbr); } // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs left=new Vector(); for(int i=0; i < old_mbrs.size(); i++) { mbr=old_mbrs.elementAt(i); if(!new_mbrs.contains(mbr)) left.addElement(mbr); } notifyViewChange(joined, left); } private Node findNode(String fqn) { Node curr=root; StringTokenizer tok; String child_name; if(fqn == null || root == null) return null; if("/".equals(fqn) || "".equals(fqn)) return root; tok=new StringTokenizer(fqn, "/"); while(tok.hasMoreTokens()) { child_name=tok.nextToken(); curr=curr.findChild(child_name); if(curr == null) return null; } return curr; } void notifyNodeAdded(String fqn, Serializable element) { for(int i=0; i < listeners.size(); i++) ((DistributedTreeListener)listeners.elementAt(i)).nodeAdded(fqn, element); } void notifyNodeRemoved(String fqn) { for(int i=0; i < listeners.size(); i++) ((DistributedTreeListener)listeners.elementAt(i)).nodeRemoved(fqn); } void notifyNodeModified(String fqn, Serializable old_element, Serializable new_element) { for(int i=0; i < listeners.size(); i++) ((DistributedTreeListener)listeners.elementAt(i)).nodeModified(fqn, old_element, new_element); } /** Generates NodeAdded notifications for all nodes of the tree. This is called whenever the tree is initially retrieved (state transfer) */ void notifyAllNodesCreated(Node curr, String tmp_fqn) { Node n; // We need a local string here to handle the empty string (root) // otherwise, we start off with two slashes in the path. String path = ""; if(curr == null) return; if(curr.name == null) { if(log.isErrorEnabled()) log.error("curr.name is null"); return; } // If we're the root node, then we supply a "starter" slash. // This lets us properly initiate the recursion with an empty // string, and then prepend a slash for each additional depth path = (curr.equals(root)) ? "/" : tmp_fqn; // Recursion must occur _before_ we look for children, or we // never notifyNodeAdded() for leaf nodes. notifyNodeAdded(path, curr.element); if(curr.children != null) { for(int i=0; i < curr.children.size(); i++) { n=(Node)curr.children.elementAt(i); notifyAllNodesCreated(n, tmp_fqn + '/' + n.name); } } } void notifyViewChange(Vector new_mbrs, Vector old_mbrs) { for(int i=0; i < view_listeners.size(); i++) ((ViewListener)view_listeners.elementAt(i)).viewChange(new_mbrs, old_mbrs); } private static class Node implements Serializable { String name=null; Vector children=null; Serializable element=null; private static final long serialVersionUID=-635336369135391033L; Node() { } Node(String name, Serializable element) { this.name=name; this.element=element; } void addChild(String relative_name, Serializable element) { if(relative_name == null) return; if(children == null) children=new Vector(); else { if(!children.contains(relative_name)) children.addElement(new Node(relative_name, element)); } } void addChild(Node n) { if(n == null) return; if(children == null) children=new Vector(); if(!children.contains(n)) children.addElement(n); } Node removeChild(String rel_name) { Node n=findChild(rel_name); if(n != null) children.removeElement(n); return n; } Node findChild(String relative_name) { Node child; if(children == null || relative_name == null) return null; for(int i=0; i < children.size(); i++) { child=(Node)children.elementAt(i); if(child.name == null) { if(log.isErrorEnabled()) log.error("child.name is null for " + relative_name); continue; } if(child.name.equals(relative_name)) return child; } return null; } public boolean equals(Object other) { return other != null && ((Node)other).name != null && name != null && name.equals(((Node)other).name); } Node copy() { Node ret=new Node(name, element); if(children != null) ret.children=(Vector)children.clone(); return ret; } String print(int indent) { StringBuilder sb=new StringBuilder(); boolean is_root=name != null && "/".equals(name); for(int i=0; i < indent; i++) sb.append(' '); if(!is_root) { if(name == null) sb.append("/"); else { sb.append('/' + name); // if(element != null) sb.append(" --> " + element); } } sb.append('\n'); if(children != null) { if(is_root) indent=0; else indent+=4; for(int i=0; i < children.size(); i++) sb.append(((Node)children.elementAt(i)).print(indent)); } return sb.toString(); } public String toString() { if(element != null) return "[name: " + name + ", element: " + element + ']'; else return "[name: " + name + ']'; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/ConnectionTableNIO.java0000644000175000017500000015125411366547366027152 0ustar twernertwerner// $Id: ConnectionTableNIO.java,v 1.37.2.2 2008/04/21 08:44:11 vlada Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.stack.IpAddress; import org.jgroups.util.PortsManager; import org.jgroups.util.ShutdownRejectedExecutionHandler; import java.io.IOException; import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.channels.spi.SelectorProvider; import java.util.*; import java.util.concurrent.*; /** * Manages incoming and outgoing TCP connections. For each outgoing message to destination P, if there * is not yet a connection for P, one will be created. Subsequent outgoing messages will use this * connection. For incoming messages, one server socket is created at startup. For each new incoming * client connecting, a new thread from a thread pool is allocated and listens for incoming messages * until the socket is closed by the peer.
Sockets/threads with no activity will be killed * after some time. *

* Incoming messages from any of the sockets can be received by setting the message listener. * * We currently require use_incoming_packet_handler=true (release 2.4 will support use_incoming_packet_handler=false * due to threadless stack support). * * @author Bela Ban, Scott Marlow, Alex Fu */ public class ConnectionTableNIO extends BasicConnectionTable implements Runnable { private ServerSocketChannel m_serverSocketChannel; private Selector m_acceptSelector; private WriteHandler[] m_writeHandlers; private int m_nextWriteHandler = 0; private final Object m_lockNextWriteHandler = new Object(); private ReadHandler[] m_readHandlers; private int m_nextReadHandler = 0; private final Object m_lockNextReadHandler = new Object(); // thread pool for processing read requests private Executor m_requestProcessors; private volatile boolean serverStopping=false; private final List m_backGroundThreads = new LinkedList(); // Collection of all created threads private int m_reader_threads = 3; private int m_writer_threads = 3; private int m_processor_threads = 5; // PooledExecutor.createThreads() private int m_processor_minThreads = 5; // PooledExecutor.setMinimumPoolSize() private int m_processor_maxThreads = 5; // PooledExecutor.setMaxThreads() private int m_processor_queueSize=100; // Number of queued requests that can be pending waiting // for a background thread to run the request. private long m_processor_keepAliveTime = Long.MAX_VALUE; // PooledExecutor.setKeepAliveTime( milliseconds); // negative value used to mean to wait forever, instead set to Long.MAX_VALUE to wait forever /** * @param srv_port * @throws Exception */ public ConnectionTableNIO(int srv_port) throws Exception { this.srv_port=srv_port; start(); } /** * @param srv_port * @param reaper_interval * @param conn_expire_time * @throws Exception */ public ConnectionTableNIO(int srv_port, long reaper_interval, long conn_expire_time) throws Exception { this.srv_port=srv_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; start(); } /** * @param r * @param bind_addr * @param external_addr * @param srv_port * @param max_port * @throws Exception */ public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port) throws Exception { setReceiver(r); this.external_addr=external_addr; this.bind_addr=bind_addr; this.srv_port=srv_port; this.max_port=max_port; use_reaper=true; start(); } public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, boolean doStart) throws Exception { setReceiver(r); this.external_addr=external_addr; this.bind_addr=bind_addr; this.srv_port=srv_port; this.max_port=max_port; use_reaper=true; if(doStart) start(); } public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, PortsManager pm, boolean doStart) throws Exception { setReceiver(r); this.external_addr=external_addr; this.bind_addr=bind_addr; this.srv_port=srv_port; this.max_port=max_port; this.pm=pm; use_reaper=true; if(doStart) start(); } /** * @param r * @param bind_addr * @param external_addr * @param srv_port * @param max_port * @param reaper_interval * @param conn_expire_time * @throws Exception */ public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time ) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; start(); } public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time, boolean doStart ) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; if(doStart) start(); } public ConnectionTableNIO(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, PortsManager pm, long reaper_interval, long conn_expire_time, boolean doStart ) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.pm=pm; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; if(doStart) start(); } public int getReaderThreads() { return m_reader_threads; } public void setReaderThreads(int m_reader_threads) { this.m_reader_threads=m_reader_threads; } public int getWriterThreads() { return m_writer_threads; } public void setWriterThreads(int m_writer_threads) { this.m_writer_threads=m_writer_threads; } public int getProcessorThreads() { return m_processor_threads; } public void setProcessorThreads(int m_processor_threads) { this.m_processor_threads=m_processor_threads; } public int getProcessorMinThreads() { return m_processor_minThreads;} public void setProcessorMinThreads(int m_processor_minThreads) { this.m_processor_minThreads=m_processor_minThreads; } public int getProcessorMaxThreads() { return m_processor_maxThreads;} public void setProcessorMaxThreads(int m_processor_maxThreads) { this.m_processor_maxThreads=m_processor_maxThreads; } public int getProcessorQueueSize() { return m_processor_queueSize; } public void setProcessorQueueSize(int m_processor_queueSize) { this.m_processor_queueSize=m_processor_queueSize; } public long getProcessorKeepAliveTime() { return m_processor_keepAliveTime; } public void setProcessorKeepAliveTime(long m_processor_keepAliveTime) { this.m_processor_keepAliveTime=m_processor_keepAliveTime; } /** * Try to obtain correct Connection (or create one if not yet existent) */ ConnectionTable.Connection getConnection(Address dest) throws Exception { Connection conn; SocketChannel sock_ch; synchronized (conns) { conn = (Connection) conns.get(dest); if (conn == null) { InetSocketAddress destAddress = new InetSocketAddress(((IpAddress) dest).getIpAddress(), ((IpAddress) dest).getPort()); sock_ch = SocketChannel.open(destAddress); sock_ch.socket().setTcpNoDelay(tcp_nodelay); conn = new Connection(sock_ch, dest); conn.sendLocalAddress(local_addr); // This outbound connection is ready sock_ch.configureBlocking(false); try { if (log.isTraceEnabled()) log.trace("About to change new connection send buff size from " + sock_ch.socket().getSendBufferSize() + " bytes"); sock_ch.socket().setSendBufferSize(send_buf_size); if (log.isTraceEnabled()) log.trace("Changed new connection send buff size to " + sock_ch.socket().getSendBufferSize() + " bytes"); } catch (IllegalArgumentException ex) { if (log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: " + ex); } try { if (log.isTraceEnabled()) log.trace("About to change new connection receive buff size from " + sock_ch.socket().getReceiveBufferSize() + " bytes"); sock_ch.socket().setReceiveBufferSize(recv_buf_size); if (log.isTraceEnabled()) log.trace("Changed new connection receive buff size to " + sock_ch.socket().getReceiveBufferSize() + " bytes"); } catch (IllegalArgumentException ex) { if (log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes: " + ex); } int idx; synchronized (m_lockNextWriteHandler) { idx = m_nextWriteHandler = (m_nextWriteHandler + 1) % m_writeHandlers.length; } conn.setupWriteHandler(m_writeHandlers[idx]); // Put the new connection to the queue try { synchronized (m_lockNextReadHandler) { idx = m_nextReadHandler = (m_nextReadHandler + 1) % m_readHandlers.length; } m_readHandlers[idx].add(conn); } catch (InterruptedException e) { if (log.isWarnEnabled()) log.warn("Thread (" +Thread.currentThread().getName() + ") was interrupted, closing connection", e); // What can we do? Remove it from table then. conn.destroy(); throw e; } // Add connection to table addConnection(dest, conn); notifyConnectionOpened(dest); if (log.isTraceEnabled()) log.trace("created socket to " + dest); } return conn; } } public final void start() throws Exception { super.start(); init(); srv_sock=createServerSocket(srv_port, max_port); if (external_addr!=null) local_addr=new IpAddress(external_addr, srv_sock.getLocalPort()); else if (bind_addr != null) local_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); else local_addr=new IpAddress(srv_sock.getLocalPort()); if(log.isDebugEnabled()) log.debug("server socket created on " + local_addr); //Roland Kurmann 4/7/2003, put in thread_group acceptor=getThreadFactory().newThread(thread_group, this, "ConnectionTable.AcceptorThread"); acceptor.setDaemon(true); acceptor.start(); m_backGroundThreads.add(acceptor); // start the connection reaper - will periodically remove unused connections if(use_reaper && reaper == null) { reaper=new Reaper(); reaper.start(); } } protected void init() throws Exception { // use directExector if max thread pool size is less than or equal to zero. if(getProcessorMaxThreads() <= 0) { m_requestProcessors = new Executor() { public void execute(Runnable command) { command.run(); } }; } else { // Create worker thread pool for processing incoming buffers ThreadPoolExecutor requestProcessors = new ThreadPoolExecutor(getProcessorMinThreads(), getProcessorMaxThreads(), getProcessorKeepAliveTime(), TimeUnit.MILLISECONDS, new LinkedBlockingQueue(getProcessorQueueSize())); requestProcessors.setThreadFactory(new ThreadFactory() { public Thread newThread(Runnable runnable) { Thread new_thread=new Thread(thread_group, runnable); new_thread.setDaemon(true); new_thread.setName("ConnectionTableNIO.Thread"); m_backGroundThreads.add(new_thread); return new_thread; } }); requestProcessors.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(requestProcessors.getRejectedExecutionHandler())); m_requestProcessors = requestProcessors; } m_writeHandlers = WriteHandler.create(getThreadFactory(),getWriterThreads(), thread_group, m_backGroundThreads, log); m_readHandlers = ReadHandler.create(getThreadFactory(),getReaderThreads(), this, thread_group, m_backGroundThreads, log); } /** * Closes all open sockets, the server socket and all threads waiting for incoming messages */ public void stop() { super.stop(); serverStopping = true; if(reaper != null) reaper.stop(); // Stop the main selector if(m_acceptSelector != null) m_acceptSelector.wakeup(); // Stop selector threads if(m_readHandlers != null) { for (int i = 0; i < m_readHandlers.length; i++) { try { m_readHandlers[i].add(new Shutdown()); } catch (InterruptedException e) { log.error("Thread ("+Thread.currentThread().getName() +") was interrupted, failed to shutdown selector", e); } } } if(m_writeHandlers != null) { for (int i = 0; i < m_writeHandlers.length; i++) { try { m_writeHandlers[i].queue.put(new Shutdown()); m_writeHandlers[i].selector.wakeup(); } catch (InterruptedException e) { log.error("Thread ("+Thread.currentThread().getName() +") was interrupted, failed to shutdown selector", e); } } } // Stop the callback thread pool if(m_requestProcessors instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)m_requestProcessors).shutdownNow(); if(m_requestProcessors instanceof ThreadPoolExecutor){ try{ ((ThreadPoolExecutor) m_requestProcessors).awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); }catch(InterruptedException e){ } } // then close the connections synchronized(conns) { Iterator it=conns.values().iterator(); while(it.hasNext()) { Connection conn=(Connection)it.next(); conn.destroy(); } conns.clear(); } while(!m_backGroundThreads.isEmpty()) { Thread t =m_backGroundThreads.remove(0); try { t.join(); } catch(InterruptedException e) { log.error("Thread ("+Thread.currentThread().getName() +") was interrupted while waiting on thread " + t.getName() + " to finish."); } } m_backGroundThreads.clear(); } /** * Acceptor thread. Continuously accept new connections and assign readhandler/writehandler * to them. */ public void run() { Connection conn; while(m_serverSocketChannel.isOpen() && !serverStopping) { int num; try { num=m_acceptSelector.select(); } catch(IOException e) { if(log.isWarnEnabled()) log.warn("Select operation on listening socket failed", e); continue; // Give up this time } if(num > 0) { Set readyKeys=m_acceptSelector.selectedKeys(); for(Iterator i=readyKeys.iterator(); i.hasNext();) { SelectionKey key=i.next(); i.remove(); // We only deal with new incoming connections ServerSocketChannel readyChannel=(ServerSocketChannel)key.channel(); SocketChannel client_sock_ch; try { client_sock_ch=readyChannel.accept(); } catch(IOException e) { if(log.isWarnEnabled()) log.warn("Attempt to accept new connection from listening socket failed", e); // Give up this connection continue; } if(log.isTraceEnabled()) log.trace("accepted connection, client_sock=" + client_sock_ch.socket()); try { client_sock_ch.socket().setSendBufferSize(send_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: ", ex); } catch(SocketException e) { if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes: ", e); } try { client_sock_ch.socket().setReceiveBufferSize(recv_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes: ", ex); } catch(SocketException e) { if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + recv_buf_size + " bytes: ", e); } conn=new Connection(client_sock_ch, null); try { Address peer_addr=conn.readPeerAddress(client_sock_ch.socket()); conn.peer_addr=peer_addr; synchronized(conns) { Connection tmp=(Connection)conns.get(peer_addr); if(tmp != null) { if(peer_addr.compareTo(local_addr) > 0) { if(log.isTraceEnabled()) log.trace("peer's address (" + peer_addr + ") is greater than our local address (" + local_addr + "), replacing our existing connection"); // peer's address is greater, add peer's connection to ConnectionTable, destroy existing connection addConnection(peer_addr, conn); tmp.destroy(); notifyConnectionOpened(peer_addr); } else { if(log.isTraceEnabled()) log.trace("peer's address (" + peer_addr + ") is smaller than our local address (" + local_addr + "), rejecting peer connection request"); conn.destroy(); continue; } } else { addConnection(peer_addr, conn); } } notifyConnectionOpened(peer_addr); client_sock_ch.configureBlocking(false); } catch(IOException e) { if(log.isWarnEnabled()) log.warn("Attempt to configure non-blocking mode failed", e); conn.destroy(); continue; } catch(Exception e) { if(log.isWarnEnabled()) log.warn("Attempt to handshake with other peer failed", e); conn.destroy(); continue; } int idx; synchronized(m_lockNextWriteHandler) { idx=m_nextWriteHandler=(m_nextWriteHandler + 1) % m_writeHandlers.length; } conn.setupWriteHandler(m_writeHandlers[idx]); try { synchronized(m_lockNextReadHandler) { idx=m_nextReadHandler=(m_nextReadHandler + 1) % m_readHandlers.length; } m_readHandlers[idx].add(conn); } catch(InterruptedException e) { if(log.isWarnEnabled()) log.warn("Attempt to configure read handler for accepted connection failed", e); // close connection conn.destroy(); } } // end of iteration } // end of selected key > 0 } // end of thread if(m_serverSocketChannel.isOpen()) { try { m_serverSocketChannel.close(); } catch(Exception e) { log.error("exception closing server listening socket", e); } } if(log.isTraceEnabled()) log.trace("acceptor thread terminated"); } /** * Finds first available port starting at start_port and returns server socket. Sets srv_port */ protected ServerSocket createServerSocket(int start_port, int end_port) throws Exception { this.m_acceptSelector = Selector.open(); m_serverSocketChannel = ServerSocketChannel.open(); m_serverSocketChannel.configureBlocking(false); if(start_port > 0 && pm != null) start_port=pm.getNextAvailablePort(start_port); while (true) { try { SocketAddress sockAddr; if (bind_addr == null) { sockAddr=new InetSocketAddress(start_port); m_serverSocketChannel.socket().bind(sockAddr); } else { sockAddr=new InetSocketAddress(bind_addr, start_port); m_serverSocketChannel.socket().bind(sockAddr, backlog); } } catch (BindException bind_ex) { if (start_port == end_port) throw (BindException) ((new BindException("No available port to bind to (start_port=" + start_port + ")")).initCause(bind_ex)); start_port++; continue; } catch (SocketException bind_ex) { if (start_port == end_port) throw (BindException) ((new BindException("No available port to bind to (start_port=" + start_port + ")")).initCause(bind_ex)); start_port++; continue; } catch (IOException io_ex) { if (log.isErrorEnabled()) log.error("Attempt to bind serversocket failed, port="+start_port+", bind addr=" + bind_addr ,io_ex); throw io_ex; } srv_port = start_port; break; } m_serverSocketChannel.register(this.m_acceptSelector, SelectionKey.OP_ACCEPT); return m_serverSocketChannel.socket(); } protected void runRequest(Address addr, ByteBuffer buf) throws InterruptedException { m_requestProcessors.execute(new ExecuteTask(addr, buf)); } // Represents shutdown private static class Shutdown { } // ReadHandler has selector to deal with read, it runs in seperated thread private static class ReadHandler implements Runnable { private final Selector selector= initHandler(); private final LinkedBlockingQueue queue= new LinkedBlockingQueue(); private final ConnectionTableNIO connectTable; private final Log log; ReadHandler(ConnectionTableNIO ct, Log log) { connectTable= ct; this.log=log; } public Selector initHandler() { // Open the selector try { return Selector.open(); } catch (IOException e) { if (log.isErrorEnabled()) log.error(e); throw new IllegalStateException(e.getMessage()); } } /** * create instances of ReadHandler threads for receiving data. * * @param workerThreads is the number of threads to create. */ private static ReadHandler[] create(org.jgroups.util.ThreadFactory f,int workerThreads, ConnectionTableNIO ct, ThreadGroup tg, List backGroundThreads, Log log) { ReadHandler[] handlers = new ReadHandler[workerThreads]; for (int looper = 0; looper < workerThreads; looper++) { handlers[looper] = new ReadHandler(ct, log); Thread thread = f.newThread(tg, handlers[looper], "nioReadHandlerThread"); thread.setDaemon(true); thread.start(); backGroundThreads.add(thread); } return handlers; } private void add(Object conn) throws InterruptedException { queue.put(conn); wakeup(); } private void wakeup() { selector.wakeup(); } public void run() { while (true) { // m_s can be closed by the management thread int events; try { events = selector.select(); } catch (IOException e) { if (log.isWarnEnabled()) log.warn("Select operation on socket failed", e); continue; // Give up this time } catch (ClosedSelectorException e) { if (log.isWarnEnabled()) log.warn("Select operation on socket failed" , e); return; // Selector gets closed, thread stops } if (events > 0) { // there are read-ready channels Set readyKeys = selector.selectedKeys(); try { for (Iterator i = readyKeys.iterator(); i.hasNext();) { SelectionKey key = (SelectionKey) i.next(); i.remove(); // Do partial read and handle call back Connection conn = (Connection) key.attachment(); if(conn != null && conn.getSocketChannel() != null) { try { if (conn.getSocketChannel().isOpen()) readOnce(conn); else { // socket connection is already closed, clean up connection state conn.closed(); } } catch (IOException e) { if (log.isTraceEnabled()) log.trace("Read operation on socket failed" , e); // The connection must be bad, cancel the key, close socket, then // remove it from table! key.cancel(); conn.destroy(); conn.closed(); } } } } catch(ConcurrentModificationException e) { if (log.isTraceEnabled()) log.trace("Selection set changed", e); // valid events should still be in the selection set the next time } } // Now we look at the connection queue to get any new connections added Object o; try { o = queue.poll(0L, TimeUnit.MILLISECONDS); // get a connection } catch (InterruptedException e) { if (log.isTraceEnabled()) log.trace("Thread ("+Thread.currentThread().getName() +") was interrupted while polling queue" ,e); // We must give up continue; } if (null == o) continue; if (o instanceof Shutdown) { // shutdown command? try { selector.close(); } catch(IOException e) { if (log.isTraceEnabled()) log.trace("Read selector close operation failed" , e); } return; // stop reading } Connection conn = (Connection) o;// must be a new connection SocketChannel sc = conn.getSocketChannel(); try { sc.register(selector, SelectionKey.OP_READ, conn); } catch (ClosedChannelException e) { if (log.isTraceEnabled()) log.trace("Socket channel was closed while we were trying to register it to selector" , e); // Channel becomes bad. The connection must be bad, // close socket, then remove it from table! conn.destroy(); conn.closed(); } } // end of the while true loop } private void readOnce(Connection conn) throws IOException { ConnectionReadState readState = conn.getReadState(); if (!readState.isHeadFinished()) { // a brand new message coming or header is not completed // Begin or continue to read header int size = readHeader(conn); if (0 == size) { // header is not completed return; } } // Begin or continue to read body if (readBody(conn) > 0) { // not finish yet return; } Address addr = conn.getPeerAddress(); ByteBuffer buf = readState.getReadBodyBuffer(); // Clear status readState.bodyFinished(); // Assign worker thread to execute call back try { connectTable.runRequest(addr, buf); } catch (InterruptedException e) { // Cannot do call back, what can we do? // Give up handling the message then log.error("Thread ("+Thread.currentThread().getName() +") was interrupted while assigning executor to process read request" , e); } } /** * Read message header from channel. It doesn't try to complete. If there is nothing in * the channel, the method returns immediately. * * @param conn The connection * @return 0 if header hasn't been read completely, otherwise the size of message body * @throws IOException */ private int readHeader(Connection conn) throws IOException { ConnectionReadState readState = conn.getReadState(); ByteBuffer headBuf = readState.getReadHeadBuffer(); SocketChannel sc = conn.getSocketChannel(); while (headBuf.remaining() > 0) { int num = sc.read(headBuf); if (-1 == num) {// EOS throw new IOException("Peer closed socket"); } if (0 == num) // no more data return 0; } // OK, now we get the whole header, change the status and return message size return readState.headFinished(); } /** * Read message body from channel. It doesn't try to complete. If there is nothing in * the channel, the method returns immediately. * * @param conn The connection * @return remaining bytes for the message * @throws IOException */ private int readBody(Connection conn) throws IOException { ByteBuffer bodyBuf = conn.getReadState().getReadBodyBuffer(); SocketChannel sc = conn.getSocketChannel(); while (bodyBuf.remaining() > 0) { int num = sc.read(bodyBuf); if (-1 == num) // EOS throw new IOException("Couldn't read from socket as peer closed the socket"); if (0 == num) // no more data return bodyBuf.remaining(); } // OK, we finished reading the whole message! Flip it (not necessary though) bodyBuf.flip(); return 0; } } private class ExecuteTask implements Runnable { Address m_addr = null; ByteBuffer m_buf = null; public ExecuteTask(Address addr, ByteBuffer buf) { m_addr = addr; m_buf = buf; } public void run() { receive(m_addr, m_buf.array(), m_buf.arrayOffset(), m_buf.limit()); } } private class ConnectionReadState { private final Connection m_conn; // Status for receiving message private boolean m_headFinished = false; private ByteBuffer m_readBodyBuf = null; private final ByteBuffer m_readHeadBuf = ByteBuffer.allocate(Connection.HEADER_SIZE); public ConnectionReadState(Connection conn) { m_conn = conn; } ByteBuffer getReadBodyBuffer() { return m_readBodyBuf; } ByteBuffer getReadHeadBuffer() { return m_readHeadBuf; } void bodyFinished() { m_headFinished = false; m_readHeadBuf.clear(); m_readBodyBuf = null; m_conn.updateLastAccessed(); } /** * Status change for finishing reading the message header (data already in buffer) * * @return message size */ int headFinished() { m_headFinished = true; m_readHeadBuf.flip(); int messageSize = m_readHeadBuf.getInt(); m_readBodyBuf = ByteBuffer.allocate(messageSize); m_conn.updateLastAccessed(); return messageSize; } boolean isHeadFinished() { return m_headFinished; } } class Connection extends ConnectionTable.Connection { private SocketChannel sock_ch = null; private WriteHandler m_writeHandler; private SelectorWriteHandler m_selectorWriteHandler; private final ConnectionReadState m_readState; private static final int HEADER_SIZE = 4; final ByteBuffer headerBuffer = ByteBuffer.allocate(HEADER_SIZE); Connection(SocketChannel s, Address peer_addr) { super(s.socket(), peer_addr); sock_ch = s; m_readState = new ConnectionReadState(this); is_running=true; } private ConnectionReadState getReadState() { return m_readState; } private void setupWriteHandler(WriteHandler hdlr) { m_writeHandler = hdlr; m_selectorWriteHandler = hdlr.add(sock_ch); } void doSend(byte[] buffie, int offset, int length) throws Exception { MyFuture result = new MyFuture(); m_writeHandler.write(sock_ch, ByteBuffer.wrap(buffie, offset, length), result, m_selectorWriteHandler); Object ex = result.get(); if (ex instanceof Exception) { if (log.isErrorEnabled()) log.error("failed sending message", (Exception)ex); if (((Exception)ex).getCause() instanceof IOException) throw (IOException) ((Exception)ex).getCause(); throw (Exception)ex; } result.get(); } SocketChannel getSocketChannel() { return sock_ch; } void closeSocket() { if (sock_ch != null) { try { if(sock_ch.isConnected() && sock_ch.isOpen()) { sock_ch.close(); } } catch (Exception e) { log.error("error closing socket connection", e); } sock_ch = null; } } void closed() { Address peerAddr = getPeerAddress(); synchronized (conns) { conns.remove(peerAddr); } notifyConnectionClosed(peerAddr); } } /** * Handle writing to non-blocking NIO connection. */ private static class WriteHandler implements Runnable { // Create a queue for write requests (unbounded) private final LinkedBlockingQueue queue= new LinkedBlockingQueue(); private final Selector selector= initSelector(); private int m_pendingChannels; // count of the number of channels that have pending writes // note that this variable is only accessed by one thread. // allocate and reuse the header for all buffer write operations private ByteBuffer m_headerBuffer = ByteBuffer.allocate(Connection.HEADER_SIZE); private final Log log; public WriteHandler(Log log) { this.log=log; } Selector initSelector() { try { return SelectorProvider.provider().openSelector(); } catch (IOException e) { if (log.isErrorEnabled()) log.error(e); throw new IllegalStateException(e.getMessage()); } } /** * create instances of WriteHandler threads for sending data. * * @param workerThreads is the number of threads to create. */ private static WriteHandler[] create(org.jgroups.util.ThreadFactory f, int workerThreads, ThreadGroup tg, List backGroundThreads, Log log) { WriteHandler[] handlers = new WriteHandler[workerThreads]; for (int looper = 0; looper < workerThreads; looper++) { handlers[looper] = new WriteHandler(log); Thread thread = f.newThread(tg, handlers[looper], "nioWriteHandlerThread"); thread.setDaemon(true); thread.start(); backGroundThreads.add(thread); } return handlers; } /** * Add a new channel to be handled. * * @param channel */ private SelectorWriteHandler add(SocketChannel channel) { return new SelectorWriteHandler(channel, selector, m_headerBuffer); } /** * Writes buffer to the specified socket connection. This is always performed asynchronously. If you want * to perform a synchrounous write, call notification.`get() which will block until the write operation is complete. * Best practice is to call notification.getException() which may return any exceptions that occured during the write * operation. * * @param channel is where the buffer is written to. * @param buffer is what we write. * @param notification may be specified if you want to know how many bytes were written and know if an exception * occurred. */ private void write(SocketChannel channel, ByteBuffer buffer, MyFuture notification, SelectorWriteHandler hdlr) throws InterruptedException { queue.put(new WriteRequest(channel, buffer, notification, hdlr)); } private static void close(SelectorWriteHandler entry) { entry.cancel(); } private static void handleChannelError( SelectorWriteHandler entry, Throwable error) { // notify callers of the exception and drain all of the send buffers for this channel. do { if (error != null) entry.notifyError(error); } while (entry.next()); close(entry); } // process the write operation private void processWrite(Selector selector) { Set keys = selector.selectedKeys(); Object arr[] = keys.toArray(); for (Object anArr : arr) { SelectionKey key = (SelectionKey) anArr; SelectorWriteHandler entry = (SelectorWriteHandler) key.attachment(); boolean needToDecrementPendingChannels = false; try { if (0 == entry.write()) { // write the buffer and if the remaining bytes is zero, // notify the caller of number of bytes written. entry.notifyObject(entry.getBytesWritten()); // switch to next write buffer or clear interest bit on socket channel. if (!entry.next()) { needToDecrementPendingChannels = true; } } } catch (IOException e) { needToDecrementPendingChannels = true; // connection must of closed handleChannelError(entry, e); } finally { if (needToDecrementPendingChannels) m_pendingChannels--; } } keys.clear(); } public void run() { while (selector.isOpen()) { try { WriteRequest queueEntry; Object o; // When there are no more commands in the Queue, we will hit the blocking code after this loop. while (null != (o = queue.poll(0L, TimeUnit.MILLISECONDS))) { if (o instanceof Shutdown) // Stop the thread { try { selector.close(); } catch(IOException e) { if (log.isTraceEnabled()) log.trace("Write selector close operation failed" , e); } return; } queueEntry = (WriteRequest) o; if (queueEntry.getHandler().add(queueEntry)) { // If the add operation returns true, than means that a buffer is available to be written to the // corresponding channel and channel's selection key has been modified to indicate interest in the // 'write' operation. // If the add operation threw an exception, we will not increment m_pendingChannels which // seems correct as long as a new buffer wasn't added to be sent. // Another way to view this is that we don't have to protect m_pendingChannels on the increment // side, only need to protect on the decrement side (this logic of this run() will be incorrect // if m_pendingChannels is set incorrectly). m_pendingChannels++; } try { // process any connections ready to be written to. if (selector.selectNow() > 0) { processWrite(selector); } } catch (IOException e) { // need to understand what causes this error so we can handle it properly if (log.isErrorEnabled()) log.error("SelectNow operation on write selector failed, didn't expect this to occur, please report this", e); return; // if select fails, give up so we don't go into a busy loop. } } // if there isn't any pending work to do, block on queue to get next request. if (m_pendingChannels == 0) { o = queue.take(); if (o instanceof Shutdown){ // Stop the thread try { selector.close(); } catch(IOException e) { if (log.isTraceEnabled()) log.trace("Write selector close operation failed" , e); } return; } queueEntry = (WriteRequest) o; if (queueEntry.getHandler().add(queueEntry)) m_pendingChannels++; } // otherwise do a blocking wait select operation. else { try { if ((selector.select()) > 0) { processWrite(selector); } } catch (IOException e) { // need to understand what causes this error if (log.isErrorEnabled()) log.error("Failure while writing to socket",e); } } } catch (InterruptedException e) { if (log.isErrorEnabled()) log.error("Thread ("+Thread.currentThread().getName() +") was interrupted", e); } catch (Throwable e) // Log throwable rather than terminating this thread. { // We are a daemon thread so we shouldn't prevent the process from terminating if // the controlling thread decides that should happen. if (log.isErrorEnabled()) log.error("Thread ("+Thread.currentThread().getName() +") caught Throwable" , e); } } } } // Wrapper class for passing Write requests. There will be an instance of this class for each socketChannel // mapped to a Selector. public static class SelectorWriteHandler { private final List m_writeRequests = new LinkedList(); // Collection of writeRequests private boolean m_headerSent = false; private SocketChannel m_channel; private SelectionKey m_key; private Selector m_selector; private int m_bytesWritten = 0; private boolean m_enabled = false; private ByteBuffer m_headerBuffer; SelectorWriteHandler(SocketChannel channel, Selector selector, ByteBuffer headerBuffer) { m_channel = channel; m_selector = selector; m_headerBuffer = headerBuffer; } private void register(Selector selector, SocketChannel channel) throws ClosedChannelException { // register the channel but don't enable OP_WRITE until we have a write request. m_key = channel.register(selector, 0, this); } // return true if selection key is enabled when it wasn't previous to call. private boolean enable() { boolean rc = false; try { if (m_key == null) { // register the socket on first access, // we are the only thread using this variable, so no sync needed. register(m_selector, m_channel); } } catch (ClosedChannelException e) { return rc; } if (!m_enabled) { rc = true; try { m_key.interestOps(SelectionKey.OP_WRITE); } catch (CancelledKeyException e) { // channel must of closed return false; } m_enabled = true; } return rc; } private void disable() { if (m_enabled) { try { m_key.interestOps(0); // pass zero which means that we are not interested in being // notified of anything for this channel. } catch (CancelledKeyException eat) // If we finished writing and didn't get an exception, then { // we probably don't need to throw this exception (if they try to write // again, we will then throw an exception). } m_enabled = false; } } private void cancel() { m_key.cancel(); } boolean add(WriteRequest entry) { m_writeRequests.add(entry); return enable(); } WriteRequest getCurrentRequest() { return m_writeRequests.get(0); } SocketChannel getChannel() { return m_channel; } ByteBuffer getBuffer() { return getCurrentRequest().getBuffer(); } MyFuture getCallback() { return getCurrentRequest().getCallback(); } int getBytesWritten() { return m_bytesWritten; } void notifyError(Throwable error) { if (getCallback() != null) getCallback().setException(error); } void notifyObject(Object result) { if (getCallback() != null) getCallback().set(result); } /** * switch to next request or disable write interest bit if there are no more buffers. * * @return true if another request was found to be processed. */ boolean next() { m_headerSent = false; m_bytesWritten = 0; m_writeRequests.remove(0); // remove current entry boolean rc = !m_writeRequests.isEmpty(); if (!rc) // disable select for this channel if no more entries disable(); return rc; } /** * @return bytes remaining to write. This function will only throw IOException, unchecked exceptions are not * expected to be thrown from here. It is very important for the caller to know if an unchecked exception can * be thrown in here. Please correct the following throws list to include any other exceptions and update * caller to handle them. * @throws IOException */ int write() throws IOException { // Send header first. Note that while we are writing the shared header buffer, // no other threads can access the header buffer as we are the only thread that has access to it. if (!m_headerSent) { m_headerSent = true; m_headerBuffer.clear(); m_headerBuffer.putInt(getBuffer().remaining()); m_headerBuffer.flip(); do { getChannel().write(m_headerBuffer); } // we should be able to handle writing the header in one action but just in case, just do a busy loop while (m_headerBuffer.remaining() > 0); } m_bytesWritten += (getChannel().write(getBuffer())); return getBuffer().remaining(); } } public static class WriteRequest { private final SocketChannel m_channel; private final ByteBuffer m_buffer; private final MyFuture m_callback; private final SelectorWriteHandler m_hdlr; WriteRequest(SocketChannel channel, ByteBuffer buffer, MyFuture callback, SelectorWriteHandler hdlr) { m_channel = channel; m_buffer = buffer; m_callback = callback; m_hdlr = hdlr; } SelectorWriteHandler getHandler() { return m_hdlr; } SocketChannel getChannel() { return m_channel; } ByteBuffer getBuffer() { return m_buffer; } MyFuture getCallback() { return m_callback; } } private static class NullCallable implements Callable { public Object call() { System.out.println("nullCallable.call invoked"); return null; } } private static final NullCallable NULLCALL = new NullCallable(); public static class MyFuture extends FutureTask { // make FutureTask work like the old FutureResult public MyFuture() { super(NULLCALL); } protected void set(Object o) { super.set(o); } protected void setException(Throwable t) { super.setException(t); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/VotingAdapter.java0000644000175000017500000004014011366547366026273 0ustar twernertwernerpackage org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.io.Serializable; import java.util.*; /** * Voting adapter provides a voting functionality for an application. There * should be at most one {@link VotingAdapter} listening on one {@link Channel} * instance. Each adapter can have zero or more registered {@link VotingListener} * instances that will be called during voting process. *

* Decree is an object that has some semantic meaning within the application. * Each voting listener receives a decree and can respond with either * true or false. If the decree has no meaning for the voting * listener, it is required to throw {@link VoteException}. In this case * this specific listener will be excluded from the voting on the specified * decree. After performing local voting, this voting adapter sends the request * back to the originator of the voting process. Originator receives results * from each node and decides if all voting process succeeded or not depending * on the consensus type specified during voting. * * @author Roman Rokytskyy (rrokytskyy@acm.org) * @author Robert Schaffar-Taurok (robert@fusion.at) * @version $Id: VotingAdapter.java,v 1.10 2006/09/27 12:42:53 belaban Exp $ */ public class VotingAdapter implements MessageListener, MembershipListener, VoteResponseProcessor { /** * This consensus type means that at least one positive vote is required * for the voting to succeed. */ public static final int VOTE_ANY = 0; /** * This consensus type means that at least one positive vote and no negative * votes are required for the voting to succeed. */ public static final int VOTE_ALL = 1; /** * This consensus type means that number of positive votes should be greater * than number of negative votes. */ public static final int VOTE_MAJORITY = 2; private static final int PROCESS_CONTINUE = 0; private static final int PROCESS_SKIP = 1; private static final int PROCESS_BREAK = 2; private final RpcDispatcher rpcDispatcher; protected final Log log=LogFactory.getLog(getClass()); private final HashSet suspectedNodes = new HashSet(); private boolean closed; private final List membership_listeners=new LinkedList(); /** * Creates an instance of the VoteChannel that uses JGroups * for communication between group members. * @param channel JGroups channel. */ public VotingAdapter(Channel channel) { rpcDispatcher = new RpcDispatcher(channel, this, this, this); } public VotingAdapter(PullPushAdapter adapter, Serializable id) { rpcDispatcher = new RpcDispatcher(adapter, id, this, this, this); } public Collection getMembers() { return rpcDispatcher != null? rpcDispatcher.getMembers() : null; } public void addMembershipListener(MembershipListener l) { if(l != null && !membership_listeners.contains(l)) membership_listeners.add(l); } public void removeMembershipListener(MembershipListener l) { if(l != null) membership_listeners.remove(l); } /** * Performs actual voting on the VoteChannel using the JGroups * facilities for communication. */ public boolean vote(Object decree, int consensusType, long timeout) throws ChannelException { return vote(decree, consensusType, timeout, null); } /** * Performs actual voting on the VoteChannel using the JGroups * facilities for communication. */ public boolean vote(Object decree, int consensusType, long timeout, VoteResponseProcessor voteResponseProcessor) throws ChannelException { if (closed) throw new ChannelException("Channel was closed."); if(log.isDebugEnabled()) log.debug("Conducting voting on decree " + decree + ", consensus type " + getConsensusStr(consensusType) + ", timeout " + timeout); int mode = GroupRequest.GET_ALL; // perform the consensus mapping switch (consensusType) { case VotingAdapter.VOTE_ALL : mode = GroupRequest.GET_ALL; break; case VotingAdapter.VOTE_ANY : mode = GroupRequest.GET_FIRST; break; case VotingAdapter.VOTE_MAJORITY : mode = GroupRequest.GET_MAJORITY; break; default : mode = GroupRequest.GET_ALL; } try { java.lang.reflect.Method method = this.getClass().getMethod( "localVote", new Class[] { Object.class }); MethodCall methodCall = new MethodCall(method, new Object[] {decree}); if(log.isDebugEnabled()) log.debug("Calling remote methods..."); // vote RspList responses = rpcDispatcher.callRemoteMethods( null, methodCall, mode, timeout); if(log.isDebugEnabled()) log.debug("Checking responses."); if (voteResponseProcessor == null) { voteResponseProcessor = this; } return voteResponseProcessor.processResponses(responses, consensusType, decree); } catch(NoSuchMethodException nsmex) { // UPS!!! How can this happen?! if(log.isErrorEnabled()) log.error("Could not find method localVote(Object). " + nsmex.toString()); throw new UnsupportedOperationException( "Cannot execute voting because of absence of " + this.getClass().getName() + ".localVote(Object) method."); } } /** * Processes the response list and makes a decision according to the * type of the consensus for current voting. *

* Note: we do not support voting in case of Byzantine failures, i.e. * when the node responds with the fault message. */ public boolean processResponses(RspList responses, int consensusType, Object decree) throws ChannelException { if (responses == null) { return false; } boolean voteResult = false; int totalPositiveVotes = 0; int totalNegativeVotes = 0; for(Iterator it=responses.values().iterator(); it.hasNext();) { Rsp response = (Rsp)it.next(); switch(checkResponse(response)) { case PROCESS_SKIP : continue; case PROCESS_BREAK : return false; } VoteResult result = (VoteResult)response.getValue(); totalPositiveVotes += result.getPositiveVotes(); totalNegativeVotes += result.getNegativeVotes(); } switch(consensusType) { case VotingAdapter.VOTE_ALL : voteResult = (totalNegativeVotes == 0 && totalPositiveVotes > 0); break; case VotingAdapter.VOTE_ANY : voteResult = (totalPositiveVotes > 0); break; case VotingAdapter.VOTE_MAJORITY : voteResult = (totalPositiveVotes > totalNegativeVotes); } return voteResult; } /** * This method checks the response and says the processResponses() method * what to do. * @return PROCESS_CONTINUE to continue calculating votes, * PROCESS_BREAK to stop calculating votes from the nodes, * PROCESS_SKIP to skip current response. * @throws ChannelException when the response is fatal to the * current voting process. */ private int checkResponse(Rsp response) throws ChannelException { if (!response.wasReceived()) { if(log.isDebugEnabled()) log.debug("Response from node " + response.getSender() + " was not received."); // what do we do when one node failed to respond? //throw new ChannelException("Node " + response.GetSender() + // " failed to respond."); return PROCESS_BREAK ; } /**@todo check what to do here */ if (response.wasSuspected()) { if(log.isDebugEnabled()) log.debug("Node " + response.getSender() + " was suspected."); // wat do we do when one node is suspected? return PROCESS_SKIP ; } Object object = response.getValue(); // we received exception/error, something went wrong // on one of the nodes... and we do not handle such faults if (object instanceof Throwable) { throw new ChannelException("Node " + response.getSender() + " is faulty."); } if (object == null) { return PROCESS_SKIP; } // it is always interesting to know the class that caused failure... if (!(object instanceof VoteResult)) { String faultClass = object.getClass().getName(); // ...but we do not handle byzantine faults throw new ChannelException("Node " + response.getSender() + " generated fault (class " + faultClass + ')'); } // what if we received the response from faulty node? if (object instanceof FailureVoteResult) { if(log.isErrorEnabled()) log.error(((FailureVoteResult)object).getReason()); return PROCESS_BREAK; } // everything is fine :) return PROCESS_CONTINUE; } /** * Callback for notification about the new view of the group. */ public void viewAccepted(View newView) { // clean nodes that were suspected but still exist in new view Iterator iterator = suspectedNodes.iterator(); while(iterator.hasNext()) { Address suspectedNode = (Address)iterator.next(); if (newView.containsMember(suspectedNode)) iterator.remove(); } for(Iterator it=membership_listeners.iterator(); it.hasNext();) { MembershipListener listener=(MembershipListener)it.next(); try { listener.viewAccepted(newView); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed calling viewAccepted() on " + listener, t); } } } /** * Callback for notification that one node is suspected */ public void suspect(Address suspected) { suspectedNodes.add(suspected); for(Iterator it=membership_listeners.iterator(); it.hasNext();) { MembershipListener listener=(MembershipListener)it.next(); try { listener.suspect(suspected); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed calling suspect() on " + listener, t); } } } /** * Blocks the channel until the ViewAccepted is invoked. */ public void block() { for(Iterator it=membership_listeners.iterator(); it.hasNext();) { MembershipListener listener=(MembershipListener)it.next(); try { listener.block(); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed calling block() on " + listener, t); } } } /** * Get the channel state. * * @return always null, we do not have any group-shared * state. */ public byte[] getState() { return null; } /** * Receive the message. All messages are ignored. * * @param msg message to check. */ public void receive(org.jgroups.Message msg) { // do nothing } /** * Set the channel state. We do nothing here. */ public void setState(byte[] state) { // ignore the state, we do not have any. } private final Set voteListeners = new HashSet(); private VotingListener[] listeners; /** * Vote on the specified decree requiring all nodes to vote. * * @param decree decree on which nodes should vote. * @param timeout time during which nodes can vote. * * @return true if nodes agreed on a decree, otherwise * false * * @throws ChannelException if something went wrong. */ public boolean vote(Object decree, long timeout) throws ChannelException { return vote(decree, timeout, null); } /** * Vote on the specified decree requiring all nodes to vote. * * @param decree decree on which nodes should vote. * @param timeout time during which nodes can vote. * @param voteResponseProcessor processor which will be called for every response that is received. * * @return true if nodes agreed on a decree, otherwise * false * * @throws ChannelException if something went wrong. */ public boolean vote(Object decree, long timeout, VoteResponseProcessor voteResponseProcessor) throws ChannelException { return vote(decree, VOTE_ALL, timeout, voteResponseProcessor); } /** * Adds voting listener. */ public void addVoteListener(VotingListener listener) { voteListeners.add(listener); listeners = (VotingListener[])voteListeners.toArray( new VotingListener[voteListeners.size()]); } /** * Removes voting listener. */ public void removeVoteListener(VotingListener listener) { voteListeners.remove(listener); listeners = (VotingListener[])voteListeners.toArray( new VotingListener[voteListeners.size()]); } /** * This method performs voting on the specific decree between all * local voteListeners. */ public VoteResult localVote(Object decree) { VoteResult voteResult = new VoteResult(); for(int i = 0; i < listeners.length; i++) { VotingListener listener = listeners[i]; try { voteResult.addVote(listener.vote(decree)); } catch (VoteException vex) { // do nothing here. } catch(RuntimeException ex) { if(log.isErrorEnabled()) log.error(ex.toString()); // if we are here, then listener // had thrown a RuntimeException return new FailureVoteResult(ex.getMessage()); } } if(log.isDebugEnabled()) log.debug("Voting on decree " + decree.toString() + " : " + voteResult.toString()); return voteResult; } /** * Convert consensus type into string representation. This method is * useful for debugginf. * * @param consensusType type of the consensus. * * @return string representation of the consensus type. */ public static String getConsensusStr(int consensusType) { switch(consensusType) { case VotingAdapter.VOTE_ALL : return "VOTE_ALL"; case VotingAdapter.VOTE_ANY : return "VOTE_ANY"; case VotingAdapter.VOTE_MAJORITY : return "VOTE_MAJORITY"; default : return "UNKNOWN"; } } /** * This class represents the result of local voting. It contains a * number of positive and negative votes collected during local voting. */ public static class VoteResult implements Serializable { private int positiveVotes = 0; private int negativeVotes = 0; private static final long serialVersionUID = 2868605599965196746L; public void addVote(boolean vote) { if (vote) positiveVotes++; else negativeVotes++; } public int getPositiveVotes() { return positiveVotes; } public int getNegativeVotes() { return negativeVotes; } public String toString() { return "VoteResult: up=" + positiveVotes + ", down=" + negativeVotes; } } /** * Class that represents a result of local voting on the failed node. */ public static class FailureVoteResult extends VoteResult { private final String reason; public FailureVoteResult(String reason) { this.reason = reason; } public String getReason() { return reason; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/LockNotGrantedException.java0000644000175000017500000000067711366547366030274 0ustar twernertwernerpackage org.jgroups.blocks; /** * This exception indicated that lock manager refused to give a lock on * some resource. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public class LockNotGrantedException extends Exception { private static final long serialVersionUID = 4074824788210185433L; public LockNotGrantedException() { super(); } public LockNotGrantedException(String s) { super(s); } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/RequestCorrelator.java0000644000175000017500000010332711366547366027220 0ustar twernertwerner// $Id: RequestCorrelator.java,v 1.40.2.4 2009/02/04 16:39:26 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.protocols.TP; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import java.io.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Framework to send requests and receive matching responses (matching on * request ID). * Multiple requests can be sent at a time. Whenever a response is received, * the correct RspCollector is looked up (key = id) and its * method receiveResponse() invoked. A caller may use * done() to signal that no more responses are expected, and that * the corresponding entry may be removed. *

* RequestCorrelator can be installed at both client and server * sides, it can also switch roles dynamically; i.e., send a request and at * the same time process an incoming request (when local delivery is enabled, * this is actually the default). *

* * @author Bela Ban */ public class RequestCorrelator { /** The protocol layer to use to pass up/down messages. Can be either a Protocol or a Transport */ protected Object transport=null; /** The table of pending requests (keys=Long (request IDs), values=RequestEntry) */ protected final ConcurrentMap requests=new ConcurrentHashMap(); /** The handler for the incoming requests. It is called from inside the dispatcher thread */ protected RequestHandler request_handler=null; /** Possibility for an external marshaller to marshal/unmarshal responses */ protected RpcDispatcher.Marshaller2 marshaller=null; /** makes the instance unique (together with IDs) */ protected String name=null; /** The dispatching thread pool */ protected Scheduler scheduler=null; /** The address of this group member */ protected Address local_addr=null; /** * This field is used only if deadlock detection is enabled. * In case of nested synchronous requests, it holds a list of the * addreses of the senders with the address at the bottom being the * address of the first caller */ protected java.util.Stack

call_stack=null; /** Whether or not to perform deadlock detection for synchronous (potentially recursive) group method invocations. * If on, we use a scheduler (handling a priority queue), otherwise we don't and call handleRequest() directly. */ protected boolean deadlock_detection=false; /** * This field is used only if deadlock detection is enabled. * It sets the calling stack to the currently running request */ private CallStackSetter call_stack_setter=null; /** Process items on the queue concurrently (Scheduler). The default is to wait until the processing of an item * has completed before fetching the next item from the queue. Note that setting this to true * may destroy the properties of a protocol stack, e.g total or causal order may not be * guaranteed. Set this to true only if you know what you're doing ! */ protected boolean concurrent_processing=false; protected boolean started=false; private final MyProbeHandler probe_handler=new MyProbeHandler(requests); protected static final Log log=LogFactory.getLog(RequestCorrelator.class); /** * Constructor. Uses transport to send messages. If handler * is not null, all incoming requests will be dispatched to it (via * handle(Message)). * * @param name Used to differentiate between different RequestCorrelators * (e.g. in different protocol layers). Has to be unique if multiple * request correlators are used. * * @param transport Used to send/pass up requests. Can be either a Transport (only send() will be * used then), or a Protocol (up_prot.up()/down_prot.down() will be used) * * @param handler Request handler. Method handle(Message) * will be called when a request is received. */ public RequestCorrelator(String name, Object transport, RequestHandler handler) { this.name = name; this.transport = transport; request_handler = handler; start(); } public RequestCorrelator(String name, Object transport, RequestHandler handler, Address local_addr) { this.name = name; this.transport = transport; this.local_addr=local_addr; request_handler = handler; start(); } /** * Constructor. Uses transport to send messages. If handler * is not null, all incoming requests will be dispatched to it (via * handle(Message)). * * @param name Used to differentiate between different RequestCorrelators * (e.g. in different protocol layers). Has to be unique if multiple * request correlators are used. * * @param transport Used to send/pass up requests. Can be either a Transport (only send() will be * used then), or a Protocol (up_prot.up()/down_prot.down() will be used) * * @param handler Request handler. Method handle(Message) * will be called when a request is received. * * @param deadlock_detection When enabled (true) recursive synchronous * message calls will be detected and processed with higher priority in * order to solve deadlocks. Slows down processing a little bit when * enabled due to runtime checks involved. */ public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection) { this.deadlock_detection = deadlock_detection; this.name = name; this.transport = transport; request_handler = handler; start(); } public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection, boolean concurrent_processing) { this.deadlock_detection = deadlock_detection; this.name = name; this.transport = transport; request_handler = handler; this.concurrent_processing = concurrent_processing; start(); } public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection, Address local_addr) { this.deadlock_detection = deadlock_detection; this.name = name; this.transport = transport; this.local_addr = local_addr; request_handler = handler; start(); } public RequestCorrelator(String name, Object transport, RequestHandler handler, boolean deadlock_detection, Address local_addr, boolean concurrent_processing) { this.deadlock_detection = deadlock_detection; this.name = name; this.transport = transport; this.local_addr = local_addr; request_handler = handler; this.concurrent_processing = concurrent_processing; start(); } /** * Switch the deadlock detection mechanism on/off * @param flag the deadlock detection flag */ public void setDeadlockDetection(boolean flag) { if(deadlock_detection != flag) { // only set it if different deadlock_detection=flag; if(started) { if(deadlock_detection) { startScheduler(); } else { stopScheduler(); } } } } public void setRequestHandler(RequestHandler handler) { request_handler=handler; start(); } public void setConcurrentProcessing(boolean concurrent_processing) { this.concurrent_processing=concurrent_processing; } /** * Helper method for {@link #sendRequest(long,List,Message,RspCollector)}. */ public void sendRequest(long id, Message msg, RspCollector coll) throws Exception { sendRequest(id, null, msg, coll); } public RpcDispatcher.Marshaller getMarshaller() { return marshaller; } public void setMarshaller(RpcDispatcher.Marshaller marshaller) { if(marshaller == null) this.marshaller=null; else if(marshaller instanceof RpcDispatcher.Marshaller2) this.marshaller=(RpcDispatcher.Marshaller2)marshaller; else this.marshaller=new RpcDispatcher.MarshallerAdapter(marshaller); } public void sendRequest(long id, List
dest_mbrs, Message msg, RspCollector coll) throws Exception { sendRequest(id, dest_mbrs, msg, coll, false); } /** * Send a request to a group. If no response collector is given, no * responses are expected (making the call asynchronous). * * @param id The request ID. Must be unique for this JVM (e.g. current * time in millisecs) * @param dest_mbrs The list of members who should receive the call. Usually a group RPC * is sent via multicast, but a receiver drops the request if its own address * is not in this list. Will not be used if it is null. * @param msg The request to be sent. The body of the message carries * the request data * * @param coll A response collector (usually the object that invokes * this method). Its methods receiveResponse() and * suspect() will be invoked when a message has been received * or a member is suspected, respectively. */ public void sendRequest(long id, List
dest_mbrs, Message msg, RspCollector coll, boolean use_anycasting) throws Exception { Header hdr; if(transport == null) { if(log.isWarnEnabled()) log.warn("transport is not available !"); return; } // i. Create the request correlator header and add it to the // msg // ii. If a reply is expected (sync call / 'coll != null'), add a // coresponding entry in the pending requests table // iii. If deadlock detection is enabled, set/update the call stack // iv. Pass the msg down to the protocol layer below hdr=new Header(Header.REQ, id, (coll != null), name); hdr.dest_mbrs=dest_mbrs; if (coll != null) { if(deadlock_detection) { if(local_addr == null) { if(log.isErrorEnabled()) log.error("local address is null !"); return; } java.util.Stack
new_call_stack = (call_stack != null? (java.util.Stack
)call_stack.clone():new java.util.Stack
()); new_call_stack.push(local_addr); hdr.callStack=new_call_stack; } addEntry(hdr.id, coll); } msg.putHeader(name, hdr); if(transport instanceof Protocol) { if(use_anycasting) { Message copy; for(Iterator it=dest_mbrs.iterator(); it.hasNext();) { Address mbr=(Address)it.next(); copy=msg.copy(true); copy.setDest(mbr); ((Protocol)transport).down(new Event(Event.MSG, copy)); } } else { ((Protocol)transport).down(new Event(Event.MSG, msg)); } } else if(transport instanceof Transport) { if(use_anycasting) { Message copy; for(Iterator it=dest_mbrs.iterator(); it.hasNext();) { Address mbr=(Address)it.next(); copy=msg.copy(true); copy.setDest(mbr); ((Transport)transport).send(copy); } } else { ((Transport)transport).send(msg); } } else throw new IllegalStateException("transport has to be either a Transport or a Protocol, however it is a " + transport.getClass()); } /** * Used to signal that a certain request may be garbage collected as * all responses have been received. */ public void done(long id) { removeEntry(id); } /** * Callback. *

* Called by the protocol below when a message has been received. The * algorithm should test whether the message is destined for us and, * if not, pass it up to the next layer. Otherwise, it should remove * the header and check whether the message is a request or response. * In the first case, the message will be delivered to the request * handler registered (calling its handle() method), in the * second case, the corresponding response collector is looked up and * the message delivered. * @param evt The event to be received * @return Whether or not the event was consumed. If true, don't pass message up, else pass it up */ public boolean receive(Event evt) { switch(evt.getType()) { case Event.SUSPECT: // don't wait for responses from faulty members receiveSuspect((Address)evt.getArg()); break; case Event.VIEW_CHANGE: // adjust number of responses to wait for receiveView((View)evt.getArg()); break; case Event.SET_LOCAL_ADDRESS: setLocalAddress((Address)evt.getArg()); break; case Event.MSG: if(receiveMessage((Message)evt.getArg())) return true; // message was consumed, don't pass it up break; } // if(transport instanceof Protocol) // ((Protocol)transport).getUpProtocol().up(evt); // else // if(log.isErrorEnabled()) log.error("we do not pass up messages via Transport"); return false; } /** */ public final void start() { if(deadlock_detection) { startScheduler(); } started=true; } public void stop() { stopScheduler(); started=false; } void startScheduler() { if(scheduler == null) { scheduler=new Scheduler(); if(deadlock_detection && call_stack_setter == null) { call_stack_setter=new CallStackSetter(); scheduler.setListener(call_stack_setter); } if(concurrent_processing) scheduler.setConcurrentProcessing(concurrent_processing); scheduler.start(); } } void stopScheduler() { if(scheduler != null) { scheduler.stop(); scheduler=null; } } public void registerProbeHandler(TP transport) { if(transport != null) transport.registerProbeHandler(probe_handler); } public void unregisterProbeHandler(TP transport) { if(transport != null) transport.unregisterProbeHandler(probe_handler); } // ....................................................................... /** * Event.SUSPECT event received from a layer below. *

* All response collectors currently registered will * be notified that mbr may have crashed, so they won't * wait for its response. */ public void receiveSuspect(Address mbr) { if(mbr == null) return; if(log.isDebugEnabled()) log.debug("suspect=" + mbr); // copy so we don't run into bug #761804 - Bela June 27 2003 // copy=new ArrayList(requests.values()); // removed because ConcurrentReaderHashMap can tolerate concurrent mods (bela May 8 2006) for(RspCollector coll: requests.values()) { if(coll != null) coll.suspect(mbr); } } /** * Event.VIEW_CHANGE event received from a layer below. *

* Mark all responses from members that are not in new_view as * NOT_RECEIVED. * */ public void receiveView(View new_view) { // ArrayList copy; // copy so we don't run into bug #761804 - Bela June 27 2003 // copy=new ArrayList(requests.values()); // removed because ConcurrentReaderHashMap can tolerate concurrent mods (bela May 8 2006) for(RspCollector coll: requests.values()) { if(coll != null) coll.viewChange(new_view); } } /** * Handles a message coming from a layer below * * @return true if the message was consumed, don't pass it further up, else false */ public boolean receiveMessage(Message msg) { // i. If header is not an instance of request correlator header, ignore // // ii. Check whether the message was sent by a request correlator with // the same name (there may be multiple request correlators in the same // protocol stack...) Header hdr=(Header)msg.getHeader(name); if(hdr == null) return false; if(hdr.corrName == null || !hdr.corrName.equals(name)) { if(log.isTraceEnabled()) { log.trace(new StringBuilder("name of request correlator header (").append(hdr.corrName). append(") is different from ours (").append(name).append("). Msg not accepted, passed up")); } return false; } // If the header contains a destination list, and we are not part of it, then we discard the // request (was addressed to other members) java.util.List dests=hdr.dest_mbrs; if(dests != null && local_addr != null && !dests.contains(local_addr)) { if(log.isTraceEnabled()) { log.trace(new StringBuilder("discarded request from ").append(msg.getSrc()). append(" as we are not part of destination list (local_addr="). append(local_addr).append(", hdr=").append(hdr).append(')')); } return true; // don't pass this message further up } // [Header.REQ]: // i. If there is no request handler, discard // ii. Check whether priority: if synchronous and call stack contains // address that equals local address -> add priority request. Else // add normal request. // // [Header.RSP]: // Remove the msg request correlator header and notify the associated // RspCollector that a reply has been received switch(hdr.type) { case Header.REQ: if(request_handler == null) { if(log.isWarnEnabled()) { log.warn("there is no request handler installed to deliver request !"); } return true; } if(deadlock_detection) { if(scheduler == null) { log.error("deadlock_detection is true, but scheduler is null: this is not supposed to happen" + " (discarding request)"); break; } Request req=new Request(msg, hdr); java.util.Stack stack=hdr.callStack; if(hdr.rsp_expected && stack != null && local_addr != null) { if(stack.contains(local_addr)) { if(log.isTraceEnabled()) log.trace("call stack=" + hdr.callStack + " contains " + local_addr + ": adding request to priority queue"); scheduler.addPrio(req); break; } } scheduler.add(req); break; } handleRequest(msg, hdr); break; case Header.RSP: msg.getHeader(name); RspCollector coll=requests.get(Long.valueOf(hdr.id)); if(coll != null) { Address sender=msg.getSrc(); Object retval=null; byte[] buf=msg.getBuffer(); int offset=msg.getOffset(), length=msg.getLength(); try { retval=marshaller != null? marshaller.objectFromByteBuffer(buf, offset, length) : Util.objectFromByteBuffer(buf, offset, length); } catch(Exception e) { log.error("failed unmarshalling buffer into return value", e); retval=e; } coll.receiveResponse(retval, sender); } break; default: msg.getHeader(name); if(log.isErrorEnabled()) log.error("header's type is neither REQ nor RSP !"); break; } return true; // message was consumed } public Address getLocalAddress() { return local_addr; } public void setLocalAddress(Address local_addr) { this.local_addr=local_addr; } // ....................................................................... /** * Add an association of:
* ID -> RspCollector */ private void addEntry(long id, RspCollector coll) { requests.putIfAbsent(id, coll); } /** * Remove the request entry associated with the given ID * * @param id the id of the RequestEntry to remove */ private void removeEntry(long id) { Long id_obj = new Long(id); // changed by bela Feb 28 2003 (bug fix for 690606) // changed back to use synchronization by bela June 27 2003 (bug fix for #761804), // we can do this because we now copy for iteration (viewChange() and suspect()) requests.remove(id_obj); } /** * Handle a request msg for this correlator * * @param req the request msg */ protected void handleRequest(Message req, Header hdr) { Object retval; Object rsp_buf; // either byte[] or Buffer Header rsp_hdr; Message rsp; // i. Get the request correlator header from the msg and pass it to // the registered handler // // ii. If a reply is expected, pack the return value from the request // handler to a reply msg and send it back. The reply msg has the same // ID as the request and the name of the sender request correlator if(log.isTraceEnabled()) { log.trace(new StringBuilder("calling (").append((request_handler != null? request_handler.getClass().getName() : "null")). append(") with request ").append(hdr.id)); } try { retval=request_handler.handle(req); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("error invoking method", t); retval=t; } if(!hdr.rsp_expected) // asynchronous call, we don't need to send a response; terminate call here return; if(transport == null) { if(log.isErrorEnabled()) log.error("failure sending response; no transport available"); return; } // changed (bela Feb 20 2004): catch exception and return exception try { // retval could be an exception, or a real value rsp_buf=marshaller != null? marshaller.objectToBuffer(retval) : Util.objectToByteBuffer(retval); } catch(Throwable t) { try { // this call should succeed (all exceptions are serializable) rsp_buf=marshaller != null? marshaller.objectToBuffer(t) : Util.objectToByteBuffer(t); } catch(Throwable tt) { if(log.isErrorEnabled()) log.error("failed sending rsp: return value (" + retval + ") is not serializable"); return; } } rsp=req.makeReply(); rsp.setFlag(Message.OOB); // back ported from 2.7: make response OOB no matter what if(rsp_buf instanceof Buffer) rsp.setBuffer((Buffer)rsp_buf); else if (rsp_buf instanceof byte[]) rsp.setBuffer((byte[])rsp_buf); rsp_hdr=new Header(Header.RSP, hdr.id, false, name); rsp.putHeader(name, rsp_hdr); if(log.isTraceEnabled()) log.trace(new StringBuilder("sending rsp for ").append(rsp_hdr.id).append(" to ").append(rsp.getDest())); try { if(transport instanceof Protocol) ((Protocol)transport).down(new Event(Event.MSG, rsp)); else if(transport instanceof Transport) ((Transport)transport).send(rsp); else if(log.isErrorEnabled()) log.error("transport object has to be either a " + "Transport or a Protocol, however it is a " + transport.getClass()); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("failed sending the response", e); } } // ....................................................................... /** * The header for RequestCorrelator messages */ public static final class Header extends org.jgroups.Header implements Streamable { public static final byte REQ = 0; public static final byte RSP = 1; /** Type of header: request or reply */ public byte type=REQ; /** * The id of this request to distinguish among other requests from * the same RequestCorrelator */ public long id=0; /** msg is synchronous if true */ public boolean rsp_expected=true; /** The unique name of the associated RequestCorrelator */ public String corrName=null; /** Stack<Address>. Contains senders (e.g. P --> Q --> R) */ public java.util.Stack

callStack=null; /** Contains a list of members who should receive the request (others will drop). Ignored if null */ public java.util.List
dest_mbrs=null; /** * Used for externalization */ public Header() {} /** * @param type type of header (REQ/RSP) * @param id id of this header relative to ids of other requests * originating from the same correlator * @param rsp_expected whether it's a sync or async request * @param name the name of the RequestCorrelator from which */ public Header(byte type, long id, boolean rsp_expected, String name) { this.type = type; this.id = id; this.rsp_expected = rsp_expected; this.corrName = name; } /** */ public String toString() { StringBuilder ret=new StringBuilder(); ret.append("[Header: name=" + corrName + ", type="); ret.append(type == REQ ? "REQ" : type == RSP ? "RSP" : ""); ret.append(", id=" + id); ret.append(", rsp_expected=" + rsp_expected + ']'); if(callStack != null) ret.append(", call stack=" + callStack); if(dest_mbrs != null) ret.append(", dest_mbrs=").append(dest_mbrs); return ret.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeLong(id); out.writeBoolean(rsp_expected); if(corrName != null) { out.writeBoolean(true); out.writeUTF(corrName); } else { out.writeBoolean(false); } out.writeObject(callStack); out.writeObject(dest_mbrs); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type = in.readByte(); id = in.readLong(); rsp_expected = in.readBoolean(); if(in.readBoolean()) corrName = in.readUTF(); callStack = (java.util.Stack
)in.readObject(); dest_mbrs=(java.util.List
)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(id); out.writeBoolean(rsp_expected); if(corrName != null) { out.writeBoolean(true); out.writeUTF(corrName); } else { out.writeBoolean(false); } if(callStack != null) { out.writeBoolean(true); out.writeShort(callStack.size()); Address mbr; for(int i=0; i < callStack.size(); i++) { mbr=(Address)callStack.elementAt(i); Util.writeAddress(mbr, out); } } else { out.writeBoolean(false); } Util.writeAddresses(dest_mbrs, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { boolean present; type=in.readByte(); id=in.readLong(); rsp_expected=in.readBoolean(); present=in.readBoolean(); if(present) corrName=in.readUTF(); present=in.readBoolean(); if(present) { callStack=new Stack
(); short len=in.readShort(); Address tmp; for(short i=0; i < len; i++) { tmp=Util.readAddress(in); callStack.add(tmp); } } dest_mbrs=(List
)Util.readAddresses(in, java.util.LinkedList.class); } public int size() { int retval=Global.BYTE_SIZE // type + Global.LONG_SIZE // id + Global.BYTE_SIZE; // rsp_expected retval+=Global.BYTE_SIZE; // presence for corrName if(corrName != null) retval+=corrName.length() +2; // UTF retval+=Global.BYTE_SIZE; // presence if(callStack != null) { retval+=Global.SHORT_SIZE; // number of elements if(!callStack.isEmpty()) { Address mbr=(Address)callStack.firstElement(); retval+=callStack.size() * (Util.size(mbr)); } } retval+=Util.size(dest_mbrs); return retval; } } private static class MyProbeHandler implements TP.ProbeHandler { private final ConcurrentMap requests; public MyProbeHandler(ConcurrentMap requests) { this.requests=requests; } public Map handleProbe(String... keys) { if(requests == null) return null; Map retval=new HashMap(); for(String key: keys) { if(key.equals("requests")) { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: requests.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } retval.put("requests", sb.toString()); break; } } return retval; } public String[] supportedKeys() { return new String[]{"requests"}; } } /** * Listens for scheduler events and sets the current call chain (stack) * whenever a thread is started, or a suspended thread resumed. Does * this only for synchronous requests (Runnable is actually * a Request). */ private class CallStackSetter implements SchedulerListener { public void started(Runnable r) { setCallStack(r); } public void stopped(Runnable r) { setCallStack(null); } public void suspended(Runnable r) { setCallStack(null); } public void resumed(Runnable r) { setCallStack(r); } void setCallStack(Runnable r) { java.util.Stack new_stack; Message req; Header hdr; Object obj; if(r == null) { call_stack=null; return; } req=((Request)r).req; if(req == null) return; obj=req.getHeader(name); if(obj == null || !(obj instanceof Header)) return; hdr=(Header)obj; if(hdr.rsp_expected == false) return; new_stack=hdr.callStack; if(new_stack != null) call_stack=(java.util.Stack
)new_stack.clone(); } } /** * The runnable for an incoming request which is submitted to the * dispatcher */ private class Request implements Runnable { final Message req; final Header hdr; public Request(Message req, Header hdr) { this.req=req; this.hdr=hdr;} public void run() { handleRequest(req, hdr); } public String toString() { StringBuilder sb=new StringBuilder(); if(req != null) sb.append("req=" + req + ", headers=" + req.printObjectHeaders()); return sb.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/LockingException.java0000644000175000017500000000147211366547366026776 0ustar twernertwerner// $Id: LockingException.java,v 1.2.4.1 2008/01/22 10:00:58 belaban Exp $ package org.jgroups.blocks; import java.util.Map; public class LockingException extends Exception { private static final long serialVersionUID = -712594616520011007L; Map failed_lockers=null; // list of members who failed acquiring locks (keys=Address, values=exception string) public LockingException(String msg) { super(msg); } public LockingException(Map m) { super("LockingException"); failed_lockers=m; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(super.toString()); if(failed_lockers != null && failed_lockers.size() > 0) sb.append(" (failed members: ").append(failed_lockers); return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/DistributedQueue.java0000644000175000017500000005126111366547366027021 0ustar twernertwerner// $Id: DistributedQueue.java,v 1.19.6.1 2008/02/28 07:31:56 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.util.RspList; import org.jgroups.util.Util; import java.io.Serializable; import java.util.*; /** * Provides the abstraction of a java.util.LinkedList that is replicated at several * locations. Any change to the list (reset, add, remove, etc.) will transparently be * propagated to all replicas in the group. All read-only methods will always access the * local replica.

* Both keys and values added to the list must be serializable, the reason * being that they will be sent across the network to all replicas of the group. * An instance of this class will contact an existing member of the group to fetch its * initial state. * Beware to use a total protocol on initialization or elements would not be in same * order on all replicas. * @author Romuald du Song */ public class DistributedQueue implements MessageListener, MembershipListener, Cloneable { public interface Notification { void entryAdd(Object value); void entryRemoved(Object key); void viewChange(Vector new_mbrs, Vector old_mbrs); void contentsCleared(); void contentsSet(Collection new_entries); } protected Log logger = LogFactory.getLog(getClass()); private long internal_timeout = 10000; // 10 seconds to wait for a response /*lock object for synchronization*/ protected final Object mutex = new Object(); protected boolean stopped = false; // whether to we are stopped ! protected LinkedList internalQueue; protected Channel channel; protected RpcDispatcher disp = null; protected String groupname = null; protected Vector notifs = new Vector(); // to be notified when mbrship changes protected Vector members = new Vector(); // keeps track of all DHTs private Class[] add_signature = null; private Class[] addAtHead_signature = null; private Class[] addAll_signature = null; private Class[] reset_signature = null; private Class[] remove_signature = null; /** * Creates a DistributedQueue * @param groupname The name of the group to join * @param factory The ChannelFactory which will be used to create a channel * @param properties The property string to be used to define the channel * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. */ public DistributedQueue(String groupname, ChannelFactory factory, String properties, long state_timeout) throws ChannelException { if (logger.isDebugEnabled()) { logger.debug("DistributedQueue(" + groupname + ',' + properties + ',' + state_timeout); } this.groupname = groupname; initSignatures(); internalQueue = new LinkedList(); channel = (factory != null) ? factory.createChannel((Object)properties) : new JChannel(properties); disp = new RpcDispatcher(channel, this, this, this); disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall channel.connect(groupname); start(state_timeout); } public DistributedQueue(JChannel channel) { this.groupname = channel.getClusterName(); this.channel = channel; init(); } /** * Uses a user-provided PullPushAdapter to create the dispatcher rather than a Channel. If id is non-null, it will be * used to register under that id. This is typically used when another building block is already using * PullPushAdapter, and we want to add this building block in addition. The id is the used to discriminate * between messages for the various blocks on top of PullPushAdapter. If null, we will assume we are the * first block created on PullPushAdapter. * The caller needs to call start(), before using the this block. It gives the opportunity for the caller * to register as a lessoner for Notifications events. * @param adapter The PullPushAdapter which to use as underlying transport * @param id A serializable object (e.g. an Integer) used to discriminate (multiplex/demultiplex) between * requests/responses for different building blocks on top of PullPushAdapter. */ public DistributedQueue(PullPushAdapter adapter, Serializable id) { this.channel = (Channel)adapter.getTransport(); this.groupname = this.channel.getClusterName(); initSignatures(); internalQueue = new LinkedList(); disp = new RpcDispatcher(adapter, id, this, this, this); disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall } protected final void init() { initSignatures(); internalQueue = new LinkedList(); disp = new RpcDispatcher(channel, this, this, this); disp.setDeadlockDetection(false); // To ensure strict FIFO MethodCall } public final void start(long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { boolean rc; logger.debug("DistributedQueue.initState(" + groupname + "): starting state retrieval"); rc = channel.getState(null, state_timeout); if (rc) { logger.info("DistributedQueue.initState(" + groupname + "): state was retrieved successfully"); } else { logger.info("DistributedQueue.initState(" + groupname + "): state could not be retrieved (first member)"); } } public Address getLocalAddress() { return (channel != null) ? channel.getLocalAddress() : null; } public Channel getChannel() { return channel; } public void addNotifier(Notification n) { if (n != null && !notifs.contains(n)) { notifs.addElement(n); } } public void removeNotifier(Notification n) { notifs.removeElement(n); } public void stop() { /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.clear(); if (disp != null) { disp.stop(); disp = null; } if (channel != null) { channel.close(); channel = null; } stopped = true; } } /** * Add the speficied element at the bottom of the queue * @param value */ public void add(Object value) { try { Object retval = null; RspList rsp = disp.callRemoteMethods(null, "_add", new Object[]{value}, add_signature, GroupRequest.GET_ALL, 0); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } } catch (Exception e) { logger.error("Unable to add value " + value, e); } } /** * Add the speficied element at the top of the queue * @param value */ public void addAtHead(Object value) { try { disp.callRemoteMethods(null, "_addAtHead", new Object[]{value}, addAtHead_signature, GroupRequest.GET_ALL, 0); } catch (Exception e) { logger.error("Unable to addAtHead value " + value, e); } } /** * Add the speficied collection to the top of the queue. * Elements are added in the order that they are returned by the specified * collection's iterator. * @param values */ public void addAll(Collection values) { try { disp.callRemoteMethods(null, "_addAll", new Object[]{values}, addAll_signature, GroupRequest.GET_ALL, 0); } catch (Exception e) { logger.error("Unable to addAll value: " + values, e); } } public Vector getContents() { Vector result = new Vector(); for (Iterator e = internalQueue.iterator(); e.hasNext();) result.add(e.next()); return result; } public int size() { return internalQueue.size(); } /** * returns the first object on the queue, without removing it. * If the queue is empty this object blocks until the first queue object has * been added * @return the first object on the queue */ public Object peek() { Object retval = null; try { retval = internalQueue.getFirst(); } catch (NoSuchElementException e) { } return retval; } public void reset() { try { disp.callRemoteMethods(null, "_reset", null, reset_signature, GroupRequest.GET_ALL, 0); } catch (Exception e) { logger.error("DistributedQueue.reset(" + groupname + ')', e); } } protected void checkResult(RspList rsp, Object retval) { if (logger.isDebugEnabled()) { logger.debug("Value updated from " + groupname + " :" + retval); } Vector results = rsp.getResults(); for (int i = 0; i < results.size(); i++) { Object data = results.elementAt(i); if (!data.equals(retval)) { logger.error("Reference value differs from returned value " + retval + " != " + data); } } } /** * Try to return the first objet in the queue.It does not wait for an object. * @return the first object in the queue or null if none were found. */ public Object remove() { Object retval = null; RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } return retval; } /** * @param timeout The time to wait until an entry is retrieved in milliseconds. A value of 0 means wait forever. * @return the first object in the queue or null if none were found */ public Object remove(long timeout) { Object retval = null; long start = System.currentTimeMillis(); if (timeout <= 0) { while (!stopped && (retval == null)) { RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } if (retval == null) { try { synchronized (mutex) { mutex.wait(); } } catch (InterruptedException e) { } } } } else { while (((System.currentTimeMillis() - start) < timeout) && !stopped && (retval == null)) { RspList rsp = disp.callRemoteMethods(null, "_remove", null, remove_signature, GroupRequest.GET_ALL, internal_timeout); Vector results = rsp.getResults(); if (results.size() > 0) { retval = results.elementAt(0); if (logger.isDebugEnabled()) { checkResult(rsp, retval); } } if (retval == null) { try { long delay = timeout - (System.currentTimeMillis() - start); synchronized (mutex) { if (delay > 0) { mutex.wait(delay); } } } catch (InterruptedException e) { } } } } return retval; } public String toString() { return internalQueue.toString(); } /*------------------------ Callbacks -----------------------*/ public void _add(Object value) { if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + " _add(" + value + ')'); } /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.add(value); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryAdd(value); } public void _addAtHead(Object value) { /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.addFirst(value); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryAdd(value); } public void _reset() { if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + " _reset()"); } _private_reset(); for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsCleared(); } protected void _private_reset() { /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.clear(); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } } public Object _remove() { Object retval = null; try { /*lock the queue from other threads*/ synchronized (mutex) { retval = internalQueue.removeFirst(); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + "_remove(" + retval + ')'); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryRemoved(retval); } catch (NoSuchElementException e) { logger.debug(groupname + '@' + getLocalAddress() + "_remove(): nothing to remove"); } return retval; } public void _addAll(Collection c) { if (logger.isDebugEnabled()) { logger.debug(groupname + '@' + getLocalAddress() + " _addAll(" + c + ')'); } /*lock the queue from other threads*/ synchronized (mutex) { internalQueue.addAll(c); /*wake up all the threads that are waiting for the lock to be released*/ mutex.notifyAll(); } for (int i = 0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsSet(c); } /*----------------------------------------------------------*/ /*-------------------- State Exchange ----------------------*/ public void receive(Message msg) { } public byte[] getState() { Vector copy = (Vector)getContents().clone(); try { return Util.objectToByteBuffer(copy); } catch (Throwable ex) { logger.error("DistributedQueue.getState(): exception marshalling state.", ex); return null; } } public void setState(byte[] new_state) { Vector new_copy; try { new_copy = (Vector)Util.objectFromByteBuffer(new_state); if (new_copy == null) { return; } } catch (Throwable ex) { logger.error("DistributedQueue.setState(): exception unmarshalling state.", ex); return; } _private_reset(); // remove all elements _addAll(new_copy); } /*------------------- Membership Changes ----------------------*/ public void viewAccepted(View new_view) { Vector new_mbrs = new_view.getMembers(); if (new_mbrs != null) { sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) members.removeAllElements(); for (int i = 0; i < new_mbrs.size(); i++) members.addElement(new_mbrs.elementAt(i)); } } /** Called when a member is suspected */ public void suspect(Address suspected_mbr) { ; } /** Block sending and receiving of messages until ViewAccepted is called */ public void block() { } void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { Vector joined; Vector left; Object mbr; Notification n; if ((notifs.size() == 0) || (old_mbrs == null) || (new_mbrs == null) || (old_mbrs.size() == 0) || (new_mbrs.size() == 0)) { return; } // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs joined = new Vector(); for (int i = 0; i < new_mbrs.size(); i++) { mbr = new_mbrs.elementAt(i); if (!old_mbrs.contains(mbr)) { joined.addElement(mbr); } } // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs left = new Vector(); for (int i = 0; i < old_mbrs.size(); i++) { mbr = old_mbrs.elementAt(i); if (!new_mbrs.contains(mbr)) { left.addElement(mbr); } } for (int i = 0; i < notifs.size(); i++) { n = (Notification)notifs.elementAt(i); n.viewChange(joined, left); } } final void initSignatures() { try { if (add_signature == null) { add_signature = new Class[] { Object.class }; } if (addAtHead_signature == null) { addAtHead_signature = new Class[] { Object.class }; } if (addAll_signature == null) { addAll_signature = new Class[] { Collection.class }; } if (reset_signature == null) { reset_signature = new Class[0]; } if (remove_signature == null) { remove_signature = new Class[0]; } } catch (Throwable ex) { logger.error("DistributedQueue.initMethods()", ex); } } public static void main(String[] args) { try { // The setup here is kind of weird: // 1. Create a channel // 2. Create a DistributedQueue (on the channel) // 3. Connect the channel (so the HT gets a VIEW_CHANGE) // 4. Start the HT // // A simpler setup is // DistributedQueue ht = new DistributedQueue("demo", null, // "file://c:/JGroups-2.0/conf/total-token.xml", 5000); JChannel c = new JChannel("file:/c:/JGroups-2.0/conf/conf/total-token.xml"); DistributedQueue ht = new DistributedQueue(c); c.connect("demo"); ht.start(5000); ht.add("name"); ht.add("Michelle Ban"); Object old_key = ht.remove(); System.out.println("old key was " + old_key); old_key = ht.remove(); System.out.println("old value was " + old_key); ht.add("name 'Michelle Ban'"); System.out.println("queue is " + ht); } catch (Throwable t) { t.printStackTrace(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/ReplicatedHashtable.java0000644000175000017500000004172411366547366027425 0ustar twernertwerner// $Id: ReplicatedHashtable.java,v 1.15.4.2 2008/02/28 07:31:56 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.util.Util; import java.io.Serializable; import java.util.*; /** * Provides the abstraction of a java.util.Hashtable that is replicated at several * locations. Any change to the hashtable (clear, put, remove etc) will transparently be * propagated to all replicas in the group. All read-only methods will always access the * local replica.

* Both keys and values added to the hashtable must be serializable, the reason * being that they will be sent across the network to all replicas of the group. Having said * this, it is now for example possible to add RMI remote objects to the hashtable as they * are derived from java.rmi.server.RemoteObject which in turn is serializable. * This allows to lookup shared distributed objects by their name and invoke methods on them, * regardless of one's onw location. A ReplicatedHashtable thus allows to * implement a distributed naming service in just a couple of lines.

* An instance of this class will contact an existing member of the group to fetch its * initial state.

* Contrary to DistributedHashtable, this class does not make use of RpcDispatcher (and RequestCorrelator) * but uses plain asynchronous messages instead. * @author Bela Ban * @author Alfonso Olias-Sanz * @deprecated Use {@link org.jgroups.blocks.ReplicatedHashMap} instead */ public class ReplicatedHashtable extends Hashtable implements MessageListener, MembershipListener { public interface Notification { void entrySet(Object key, Object value); void entryRemoved(Object key); void viewChange(Vector new_mbrs, Vector old_mbrs); void contentsSet(Map new_entries); } public interface StateTransferListener { void stateTransferStarted(); void stateTransferCompleted(boolean success); } transient Channel channel; transient PullPushAdapter adapter=null; final transient Vector notifs=new Vector(); // to be notified when mbrship changes final transient Vector members=new Vector(); // keeps track of all DHTs final transient List state_transfer_listeners=new ArrayList(); transient boolean state_transfer_running=false; /** Determines when the updates have to be sent across the network, avoids sending unnecessary * messages when there are no member in the group */ private transient boolean send_message=false; protected final transient Log log=LogFactory.getLog(this.getClass()); /** * Creates a ReplicatedHashtable * @param groupname The name of the group to join * @param factory The ChannelFactory which will be used to create a channel * @param properties The property string to be used to define the channel * @param state_timeout The time to wait until state is retrieved in milliseconds. A value of 0 means wait forever. */ public ReplicatedHashtable(String groupname, ChannelFactory factory, StateTransferListener l, String properties, long state_timeout) { if(l != null) addStateTransferListener(l); try { channel=factory != null ? factory.createChannel((Object)properties) : new JChannel(properties); channel.connect(groupname); adapter=new PullPushAdapter(channel, this, this); adapter.setListener(this); getInitState(channel, state_timeout); } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e); } } private void getInitState(Channel channel, long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { try { notifyStateTransferStarted(); boolean rc=channel.getState(null, state_timeout); if(rc) { if(log.isInfoEnabled()) log.info("state was retrieved successfully"); } else { if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); notifyStateTransferCompleted(false); } } catch(ChannelClosedException ex) { notifyStateTransferCompleted(false); throw ex; } catch(ChannelNotConnectedException ex2) { notifyStateTransferCompleted(false); throw ex2; } } public ReplicatedHashtable(String groupname, ChannelFactory factory, String properties, long state_timeout) { this(groupname, factory, null, properties, state_timeout); } public ReplicatedHashtable(JChannel channel, long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { this(channel, null, state_timeout); } public ReplicatedHashtable(JChannel channel, StateTransferListener l, long state_timeout) throws ChannelClosedException, ChannelNotConnectedException { this.channel=channel; this.adapter=new PullPushAdapter(channel, this, this); this.adapter.setListener(this); if(l != null) addStateTransferListener(l); getInitState(channel, state_timeout); // boolean rc=channel.getState(null, state_timeout); // if(rc) // if(log.isInfoEnabled()) log.info("state was retrieved successfully"); // else // if(log.isInfoEnabled()) log.info("state could not be retrieved (first member)"); } public boolean stateTransferRunning() { return state_transfer_running; } public Address getLocalAddress() { return channel != null ? channel.getLocalAddress() : null; } public Channel getChannel() { return channel; } public void addNotifier(Notification n) { if(!notifs.contains(n)) notifs.addElement(n); } public final void addStateTransferListener(StateTransferListener l) { if(l != null && !(state_transfer_listeners.contains(l))) state_transfer_listeners.add(l); } public void removeStateTransferListener(StateTransferListener l) { if(l != null) state_transfer_listeners.remove(l); } /** * Maps the specified key to the specified value in the hashtable. Neither of both parameters can be null * @param key - the hashtable key * @param value - the value * @return the previous value of the specified key in this hashtable, or null if it did not have one */ public Object put(Object key, Object value) { Message msg; Object prev_val=null; prev_val=get(key); //Changes done by //if true, send message to the group if(send_message == true) { try { msg=new Message(null, null, new Request(Request.PUT, key, value)); channel.send(msg); //return prev_val; } catch(Exception e) { //return null; } } else { super.put(key, value); //don't have to do prev_val = super.put(..) as is done at the beginning } return prev_val; } /** * Copies all of the mappings from the specified Map to this Hashtable These mappings will replace any mappings that this Hashtable had for any of the keys currently in the specified Map. * @param m - Mappings to be stored in this map */ public void putAll(Map m) { Message msg; //Changes done by //if true, send message to the group if(send_message == true) { try { msg=new Message(null, null, new Request(Request.PUT_ALL, null, m)); channel.send(msg); } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e); } } else { super.putAll(m); } } /** * Clears this hashtable so that it contains no keys */ public void clear() { Message msg; //Changes done by //if true, send message to the group if(send_message == true) { try { msg=new Message(null, null, new Request(Request.CLEAR, null, null)); channel.send(msg); } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e); } } else { super.clear(); } } /** * Removes the key (and its corresponding value) from the Hashtable. * @param key - the key to be removed. * @return the value to which the key had been mapped in this hashtable, or null if the key did not have a mapping. */ public Object remove(Object key) { Message msg; Object retval=null; retval=get(key); //Changes done by //if true, propagate action to the group if(send_message == true) { try { msg=new Message(null, null, new Request(Request.REMOVE, key, null)); channel.send(msg); //return retval; } catch(Exception e) { //return null; } } else { super.remove(key); //don't have to do retval = super.remove(..) as is done at the beginning } return retval; } /*------------------------ Callbacks -----------------------*/ Object _put(Object key, Object value) { Object retval=super.put(key, value); for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entrySet(key, value); return retval; } void _clear() { super.clear(); } Object _remove(Object key) { Object retval=super.remove(key); for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).entryRemoved(key); return retval; } /** * @see java.util.Map#putAll(java.util.Map) */ public void _putAll(Map m) { if(m == null) return; //######## The same way as in the DistributedHashtable // Calling the method below seems okay, but would result in ... deadlock ! // The reason is that Map.putAll() calls put(), which we override, which results in // lock contention for the map. // ---> super.putAll(m); <--- CULPRIT !!!@#$%$ // That said let's do it the stupid way: //######## The same way as in the DistributedHashtable Map.Entry entry; for(Iterator it=m.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); super.put(entry.getKey(), entry.getValue()); } for(int i=0; i < notifs.size(); i++) ((Notification)notifs.elementAt(i)).contentsSet(m); } /*----------------------------------------------------------*/ /*-------------------- MessageListener ----------------------*/ public void receive(Message msg) { Request req=null; if(msg == null) return; req=(Request)msg.getObject(); if(req == null) return; switch(req.req_type) { case Request.PUT: if(req.key != null && req.val != null) _put(req.key, req.val); break; case Request.REMOVE: if(req.key != null) _remove(req.key); break; case Request.CLEAR: _clear(); break; case Request.PUT_ALL: if(req.val != null) _putAll((Map)req.val); break; default : // error } } public byte[] getState() { Object key, val; Hashtable copy=new Hashtable(); for(Enumeration e=keys(); e.hasMoreElements();) { key=e.nextElement(); val=get(key); copy.put(key, val); } try { return Util.objectToByteBuffer(copy); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("exception marshalling state: " + ex); return null; } } public void setState(byte[] new_state) { Hashtable new_copy; Object key; try { new_copy=(Hashtable)Util.objectFromByteBuffer(new_state); if(new_copy == null) { notifyStateTransferCompleted(true); return; } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception unmarshalling state: " + ex); notifyStateTransferCompleted(false); return; } _clear(); // remove all elements for(Enumeration e=new_copy.keys(); e.hasMoreElements();) { key=e.nextElement(); _put(key, new_copy.get(key)); } notifyStateTransferCompleted(true); } /*-------------------- End of MessageListener ----------------------*/ /*----------------------- MembershipListener ------------------------*/ public void viewAccepted(View new_view) { Vector new_mbrs=new_view.getMembers(); if(new_mbrs != null) { sendViewChangeNotifications(new_mbrs, members); // notifies observers (joined, left) members.removeAllElements(); for(int i=0; i < new_mbrs.size(); i++) members.addElement(new_mbrs.elementAt(i)); } //if size is bigger than one, there are more peers in the group //otherwise there is only one server. if(members.size() > 1) { send_message=true; } else { send_message=false; } } /** Called when a member is suspected */ public void suspect(Address suspected_mbr) { ; } /** Block sending and receiving of messages until ViewAccepted is called */ public void block() { } /*------------------- End of MembershipListener ----------------------*/ void sendViewChangeNotifications(Vector new_mbrs, Vector old_mbrs) { Vector joined, left; Object mbr; Notification n; if(notifs.size() == 0 || old_mbrs == null || new_mbrs == null || old_mbrs.size() == 0 || new_mbrs.size() == 0) return; // 1. Compute set of members that joined: all that are in new_mbrs, but not in old_mbrs joined=new Vector(); for(int i=0; i < new_mbrs.size(); i++) { mbr=new_mbrs.elementAt(i); if(!old_mbrs.contains(mbr)) joined.addElement(mbr); } // 2. Compute set of members that left: all that were in old_mbrs, but not in new_mbrs left=new Vector(); for(int i=0; i < old_mbrs.size(); i++) { mbr=old_mbrs.elementAt(i); if(!new_mbrs.contains(mbr)) { left.addElement(mbr); } } for(int i=0; i < notifs.size(); i++) { n=(Notification)notifs.elementAt(i); n.viewChange(joined, left); } } void notifyStateTransferStarted() { state_transfer_running=true; for(Iterator it=state_transfer_listeners.iterator(); it.hasNext();) { StateTransferListener listener=(StateTransferListener)it.next(); try { listener.stateTransferStarted(); } catch(Throwable t) { } } } void notifyStateTransferCompleted(boolean success) { state_transfer_running=false; for(Iterator it=state_transfer_listeners.iterator(); it.hasNext();) { StateTransferListener listener=(StateTransferListener)it.next(); try { listener.stateTransferCompleted(success); } catch(Throwable t) { } } } private static class Request implements Serializable { static final int PUT=1; static final int REMOVE=2; static final int CLEAR=3; static final int PUT_ALL=4; int req_type=0; Object key=null; Object val=null; Request(int req_type, Object key, Object val) { this.req_type=req_type; this.key=key; this.val=val; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(type2String(req_type)); if(key != null) sb.append("\nkey=" + key); if(val != null) sb.append("\nval=" + val); return sb.toString(); } String type2String(int t) { switch(t) { case PUT: return "PUT"; case REMOVE: return "REMOVE"; case CLEAR: return "CLEAR"; case PUT_ALL: return "PUT_ALL"; default : return ""; } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/NBMessageForm_NIO.java0000644000175000017500000000362411366547366026667 0ustar twernertwerner// $Id: NBMessageForm_NIO.java,v 1.3 2005/06/30 15:38:43 belaban Exp $ package org.jgroups.blocks; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * NBMessageForm - Message form for non-blocking message reads. * @author akbollu * @author Bela Ban */ public class NBMessageForm_NIO { ByteBuffer headerBuffer = null; ByteBuffer dataBuffer = null; static final int HEADER_SIZE = 4; final boolean isComplete = false; int messageSize = 0; boolean w_in_p = false; SocketChannel channel = null; public NBMessageForm_NIO(int dataBuffSize, SocketChannel ch) { headerBuffer = ByteBuffer.allocate(HEADER_SIZE); dataBuffer = ByteBuffer.allocate(dataBuffSize); channel = ch; } public ByteBuffer readCompleteMsgBuffer() throws IOException { int rt; try { rt = channel.read(headerBuffer); if ( (rt == 0) || (rt == -1) ) { channel.close(); return null; } if (rt == HEADER_SIZE) { headerBuffer.flip(); messageSize = headerBuffer.getInt(); if(dataBuffer.capacity() < messageSize) { dataBuffer = ByteBuffer.allocate(messageSize); } } else { return null; } } catch(IOException ex) { channel.close(); throw ex; } //rt == 0 need not be checked twice in the same event channel.read(dataBuffer); if(isComplete()) { dataBuffer.flip(); return dataBuffer; } return null; } public void reset() { dataBuffer.clear(); headerBuffer.clear(); messageSize = 0; w_in_p = false; } private boolean isComplete() { return ( dataBuffer.position() == messageSize ); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/DistributedLockManager.java0000644000175000017500000007004211366547366030116 0ustar twernertwernerpackage org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.ChannelException; import org.jgroups.MembershipListener; import org.jgroups.View; import org.jgroups.Address; import org.jgroups.blocks.VotingAdapter.FailureVoteResult; import org.jgroups.blocks.VotingAdapter.VoteResult; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.io.Serializable; import java.util.*; /** * Distributed lock manager is responsible for maintaining the lock information * consistent on all participating nodes. * * @author Roman Rokytskyy (rrokytskyy@acm.org) * @author Robert Schaffar-Taurok (robert@fusion.at) * @version $Id: DistributedLockManager.java,v 1.9.4.2 2008/05/22 09:51:25 belaban Exp $ */ public class DistributedLockManager implements TwoPhaseVotingListener, LockManager, VoteResponseProcessor, MembershipListener { /** * Definitions for the implementation of the VoteResponseProcessor */ private static final int PROCESS_CONTINUE = 0; private static final int PROCESS_SKIP = 1; private static final int PROCESS_BREAK = 2; /** * This parameter means that lock acquisition expires after 5 seconds. * If there were no "commit" operation on prepared lock, then it * is treated as expired and is removed from the prepared locks table. */ private static final long ACQUIRE_EXPIRATION = 5000; /** * This parameter is used during lock releasing. If group fails to release * the lock during the specified period of time, unlocking fails. */ private static final long VOTE_TIMEOUT = 10000; /** HashMap. List of all prepared locks */ private final HashMap preparedLocks = new HashMap(); /** HashMap. List of all prepared releases */ private final HashMap preparedReleases = new HashMap(); /* HashMap. List of locks on the node */ private final HashMap heldLocks = new HashMap(); private final TwoPhaseVotingAdapter votingAdapter; private final Object id; final Vector current_members=new Vector(); protected final Log log=LogFactory.getLog(getClass()); /** * Create instance of this class. * * @param voteChannel instance of {@link VotingAdapter} that will be used * for voting purposes on the lock decrees. voteChannel() will * be wrapped by the instance of the {@link TwoPhaseVotingAdapter}. * * @param id the unique identifier of this lock manager. * * todo check if the node with the same id is already in the group. */ public DistributedLockManager(VotingAdapter voteChannel, Object id) { this(new TwoPhaseVotingAdapter(voteChannel), id); } /** * Constructor for the DistributedLockManager_cl object. * * @param channel instance of {@link TwoPhaseVotingAdapter} * that will be used for voting purposes on the lock decrees. * * @param id the unique identifier of this lock manager. * * @todo check if the node with the same id is already in the group. */ public DistributedLockManager(TwoPhaseVotingAdapter channel, Object id) { this.id = id; this.votingAdapter = channel; this.votingAdapter.addListener(this); if(votingAdapter.getVoteChannel() != null) { votingAdapter.getVoteChannel().addMembershipListener(this); setInitialMembership(votingAdapter.getVoteChannel().getMembers()); } } private void setInitialMembership(Collection members) { if(members != null) { current_members.clear(); current_members.addAll(members); } } /** * Performs local lock. This method also performs the clean-up of the lock * table, all expired locks are removed. */ private boolean localLock(LockDecree lockDecree) { // remove expired locks removeExpired(lockDecree); LockDecree localLock = (LockDecree) heldLocks.get(lockDecree.getKey()); if (localLock == null) { // promote lock into commited state lockDecree.commit(); // no lock exist, perform local lock, note: // we do not store locks that were requested by other manager. if (lockDecree.managerId.equals(id)) heldLocks.put(lockDecree.getKey(), lockDecree); // everything is fine :) return true; } else return localLock.requester.equals(lockDecree.requester); } /** * Returns true if the requested lock can be granted by the * current node. * * @param decree instance of LockDecree containing information * about the lock. */ private boolean canLock(LockDecree decree) { // clean expired locks removeExpired(decree); LockDecree lock = (LockDecree)heldLocks.get(decree.getKey()); return lock == null || lock.requester.equals(decree.requester); } /** * Returns true if the requested lock can be released by the * current node. * * @param decree instance of {@link LockDecree} containing information * about the lock. */ private boolean canRelease(LockDecree decree) { // clean expired locks removeExpired(decree); // we need to check only hold locks, because // prepared locks cannot contain the lock LockDecree lock = (LockDecree)heldLocks.get(decree.getKey()); return lock == null || lock.requester.equals(decree.requester); } /** * Removes expired locks. * * @param decree instance of {@link LockDecree} describing the lock. */ private void removeExpired(LockDecree decree) { // remove the invalid (expired) lock LockDecree localLock = (LockDecree)heldLocks.get(decree.getKey()); if (localLock != null && !localLock.isValid()) heldLocks.remove(localLock.getKey()); } /** * Releases lock locally. * * @param lockDecree instance of {@link LockDecree} describing the lock. */ private boolean localRelease(LockDecree lockDecree) { // remove expired locks removeExpired(lockDecree); LockDecree localLock= (LockDecree) heldLocks.get(lockDecree.getKey()); if(localLock == null) { // no lock exist return true; } else if(localLock.requester.equals(lockDecree.requester)) { // requester owns the lock, release the lock heldLocks.remove(lockDecree.getKey()); return true; } else // lock does not belong to requester return false; } /** * Locks an object with lockId on behalf of the specified * owner. * * @param lockId Object representing the object to be locked. * @param owner object that requests the lock. This should be the Address of a JGroups member, otherwise we cannot * release the locks for a crashed member ! * @param timeout time during which group members should decide * whether to grant a lock or not. * * @throws LockNotGrantedException when the lock cannot be granted. * * @throws ClassCastException if lockId or owner are not serializable. * * @throws ChannelException if something bad happened to underlying channel. */ public void lock(Object lockId, Object owner, int timeout) throws LockNotGrantedException, ChannelException { if (!(lockId instanceof Serializable) || !(owner instanceof Serializable)) throw new ClassCastException("DistributedLockManager works only with serializable objects."); boolean acquired = votingAdapter.vote( new AcquireLockDecree(lockId, owner, id), timeout); if (!acquired) throw new LockNotGrantedException("Lock " + lockId + " cannot be granted."); } /** * Unlocks an object with lockId on behalf of the specified * owner. * * since 2.2.9 this method is only a wrapper for * unlock(Object lockId, Object owner, boolean releaseMultiLocked). * Use that with releaseMultiLocked set to true if you want to be able to * release multiple locked locks (for example after a merge) * * @param lockId long representing the object to be unlocked. * @param owner object that releases the lock. * * @throws LockNotReleasedException when the lock cannot be released. * @throws ClassCastException if lockId or owner are not serializable. * */ public void unlock(Object lockId, Object owner) throws LockNotReleasedException, ChannelException { try { unlock(lockId, owner, false, VOTE_TIMEOUT); } catch (LockMultiLockedException e) { // This should never happen when releaseMultiLocked is false log.error("Caught MultiLockedException but releaseMultiLocked is false", e); } } public void unlock(Object lockId, Object owner, long timeout) throws LockNotReleasedException, ChannelException { try { unlock(lockId, owner, false, timeout); } catch (LockMultiLockedException e) { // This should never happen when releaseMultiLocked is false log.error("Caught MultiLockedException but releaseMultiLocked is false", e); } } /** * Unlocks an object with lockId on behalf of the specified * owner. * @param lockId long representing the object to be unlocked. * @param owner object that releases the lock. * @param releaseMultiLocked releases also multiple locked locks. (eg. locks that are locked by another DLM after a merge) * * @throws LockNotReleasedException when the lock cannot be released. * @throws ClassCastException if lockId or owner are not serializable. * @throws LockMultiLockedException if releaseMultiLocked is true and a multiple locked lock has been released. */ public void unlock(Object lockId, Object owner, boolean releaseMultiLocked) throws LockNotReleasedException, ChannelException, LockMultiLockedException { unlock(lockId, owner, releaseMultiLocked, VOTE_TIMEOUT); } public void unlock(Object lockId, Object owner, boolean releaseMultiLocked, long timeout) throws LockNotReleasedException, ChannelException, LockMultiLockedException { if (!(lockId instanceof Serializable) || !(owner instanceof Serializable)) throw new ClassCastException("DistributedLockManager " + "works only with serializable objects."); ReleaseLockDecree releaseLockDecree = new ReleaseLockDecree(lockId, owner, id); boolean released = false; if (releaseMultiLocked) { released = votingAdapter.vote(releaseLockDecree, timeout, this); if (releaseLockDecree.isMultipleLocked()) { throw new LockMultiLockedException("Lock was also locked by other DistributedLockManager(s)"); } } else { released = votingAdapter.vote(releaseLockDecree, timeout); } if (!released) throw new LockNotReleasedException("Lock cannot be unlocked."); } /** * Checks the list of prepared locks/unlocks to determine if we are in the * middle of the two-phase commit process for the lock acqusition/release. * Here we do not tolerate if the request comes from the same node on behalf * of the same owner. * * @param preparedContainer either preparedLocks or * preparedReleases depending on the situation. * * @param requestedDecree instance of LockDecree representing * the lock. */ private static boolean checkPrepared(HashMap preparedContainer, LockDecree requestedDecree) { LockDecree preparedDecree = (LockDecree)preparedContainer.get(requestedDecree.getKey()); // if prepared lock is not valid, remove it from the list if ((preparedDecree != null) && !preparedDecree.isValid()) { preparedContainer.remove(preparedDecree.getKey()); preparedDecree = null; } return preparedDecree == null || requestedDecree.requester.equals(preparedDecree.requester); } /** * Prepare phase for the lock acquisition or release. * * @param decree should be an instance LockDecree, if not, * we throw VoteException to be ignored by the * VoteChannel. * * @return true when preparing the lock operation succeeds. * * @throws VoteException if we should be ignored during voting. */ public synchronized boolean prepare(Object decree) throws VoteException { if (!(decree instanceof LockDecree)) throw new VoteException("Uknown decree type. Ignore me."); if (decree instanceof AcquireLockDecree) { AcquireLockDecree acquireDecree = (AcquireLockDecree)decree; if(log.isDebugEnabled()) log.debug("Preparing to acquire decree " + acquireDecree.lockId); if (!checkPrepared(preparedLocks, acquireDecree)) // there is a prepared lock owned by third party return false; if (canLock(acquireDecree)) { preparedLocks.put(acquireDecree.getKey(), acquireDecree); return true; } else // we are unable to aquire local lock return false; } else if (decree instanceof ReleaseLockDecree) { ReleaseLockDecree releaseDecree = (ReleaseLockDecree)decree; if(log.isDebugEnabled()) log.debug("Preparing to release decree " + releaseDecree.lockId); if (!checkPrepared(preparedReleases, releaseDecree)) // there is a prepared release owned by third party return false; if (canRelease(releaseDecree)) { preparedReleases.put(releaseDecree.getKey(), releaseDecree); // we have local lock and the prepared lock return true; } else // we were unable to aquire local lock return false; } else if (decree instanceof MultiLockDecree) { // Here we abuse the voting mechanism for notifying the other lockManagers of multiple locked objects. MultiLockDecree multiLockDecree = (MultiLockDecree)decree; if(log.isDebugEnabled()) { log.debug("Marking " + multiLockDecree.getKey() + " as multilocked"); } LockDecree lockDecree = (LockDecree)heldLocks.get(multiLockDecree.getKey()); if (lockDecree != null) { lockDecree.setMultipleLocked(true); } return true; } // we should not be here return false; } /** * Commit phase for the lock acquisition or release. * * @param decree should be an instance LockDecree, if not, * we throw VoteException to be ignored by the * VoteChannel. * * @return true when commiting the lock operation succeeds. * * @throws VoteException if we should be ignored during voting. */ public synchronized boolean commit(Object decree) throws VoteException { if (!(decree instanceof LockDecree)) throw new VoteException("Uknown decree type. Ignore me."); if (decree instanceof AcquireLockDecree) { if(log.isDebugEnabled()) log.debug("Committing decree acquisition " + ((LockDecree)decree).lockId); if (!checkPrepared(preparedLocks, (LockDecree)decree)) // there is a prepared lock owned by third party return false; if (localLock((LockDecree)decree)) { preparedLocks.remove(((LockDecree)decree).getKey()); return true; } else return false; } else if (decree instanceof ReleaseLockDecree) { if(log.isDebugEnabled()) log.debug("Committing decree release " + ((LockDecree)decree).lockId); if (!checkPrepared(preparedReleases, (LockDecree)decree)) // there is a prepared release owned by third party return false; if (localRelease((LockDecree)decree)) { preparedReleases.remove(((LockDecree)decree).getKey()); return true; } else return false; } else if (decree instanceof MultiLockDecree) { return true; } // we should not be here return false; } /** * Abort phase for the lock acquisition or release. * * @param decree should be an instance LockDecree, if not, * we throw VoteException to be ignored by the * VoteChannel. * * @throws VoteException if we should be ignored during voting. */ public synchronized void abort(Object decree) throws VoteException { if (!(decree instanceof LockDecree)) throw new VoteException("Uknown decree type. Ignore me."); if (decree instanceof AcquireLockDecree) { if(log.isDebugEnabled()) log.debug("Aborting decree acquisition " + ((LockDecree)decree).lockId); if (!checkPrepared(preparedLocks, (LockDecree)decree)) // there is a prepared lock owned by third party return; preparedLocks.remove(((LockDecree)decree).getKey()); } else if (decree instanceof ReleaseLockDecree) { if(log.isDebugEnabled()) log.debug("Aborting decree release " + ((LockDecree)decree).lockId); if (!checkPrepared(preparedReleases, (LockDecree)decree)) // there is a prepared release owned by third party return; preparedReleases.remove(((LockDecree)decree).getKey()); } } /** * Processes the response list and votes like the default processResponses method with the consensusType VOTE_ALL * If the result of the voting is false, but this DistributedLockManager owns the lock, the result is changed to * true and the lock is released, but marked as multiple locked. (only in the prepare state to reduce traffic) *

* Note: we do not support voting in case of Byzantine failures, i.e. * when the node responds with the fault message. */ public boolean processResponses(RspList responses, int consensusType, Object decree) throws ChannelException { if (responses == null) { return false; } int totalPositiveVotes = 0; int totalNegativeVotes = 0; for (int i = 0; i < responses.size(); i++) { Rsp response = (Rsp) responses.elementAt(i); switch (checkResponse(response)) { case PROCESS_SKIP: continue; case PROCESS_BREAK: return false; } VoteResult result = (VoteResult) response.getValue(); totalPositiveVotes += result.getPositiveVotes(); totalNegativeVotes += result.getNegativeVotes(); } boolean voteResult = (totalNegativeVotes == 0 && totalPositiveVotes > 0); if (decree instanceof TwoPhaseVotingAdapter.TwoPhaseWrapper) { TwoPhaseVotingAdapter.TwoPhaseWrapper wrappedDecree = (TwoPhaseVotingAdapter.TwoPhaseWrapper)decree; if (wrappedDecree.isPrepare()) { Object unwrappedDecree = wrappedDecree.getDecree(); if (unwrappedDecree instanceof ReleaseLockDecree) { ReleaseLockDecree releaseLockDecree = (ReleaseLockDecree)unwrappedDecree; LockDecree lock = null; if ((lock = (LockDecree)heldLocks.get(releaseLockDecree.getKey())) != null) { // If there is a local lock... if (!voteResult) { // ... and another DLM voted negatively, but this DLM owns the lock // we inform the other node, that it's lock is multiple locked if (informLockingNodes(releaseLockDecree)) { // we set the local lock to multiple locked lock.setMultipleLocked(true); voteResult = true; } } if (lock.isMultipleLocked()) { //... and the local lock is marked as multilocked // we mark the releaseLockDecree als multiple locked for evaluation when unlock returns releaseLockDecree.setMultipleLocked(true); } } } } } return voteResult; } /** * This method checks the response and says the processResponses() method * what to do. * @return PROCESS_CONTINUE to continue calculating votes, * PROCESS_BREAK to stop calculating votes from the nodes, * PROCESS_SKIP to skip current response. * @throws ChannelException when the response is fatal to the * current voting process. */ private int checkResponse(Rsp response) throws ChannelException { if (!response.wasReceived()) { if (log.isDebugEnabled()) log.debug("Response from node " + response.getSender() + " was not received."); throw new ChannelException("Node " + response.getSender() + " failed to respond."); } if (response.wasSuspected()) { if (log.isDebugEnabled()) log.debug("Node " + response.getSender() + " was suspected."); return PROCESS_SKIP; } Object object = response.getValue(); // we received exception/error, something went wrong // on one of the nodes... and we do not handle such faults if (object instanceof Throwable) { throw new ChannelException("Node " + response.getSender() + " is faulty."); } if (object == null) { return PROCESS_SKIP; } // it is always interesting to know the class that caused failure... if (!(object instanceof VoteResult)) { String faultClass = object.getClass().getName(); // ...but we do not handle byzantine faults throw new ChannelException("Node " + response.getSender() + " generated fault (class " + faultClass + ')'); } // what if we received the response from faulty node? if (object instanceof FailureVoteResult) { if (log.isErrorEnabled()) log.error(((FailureVoteResult) object).getReason()); return PROCESS_BREAK; } // everything is fine :) return PROCESS_CONTINUE; } private boolean informLockingNodes(ReleaseLockDecree releaseLockDecree) throws ChannelException { return votingAdapter.vote(new MultiLockDecree(releaseLockDecree), VOTE_TIMEOUT); } /** Remove all locks held by members who left the previous view */ public void viewAccepted(View new_view) { Vector prev_view=new Vector(current_members); current_members.clear(); current_members.addAll(new_view.getMembers()); if(log.isDebugEnabled()) log.debug("-- VIEW: " + current_members + ", old view: " + prev_view); prev_view.removeAll(current_members); if(!prev_view.isEmpty()) { // we have left members, so we need to check for locks which are still held by them for(Iterator it=prev_view.iterator(); it.hasNext();) { Object mbr=it.next(); removeLocksHeldBy(preparedLocks, mbr); removeLocksHeldBy(preparedReleases, mbr); removeLocksHeldBy(heldLocks, mbr); } } } /** Remove from preparedLocks, preparedReleases and heldLocks */ private void removeLocksHeldBy(Map lock_table, Object mbr) { Map.Entry entry; LockDecree val; Object holder; for(Iterator it=lock_table.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); val=(LockDecree)entry.getValue(); holder=val.requester; if(holder != null && holder.equals(mbr)) { if(log.isTraceEnabled()) log.trace("removing a leftover lock held by " + mbr + " for " + entry.getKey() + ": " + val); it.remove(); } } } public void suspect(Address suspected_mbr) { } public void block() { } /** * This class represents the lock */ public static class LockDecree implements Serializable { protected final Object lockId; protected final Object requester; protected final Object managerId; protected boolean commited; private boolean multipleLocked = false; private static final long serialVersionUID = 7264104838035219212L; private LockDecree(Object lockId, Object requester, Object managerId) { this.lockId = lockId; this.requester = requester; this.managerId = managerId; } /** * Returns the key that should be used for Map lookup. */ public Object getKey() { return lockId; } /** * This is a place-holder for future lock expiration code. */ public boolean isValid() { return true; } public void commit() { this.commited = true; } /** * @return Returns the multipleLocked. */ public boolean isMultipleLocked() { return multipleLocked; } /** * @param multipleLocked The multipleLocked to set. */ public void setMultipleLocked(boolean multipleLocked) { this.multipleLocked = multipleLocked; } /** * This is hashcode from the java.lang.Long class. */ public int hashCode() { return lockId.hashCode(); } public boolean equals(Object other) { return other instanceof LockDecree && ((LockDecree)other).lockId.equals(this.lockId); } } /** * This class represents the lock to be released. */ public static class AcquireLockDecree extends LockDecree { private final long creationTime; private AcquireLockDecree(Object lockId, Object requester, Object managerId) { super(lockId, requester, managerId); this.creationTime = System.currentTimeMillis(); } /** * Lock aquire decree is valid for a ACQUIRE_EXPIRATION * time after creation and if the lock is still valid (in the * future locks will be leased for a predefined period of time). */ public boolean isValid() { boolean result = super.isValid(); if (!commited && result) result = ((creationTime + ACQUIRE_EXPIRATION) > System.currentTimeMillis()); return result; } } /** * This class represents the lock to be released. */ public static class ReleaseLockDecree extends LockDecree { ReleaseLockDecree(Object lockId, Object requester, Object managerId) { super(lockId, requester, managerId); } } /** * This class represents the lock that has to be marked as multilocked */ public static class MultiLockDecree extends LockDecree { MultiLockDecree(Object lockId, Object requester, Object managerId) { super(lockId, requester, managerId); } MultiLockDecree(ReleaseLockDecree releaseLockDecree) { super(releaseLockDecree.lockId, releaseLockDecree.requester, releaseLockDecree.managerId); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/GroupRequest.java0000644000175000017500000005604611366547366026205 0ustar twernertwernerpackage org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.Transport; import org.jgroups.View; import org.jgroups.annotations.GuardedBy; import org.jgroups.util.Command; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Sends a message to all members of the group and waits for all responses (or timeout). Returns a * boolean value (success or failure). Results (if any) can be retrieved when done.

* The supported transport to send requests is currently either a RequestCorrelator or a generic * Transport. One of them has to be given in the constructor. It will then be used to send a * request. When a message is received by either one, the receiveResponse() of this class has to * be called (this class does not actively receive requests/responses itself). Also, when a view change * or suspicion is received, the methods viewChange() or suspect() of this class have to be called.

* When started, an array of responses, correlating to the membership, is created. Each response * is added to the corresponding field in the array. When all fields have been set, the algorithm * terminates. * This algorithm can optionally use a suspicion service (failure detector) to detect (and * exclude from the membership) fauly members. If no suspicion service is available, timeouts * can be used instead (see execute()). When done, a list of suspected members * can be retrieved.

* Because a channel might deliver requests, and responses to different requests, the * GroupRequest class cannot itself receive and process requests/responses from the * channel. A mechanism outside this class has to do this; it has to determine what the responses * are for the message sent by the execute() method and call receiveResponse() * to do so.

* Requirements: lossless delivery, e.g. acknowledgment-based message confirmation. * @author Bela Ban * @version $Id: GroupRequest.java,v 1.30.2.4 2009/06/28 16:12:06 belaban Exp $ */ public class GroupRequest implements RspCollector, Command { /** return only first response */ public static final int GET_FIRST=1; /** return all responses */ public static final int GET_ALL=2; /** return majority (of all non-faulty members) */ public static final int GET_MAJORITY=3; /** return majority (of all members, may block) */ public static final int GET_ABS_MAJORITY=4; /** return n responses (may block) */ public static final int GET_N=5; /** return no response (async call) */ public static final int GET_NONE=6; private Address caller; private final Lock lock=new ReentrantLock(); /** Is set as soon as the request has received all required responses */ private final Condition completed=lock.newCondition(); /** Map. Maps requests and responses */ @GuardedBy("lock") private final Map requests=new HashMap(); /** bounded queue of suspected members */ @GuardedBy("lock") private final List

suspects=new ArrayList
(); /** list of members, changed by viewChange() */ @GuardedBy("lock") private final Collection
members=new TreeSet
(); /** keep suspects vector bounded */ private static final int max_suspects=40; final protected Message request_msg; final protected RequestCorrelator corr; // either use RequestCorrelator or ... protected Transport transport; // Transport (one of them has to be non-null) protected RspFilter rsp_filter=null; protected int rsp_mode=GET_ALL; protected boolean done=false; protected long timeout=0; protected int expected_mbrs=0; private static final Log log=LogFactory.getLog(GroupRequest.class); /** to generate unique request IDs (see getRequestId()) */ private static long last_req_id=1; private long req_id=-1; // request ID for this request /** @param m The message to be sent @param corr The request correlator to be used. A request correlator sends requests tagged with a unique ID and notifies the sender when matching responses are received. The reason GroupRequest uses it instead of a Transport is that multiple requests/responses might be sent/received concurrently. @param members The initial membership. This value reflects the membership to which the request is sent (and from which potential responses are expected). Is reset by reset(). @param rsp_mode How many responses are expected. Can be
  1. GET_ALL: wait for all responses from non-suspected members. A suspicion service might warn us when a member from which a response is outstanding has crashed, so it can be excluded from the responses. If no suspision service is available, a timeout can be used (a value of 0 means wait forever). If a timeout of 0 is used, no suspicion service is available and a member from which we expect a response has crashed, this methods blocks forever !.
  2. GET_FIRST: wait for the first available response.
  3. GET_MAJORITY: wait for the majority of all responses. The majority is re-computed when a member is suspected.
  4. GET_ABS_MAJORITY: wait for the majority of all members. This includes failed members, so it may block if no timeout is specified.
  5. GET_N: wait for N members. Return if n is >= membership+suspects.
  6. GET_NONE: don't wait for any response. Essentially send an asynchronous message to the group members.
*/ public GroupRequest(Message m, RequestCorrelator corr, Vector
members, int rsp_mode) { request_msg=m; this.corr=corr; this.rsp_mode=rsp_mode; reset(members); // suspects.removeAllElements(); // bela Aug 23 2002: made suspects bounded } /** @param timeout Time to wait for responses (ms). A value of <= 0 means wait indefinitely (e.g. if a suspicion service is available; timeouts are not needed). */ public GroupRequest(Message m, RequestCorrelator corr, Vector
members, int rsp_mode, long timeout, int expected_mbrs) { this(m, corr, members, rsp_mode); if(timeout > 0) this.timeout=timeout; this.expected_mbrs=expected_mbrs; } public GroupRequest(Message m, Transport transport, Vector
members, int rsp_mode) { corr = null; request_msg=m; this.transport=transport; this.rsp_mode=rsp_mode; reset(members); // suspects.removeAllElements(); // bela Aug 23 2002: make suspects bounded } /** * @param timeout Time to wait for responses (ms). A value of <= 0 means wait indefinitely * (e.g. if a suspicion service is available; timeouts are not needed). */ public GroupRequest(Message m, Transport transport, Vector
members, int rsp_mode, long timeout, int expected_mbrs) { this(m, transport, members, rsp_mode); if(timeout > 0) this.timeout=timeout; this.expected_mbrs=expected_mbrs; } public Address getCaller() { return caller; } public void setCaller(Address caller) { this.caller=caller; } public void setResponseFilter(RspFilter filter) { rsp_filter=filter; } public boolean execute() throws Exception { return execute(false); } /** * Sends the message. Returns when n responses have been received, or a * timeout has occurred. n can be the first response, all * responses, or a majority of the responses. */ public boolean execute(boolean use_anycasting) throws Exception { if(corr == null && transport == null) { if(log.isErrorEnabled()) log.error("both corr and transport are null, cannot send group request"); return false; } Vector
targets=null; lock.lock(); try { targets=new Vector
(members); req_id=getRequestId(); // reset(null); // clear 'responses' array for(Address suspect: suspects) { // mark all suspects in 'received' array Rsp rsp=requests.get(suspect); if(rsp != null) { rsp.setSuspected(true); break; // we can break here because we ensure there are no duplicate members } } } finally { lock.unlock(); } sendRequest(targets, req_id, use_anycasting); lock.lock(); try { done=false; boolean retval=collectResponses(timeout); if(retval == false && log.isTraceEnabled()) log.trace("call did not execute correctly, request is " + this.toString()); return retval; } finally { done=true; lock.unlock(); } } /** * This method sets the membership variable to the value of * members. It requires that the caller already hold the * rsp_mutex lock. * @param mbrs The new list of members */ public final void reset(Vector
mbrs) { lock.lock(); try { if(mbrs != null) { requests.clear(); for(Address mbr: mbrs) { requests.put(mbr, new Rsp(mbr)); } // maintain local membership this.members.clear(); this.members.addAll(mbrs); } else { for(Rsp rsp: requests.values()) { rsp.setReceived(false); rsp.setValue(null); } } } finally { lock.unlock(); } } /* ---------------------- Interface RspCollector -------------------------- */ /** * Callback (called by RequestCorrelator or Transport). * Adds a response to the response table. When all responses have been received, * execute() returns. */ public void receiveResponse(Object response_value, Address sender) { lock.lock(); try { if(done) { if(log.isTraceEnabled()) log.trace("command is done; cannot add response !"); return; } if(suspects.contains(sender)) { if(log.isWarnEnabled()) log.warn("received response from suspected member " + sender + "; discarding"); return; } if(rsp_filter != null && !rsp_filter.isAcceptable(response_value, sender)) { if(!rsp_filter.needMoreResponses()) { done=true; completed.signalAll(); // we're done as we don't need more responses } return; } Rsp rsp=requests.get(sender); if(rsp != null) { if(rsp.wasReceived() == false) { rsp.setValue(response_value); rsp.setReceived(true); if(log.isTraceEnabled()) log.trace(new StringBuilder("received response for request ").append(req_id).append(", sender="). append(sender).append(", val=").append(response_value)); if(rsp_filter != null && !rsp_filter.needMoreResponses()) done=true; completed.signalAll(); // wakes up execute() } } } finally { lock.unlock(); } } /** * Callback (called by RequestCorrelator or Transport). * Report to GroupRequest that a member is reported as faulty (suspected). * This method would probably be called when getting a suspect message from a failure detector * (where available). It is used to exclude faulty members from the response list. */ public void suspect(Address suspected_member) { if(suspected_member == null) return; lock.lock(); try { addSuspect(suspected_member); Rsp rsp=requests.get(suspected_member); if(rsp != null) { rsp.setSuspected(true); rsp.setValue(null); completed.signalAll(); } } finally { lock.unlock(); } } /** * Any member of 'membership' that is not in the new view is flagged as * SUSPECTED. Any member in the new view that is not in the * membership (ie, the set of responses expected for the current RPC) will * not be added to it. If we did this we might run into the * following problem: *
    *
  • Membership is {A,B} *
  • A sends a synchronous group RPC (which sleeps for 60 secs in the * invocation handler) *
  • C joins while A waits for responses from A and B *
  • If this would generate a new view {A,B,C} and if this expanded the * response set to {A,B,C}, A would wait forever on C's response because C * never received the request in the first place, therefore won't send a * response. *
*/ public void viewChange(View new_view) { Address mbr; Vector
mbrs=new_view != null? new_view.getMembers() : null; lock.lock(); try { if(requests == null || requests.isEmpty() || mbrs == null) return; this.members.clear(); this.members.addAll(mbrs); Rsp rsp; Set
tmp=null; for(Map.Entry entry: requests.entrySet()) { mbr=entry.getKey(); if(!mbrs.contains(mbr)) { if(tmp == null) tmp=new HashSet
(); tmp.add(mbr); addSuspect(mbr); rsp=entry.getValue(); rsp.setValue(null); rsp.setSuspected(true); } } if(tmp != null) { for(Address suspect: tmp) { addSuspect(suspect); } completed.signalAll(); } } finally { lock.unlock(); } } /* -------------------- End of Interface RspCollector ----------------------------------- */ /** Returns the results as a RspList */ public RspList getResults() { lock.lock(); try { Collection rsps=requests.values(); return new RspList(rsps); } finally { lock.unlock(); } } public String toString() { StringBuilder ret=new StringBuilder(128); ret.append("[req_id=").append(req_id).append('\n'); if(caller != null) ret.append("caller=").append(caller).append("\n"); Address mbr; Rsp rsp; lock.lock(); try { if(!requests.isEmpty()) { ret.append("entries:\n"); for(Map.Entry entry: requests.entrySet()) { mbr=entry.getKey(); rsp=entry.getValue(); ret.append(mbr).append(": ").append(rsp).append("\n"); } } } finally { lock.unlock(); } return ret.toString(); } public int getNumSuspects() { return suspects.size(); } /** Returns the list of suspected members. An attempt to modify the return value will throw an excxeption */ public Vector
getSuspects() { return new Vector
(suspects); } public boolean isDone() { return done; } /* --------------------------------- Private Methods -------------------------------------*/ private static int determineMajority(int i) { return i < 2? i : (i / 2) + 1; } /** Generates a new unique request ID */ private static synchronized long getRequestId() { long result=System.currentTimeMillis(); if(result <= last_req_id) { result=last_req_id + 1; } last_req_id=result; return result; } /** This method runs with lock locked (called by execute()). */ @GuardedBy("lock") private boolean collectResponses(long timeout) throws Exception { if(timeout <= 0) { while(true) { /* Wait for responses: */ adjustMembership(); // may not be necessary, just to make sure... if(responsesComplete()) { if(corr != null) { corr.done(req_id); } if(log.isTraceEnabled() && rsp_mode != GET_NONE) { log.trace("received all responses: " + toString()); } return true; } completed.await(); } } else { long start_time=System.currentTimeMillis(); long timeout_time=start_time + timeout; while(timeout > 0) { /* Wait for responses: */ if(responsesComplete()) { if(corr != null) corr.done(req_id); if(log.isTraceEnabled() && rsp_mode != GET_NONE) { log.trace("received all responses: " + toString()); } return true; } timeout=timeout_time - System.currentTimeMillis(); if(timeout > 0) { completed.await(timeout, TimeUnit.MILLISECONDS); } } if(corr != null) { corr.done(req_id); } if(log.isTraceEnabled()) log.trace("timed out waiting for responses"); return false; } } private void sendRequest(Vector
targetMembers, long requestId,boolean use_anycasting) throws Exception { try { if(log.isTraceEnabled()) log.trace(new StringBuilder("sending request (id=").append(req_id).append(')')); if(corr != null) { corr.sendRequest(requestId, targetMembers, request_msg, rsp_mode == GET_NONE? null : this, use_anycasting); } else { if(use_anycasting) { for(Address mbr: targetMembers) { Message copy=request_msg.copy(true); copy.setDest(mbr); transport.send(copy); } } else { transport.send(request_msg); } } } catch(Exception ex) { if(corr != null) corr.done(requestId); throw ex; } } @GuardedBy("lock") private boolean responsesComplete() { int num_received=0, num_not_received=0, num_suspected=0; final int num_total=requests.size(); if(done) return true; for(Rsp rsp: requests.values()) { if(rsp.wasReceived()) { num_received++; } else { if(rsp.wasSuspected()) { num_suspected++; } else { num_not_received++; } } } switch(rsp_mode) { case GET_FIRST: if(num_received > 0) return true; if(num_suspected >= num_total) // e.g. 2 members, and both suspected return true; break; case GET_ALL: return num_received + num_suspected >= num_total; case GET_MAJORITY: int majority=determineMajority(num_total); if(num_received + num_suspected >= majority) return true; break; case GET_ABS_MAJORITY: majority=determineMajority(num_total); if(num_received >= majority) return true; break; case GET_N: if(expected_mbrs >= num_total) { rsp_mode=GET_ALL; return responsesComplete(); } return num_received >= expected_mbrs || num_received + num_not_received < expected_mbrs && num_received + num_suspected >= expected_mbrs; case GET_NONE: return true; default : if(log.isErrorEnabled()) log.error("rsp_mode " + rsp_mode + " unknown !"); break; } return false; } /** * Adjusts the 'received' array in the following way: *
    *
  • if a member P in 'membership' is not in 'members', P's entry in the 'received' array * will be marked as SUSPECTED *
  • if P is 'suspected_mbr', then P's entry in the 'received' array will be marked * as SUSPECTED *
* This call requires exclusive access to rsp_mutex (called by getResponses() which has * a the rsp_mutex locked, so this should not be a problem). This method needs to have lock held. */ @GuardedBy("lock") private void adjustMembership() { if(requests.isEmpty()) return; Address mbr; Rsp rsp; for(Map.Entry entry: requests.entrySet()) { mbr=entry.getKey(); if((!this.members.contains(mbr)) || suspects.contains(mbr)) { addSuspect(mbr); rsp=entry.getValue(); rsp.setValue(null); rsp.setSuspected(true); } } } /** * Adds a member to the 'suspects' list. Removes oldest elements from 'suspects' list * to keep the list bounded ('max_suspects' number of elements), Requires lock to be held */ @GuardedBy("lock") private void addSuspect(Address suspected_mbr) { if(!suspects.contains(suspected_mbr)) { suspects.add(suspected_mbr); while(suspects.size() >= max_suspects && !suspects.isEmpty()) suspects.remove(0); // keeps queue bounded } } private static String modeToString(int m) { switch(m) { case GET_FIRST: return "GET_FIRST"; case GET_ALL: return "GET_ALL"; case GET_MAJORITY: return "GET_MAJORITY"; case GET_ABS_MAJORITY: return "GET_ABS_MAJORITY"; case GET_N: return "GET_N"; case GET_NONE: return "GET_NONE"; default: return " (" + m + ")"; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/VoteException.java0000644000175000017500000000061311366547366026321 0ustar twernertwernerpackage org.jgroups.blocks; import org.jgroups.ChannelException; /** * This exception is thrown when voting listener cannot vote on the * specified decree. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public class VoteException extends ChannelException { private static final long serialVersionUID = -878345689312038489L; public VoteException(String msg) { super(msg); } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/ReplicatedTree.java0000644000175000017500000007724611366547366026441 0ustar twernertwerner// $Id: ReplicatedTree.java,v 1.15.6.2 2008/01/22 10:00:59 belaban Exp $ package org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.Util; import javax.management.MBeanServer; import java.io.Serializable; import java.util.*; /** * A tree-like structure that is replicated across several members. Updates will be multicast to all group * members reliably and in the same order. * @author Bela Ban Jan 17 2002 * @author Alfonso Olias-Sanz */ public class ReplicatedTree implements Runnable, MessageListener, MembershipListener { public static final String SEPARATOR="/"; final static int INDENT=4; Node root=new Node(SEPARATOR, SEPARATOR, null, null); final Vector listeners=new Vector(); final Queue request_queue=new Queue(); Thread request_handler=null; JChannel channel=null; PullPushAdapter adapter=null; String groupname="ReplicatedTree-Group"; final Vector members=new Vector(); long state_fetch_timeout=10000; boolean jmx=false; protected final Log log=LogFactory.getLog(this.getClass()); /** Whether or not to use remote calls. If false, all methods will be invoked directly on this instance rather than sending a message to all replicas and only then invoking the method. Useful for testing */ boolean remote_calls=true; String props="udp.xml"; /** Determines when the updates have to be sent across the network, avoids sending unnecessary * messages when there are no member in the group */ private boolean send_message = false; public interface ReplicatedTreeListener { void nodeAdded(String fqn); void nodeRemoved(String fqn); void nodeModified(String fqn); void viewChange(View new_view); // might be MergeView after merging } /** * Creates a channel with the given properties. Connects to the channel, then creates a PullPushAdapter * and starts it */ public ReplicatedTree(String groupname, String props, long state_fetch_timeout) throws Exception { if(groupname != null) this.groupname=groupname; if(props != null) this.props=props; this.state_fetch_timeout=state_fetch_timeout; channel=new JChannel(this.props); channel.connect(this.groupname); start(); } public ReplicatedTree(String groupname, String props, long state_fetch_timeout, boolean jmx) throws Exception { if(groupname != null) this.groupname=groupname; if(props != null) this.props=props; this.jmx=jmx; this.state_fetch_timeout=state_fetch_timeout; channel=new JChannel(this.props); channel.connect(this.groupname); if(jmx) { MBeanServer server=Util.getMBeanServer(); if(server == null) throw new Exception("No MBeanServers found; need to run with an MBeanServer present, or inside JDK 5"); JmxConfigurator.registerChannel(channel, server, "jgroups", channel.getClusterName() , true); } start(); } public ReplicatedTree() { } /** * Expects an already connected channel. Creates a PullPushAdapter and starts it */ public ReplicatedTree(JChannel channel) throws Exception { this.channel=channel; start(); } public void setRemoteCalls(boolean flag) { remote_calls=flag; } public void setRootNode(Node n) { root=n; } public Address getLocalAddress() { return channel != null? channel.getLocalAddress() : null; } public Vector getMembers() { return members; } /** * Fetch the group state from the current coordinator. If successful, this will trigger setState(). */ public void fetchState(long timeout) throws ChannelClosedException, ChannelNotConnectedException { boolean rc=channel.getState(null, timeout); if(log.isInfoEnabled()) { if(rc) log.info("state was retrieved successfully"); else log.info("state could not be retrieved (first member)"); } } public void addReplicatedTreeListener(ReplicatedTreeListener listener) { if(!listeners.contains(listener)) listeners.addElement(listener); } public void removeReplicatedTreeListener(ReplicatedTreeListener listener) { listeners.removeElement(listener); } public final void start() throws Exception { if(request_handler == null) { request_handler=new Thread(this, "ReplicatedTree.RequestHandler thread"); request_handler.setDaemon(true); request_handler.start(); } adapter=new PullPushAdapter(channel, this, this); adapter.setListener(this); boolean rc=channel.getState(null, state_fetch_timeout); if(log.isInfoEnabled()) { if(rc) log.info("state was retrieved successfully"); else log.info("state could not be retrieved (first member)"); } } public void stop() { if(request_handler != null && request_handler.isAlive()) { request_queue.close(true); request_handler=null; } request_handler=null; if(channel != null) { channel.close(); } if(adapter != null) { adapter.stop(); adapter=null; } channel=null; } /** * Adds a new node to the tree and sets its data. If the node doesn not yet exist, it will be created. * Also, parent nodes will be created if not existent. If the node already has data, then the new data * will override the old one. If the node already existed, a nodeModified() notification will be generated. * Otherwise a nodeCreated() motification will be emitted. * @param fqn The fully qualified name of the new node * @param data The new data. May be null if no data should be set in the node. */ public void put(String fqn, HashMap data) { if(!remote_calls) { _put(fqn, data); return; } //Changes done by //if true, propagate action to the group if(send_message == true) { if(channel == null) { if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast PUT request"); return; } try { channel.send( new Message( null, null, new Request(Request.PUT, fqn, data))); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failure bcasting PUT request: " + ex); } } else { _put(fqn, data); } } /** * Adds a key and value to a given node. If the node doesn't exist, it will be created. If the node * already existed, a nodeModified() notification will be generated. Otherwise a * nodeCreated() motification will be emitted. * @param fqn The fully qualified name of the node * @param key The key * @param value The value */ public void put(String fqn, String key, Object value) { if(!remote_calls) { _put(fqn, key, value); return; } //Changes done by //if true, propagate action to the group if(send_message == true) { if(channel == null) { if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast PUT request"); return; } try { channel.send( new Message( null, null, new Request(Request.PUT, fqn, key, value))); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failure bcasting PUT request: " + ex); } } else { _put(fqn, key, value); } } /** * Removes the node from the tree. * @param fqn The fully qualified name of the node. */ public void remove(String fqn) { if(!remote_calls) { _remove(fqn); return; } //Changes done by //if true, propagate action to the group if(send_message == true) { if(channel == null) { if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast REMOVE request"); return; } try { channel.send( new Message(null, null, new Request(Request.REMOVE, fqn))); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failure bcasting REMOVE request: " + ex); } } else { _remove(fqn); } } /** * Removes key from the node's hashmap * @param fqn The fullly qualified name of the node * @param key The key to be removed */ public void remove(String fqn, String key) { if(!remote_calls) { _remove(fqn, key); return; } //Changes done by //if true, propagate action to the group if(send_message == true) { if(channel == null) { if(log.isErrorEnabled()) log.error("channel is null, cannot broadcast REMOVE request"); return; } try { channel.send( new Message( null, null, new Request(Request.REMOVE, fqn, key))); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failure bcasting REMOVE request: " + ex); } } else { _remove(fqn, key); } } /** * Checks whether a given node exists in the tree * @param fqn The fully qualified name of the node * @return boolean Whether or not the node exists */ public boolean exists(String fqn) { if(fqn == null) return false; return findNode(fqn) != null; } /** * Gets the keys of the data map. Returns all keys as Strings. Returns null if node * does not exist. * @param fqn The fully qualified name of the node * @return Set A set of keys (as Strings) */ public Set getKeys(String fqn) { Node n=findNode(fqn); Map data; if(n == null) return null; data=n.getData(); if(data == null) return null; return data.keySet(); } /** * Finds a node given its name and returns the value associated with a given key in its data * map. Returns null if the node was not found in the tree or the key was not found in the hashmap. * @param fqn The fully qualified name of the node. * @param key The key. */ public Object get(String fqn, String key) { Node n=findNode(fqn); if(n == null) return null; return n.getData(key); } /** * Returns the data hashmap for a given node. This method can only be used by callers that are inside * the same package. The reason is that callers must not modify the return value, as these modifications * would not be replicated, thus rendering the replicas inconsistent. * @param fqn The fully qualified name of the node * @return HashMap The data hashmap for the given node */ HashMap get(String fqn) { Node n=findNode(fqn); if(n == null) return null; return n.getData(); } /** * Prints a representation of the node defined by fqn. Output includes name, fqn and * data. */ public String print(String fqn) { Node n=findNode(fqn); if(n == null) return null; return n.toString(); } /** * Returns all children of a given node * @param fqn The fully qualified name of the node * @return Set A list of child names (as Strings) */ public Set getChildrenNames(String fqn) { Node n=findNode(fqn); Map m; if(n == null) return null; m=n.getChildren(); if(m != null) return m.keySet(); else return null; } public String toString() { StringBuilder sb=new StringBuilder(); int indent=0; Map children; children=root.getChildren(); if(children != null && children.size() > 0) { Collection nodes=children.values(); for(Iterator it=nodes.iterator(); it.hasNext();) { ((Node)it.next()).print(sb, indent); sb.append('\n'); } } else sb.append(SEPARATOR); return sb.toString(); } /** * Returns the name of the group that the DistributedTree is connected to * @return String */ public String getGroupName() {return groupname;} /** * Returns the Channel the DistributedTree is connected to * @return Channel */ public Channel getChannel() {return channel;} /** * Returns the number of current members joined to the group * @return int */ public int getGroupMembersNumber() {return members.size();} /* --------------------- Callbacks -------------------------- */ public void _put(String fqn, HashMap data) { Node n; StringHolder child_name=new StringHolder(); boolean child_exists=false; if(fqn == null) return; n=findParentNode(fqn, child_name, true); // create all nodes if they don't exist if(child_name.getValue() != null) { child_exists=n.childExists(child_name.getValue()); n.createChild(child_name.getValue(), fqn, n, data); } else { child_exists=true; n.setData(data); } if(child_exists) notifyNodeModified(fqn); else notifyNodeAdded(fqn); } public void _put(String fqn, String key, Object value) { Node n; StringHolder child_name=new StringHolder(); boolean child_exists=false; if(fqn == null || key == null || value == null) return; n=findParentNode(fqn, child_name, true); if(child_name.getValue() != null) { child_exists=n.childExists(child_name.getValue()); n.createChild(child_name.getValue(), fqn, n, key, value); } else { child_exists=true; n.setData(key, value); } if(child_exists) notifyNodeModified(fqn); else notifyNodeAdded(fqn); } public void _remove(String fqn) { Node n; StringHolder child_name=new StringHolder(); if(fqn == null) return; if(fqn.equals(SEPARATOR)) { root.removeAll(); notifyNodeRemoved(fqn); return; } n=findParentNode(fqn, child_name, false); if(n == null) return; n.removeChild(child_name.getValue(), fqn); notifyNodeRemoved(fqn); } public void _remove(String fqn, String key) { Node n; if(fqn == null || key == null) return; n=findNode(fqn); if(n != null) n.removeData(key); } public void _removeData(String fqn) { Node n; if(fqn == null) return; n=findNode(fqn); if(n != null) n.removeData(); } /* ----------------- End of Callbacks ---------------------- */ /*-------------------- MessageListener ----------------------*/ /** Callback. Process the contents of the message; typically an _add() or _set() request */ public void receive(Message msg) { Request req=null; if(msg == null || msg.getLength() == 0) return; try { req=(Request)msg.getObject(); request_queue.add(req); } catch(QueueClosedException queue_closed_ex) { if(log.isErrorEnabled()) log.error("request queue is null"); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed unmarshalling request: " + ex); } } /** Return a copy of the current cache (tree) */ public byte[] getState() { try { return Util.objectToByteBuffer(root.clone()); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception returning cache: " + ex); return null; } } /** Set the cache (tree) to this value */ public void setState(byte[] new_state) { Node new_root=null; Object obj; if(new_state == null) { if(log.isInfoEnabled()) log.info("new cache is null"); return; } try { obj=Util.objectFromByteBuffer(new_state); new_root=(Node)((Node)obj).clone(); root=new_root; notifyAllNodesCreated(root); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("could not set cache: " + ex); } } /*-------------------- End of MessageListener ----------------------*/ /*----------------------- MembershipListener ------------------------*/ public void viewAccepted(View new_view) { Vector new_mbrs=new_view.getMembers(); // todo: if MergeView, fetch and reconcile state from coordinator // actually maybe this is best left up to the application ? we just notify them and let // the appl handle it ? if(new_mbrs != null) { notifyViewChange(new_view); members.removeAllElements(); for(int i=0; i < new_mbrs.size(); i++) members.addElement(new_mbrs.elementAt(i)); } //if size is bigger than one, there are more peers in the group //otherwise there is only one server. send_message=members.size() > 1; } /** Called when a member is suspected */ public void suspect(Address suspected_mbr) { ; } /** Block sending and receiving of messages until viewAccepted() is called */ public void block() { } /*------------------- End of MembershipListener ----------------------*/ /** Request handler thread */ public void run() { Request req; String fqn=null; while(request_handler != null) { try { req=(Request)request_queue.remove(0); fqn=req.fqn; switch(req.type) { case Request.PUT: if(req.key != null && req.value != null) _put(fqn, req.key, req.value); else _put(fqn, req.data); break; case Request.REMOVE: if(req.key != null) _remove(fqn, req.key); else _remove(fqn); break; default: if(log.isErrorEnabled()) log.error("type " + req.type + " unknown"); break; } } catch(QueueClosedException queue_closed_ex) { request_handler=null; break; } catch(Throwable other_ex) { if(log.isWarnEnabled()) log.warn("exception processing request: " + other_ex); } } } /** * Find the node just above the one indicated by fqn. This is needed in many cases, * e.g. to add a new node or remove an existing node. * @param fqn The fully qualified name of the node. * @param child_name Will be filled with the name of the child when this method returns. The child name * is the last relative name of the fqn, e.g. in "/a/b/c" it would be "c". * @param create_if_not_exists Create parent nodes along the way if they don't exist. Otherwise, this method * will return when a node cannot be found. */ Node findParentNode(String fqn, StringHolder child_name, boolean create_if_not_exists) { Node curr=root, node; StringTokenizer tok; String name; StringBuilder sb=null; if(fqn == null || fqn.equals(SEPARATOR) || "".equals(fqn)) return curr; sb=new StringBuilder(); tok=new StringTokenizer(fqn, SEPARATOR); while(tok.countTokens() > 1) { name=tok.nextToken(); sb.append(SEPARATOR).append(name); node=curr.getChild(name); if(node == null && create_if_not_exists) node=curr.createChild(name, sb.toString(), null, null); if(node == null) return null; else curr=node; } if(tok.countTokens() > 0 && child_name != null) child_name.setValue(tok.nextToken()); return curr; } /** * Returns the node at fqn. This method should not be used by clients (therefore it is package-private): * it is only used internally (for navigation). C++ 'friend' would come in handy here... * @param fqn The fully qualified name of the node * @return Node The node at fqn */ Node findNode(String fqn) { StringHolder sh=new StringHolder(); Node n=findParentNode(fqn, sh, false); String child_name=sh.getValue(); if(fqn == null || fqn.equals(SEPARATOR) || "".equals(fqn)) return root; if(n == null || child_name == null) return null; else return n.getChild(child_name); } void notifyNodeAdded(String fqn) { for(int i=0; i < listeners.size(); i++) ((ReplicatedTreeListener)listeners.elementAt(i)).nodeAdded(fqn); } void notifyNodeRemoved(String fqn) { for(int i=0; i < listeners.size(); i++) ((ReplicatedTreeListener)listeners.elementAt(i)).nodeRemoved(fqn); } void notifyNodeModified(String fqn) { for(int i=0; i < listeners.size(); i++) ((ReplicatedTreeListener)listeners.elementAt(i)).nodeModified(fqn); } void notifyViewChange(View v) { for(int i=0; i < listeners.size(); i++) ((ReplicatedTreeListener)listeners.elementAt(i)).viewChange(v); } /** Generates NodeAdded notifications for all nodes of the tree. This is called whenever the tree is initially retrieved (state transfer) */ void notifyAllNodesCreated(Node curr) { Node n; Map children; if(curr == null) return; notifyNodeAdded(curr.fqn); if((children=curr.getChildren()) != null) { for(Iterator it=children.values().iterator(); it.hasNext();) { n=(Node)it.next(); notifyAllNodesCreated(n); } } } public static class Node implements Serializable { String name=null; // relative name (e.g. "Security") String fqn=null; // fully qualified name (e.g. "/federations/fed1/servers/Security") Node parent=null; // parent node TreeMap children=null; // keys: child name, value: Node HashMap data=null; // data for current node private static final long serialVersionUID = -3077676554440038890L; // Address creator=null; // member that created this node (needed ?) private Node(String child_name, String fqn, Node parent, HashMap data) { name=child_name; this.fqn=fqn; this.parent=parent; if(data != null) this.data=(HashMap)data.clone(); } private Node(String child_name, String fqn, Node parent, String key, Object value) { name=child_name; this.fqn=fqn; this.parent=parent; if(data == null) data=new HashMap(); data.put(key, value); } void setData(Map data) { if(data == null) return; if(this.data == null) this.data=new HashMap(); this.data.putAll(data); } void setData(String key, Object value) { if(this.data == null) this.data=new HashMap(); this.data.put(key, value); } HashMap getData() { return data; } Object getData(String key) { return data != null? data.get(key) : null; } boolean childExists(String child_name) { if(child_name == null) return false; return children != null && children.containsKey(child_name); } Node createChild(String child_name, String fqn, Node parent, HashMap data) { Node child=null; if(child_name == null) return null; if(children == null) children=new TreeMap(); child=(Node)children.get(child_name); if(child != null) child.setData(data); else { child=new Node(child_name, fqn, parent, data); children.put(child_name, child); } return child; } Node createChild(String child_name, String fqn, Node parent, String key, Object value) { Node child=null; if(child_name == null) return null; if(children == null) children=new TreeMap(); child=(Node)children.get(child_name); if(child != null) child.setData(key, value); else { child=new Node(child_name, fqn, parent, key, value); children.put(child_name, child); } return child; } Node getChild(String child_name) { return child_name == null? null : children == null? null : (Node)children.get(child_name); } Map getChildren() { return children; } void removeData(String key) { if(data != null) data.remove(key); } void removeData() { if(data != null) data.clear(); } void removeChild(String child_name, String fqn) { if(child_name != null && children != null && children.containsKey(child_name)) { children.remove(child_name); } } void removeAll() { if(children != null) children.clear(); } void print(StringBuilder sb, int indent) { printIndent(sb, indent); sb.append(SEPARATOR).append(name); if(children != null && children.size() > 0) { Collection values=children.values(); for(Iterator it=values.iterator(); it.hasNext();) { sb.append('\n'); ((Node)it.next()).print(sb, indent + INDENT); } } } void printIndent(StringBuilder sb, int indent) { if(sb != null) { for(int i=0; i < indent; i++) sb.append(' '); } } public String toString() { StringBuilder sb=new StringBuilder(); if(name != null) sb.append("\nname=" + name); if(fqn != null) sb.append("\nfqn=" + fqn); if(data != null) sb.append("\ndata=" + data); return sb.toString(); } public Object clone() throws CloneNotSupportedException { Node n=new Node(name, fqn, parent != null? (Node)parent.clone() : null, data); if(children != null) n.children=(TreeMap)children.clone(); return n; } } private static class StringHolder { String s=null; private StringHolder() { } void setValue(String s) { this.s=s; } String getValue() { return s; } } /** * Class used to multicast add(), remove() and set() methods to all members. */ private static class Request implements Serializable { static final int PUT=1; static final int REMOVE=2; int type=0; String fqn=null; String key=null; Object value=null; HashMap data=null; private static final long serialVersionUID = 7772753222127676782L; private Request(int type, String fqn) { this.type=type; this.fqn=fqn; } private Request(int type, String fqn, HashMap data) { this(type, fqn); this.data=data; } private Request(int type, String fqn, String key) { this(type, fqn); this.key=key; } private Request(int type, String fqn, String key, Object value) { this(type, fqn); this.key=key; this.value=value; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(type2String(type)).append(" ("); if(fqn != null) sb.append(" fqn=" + fqn); switch(type) { case PUT: if(data != null) sb.append(", data=" + data); if(key != null) sb.append(", key=" + key); if(value != null) sb.append(", value=" + value); break; case REMOVE: if(key != null) sb.append(", key=" + key); break; default: break; } sb.append(')'); return sb.toString(); } static String type2String(int t) { switch(t) { case PUT: return "PUT"; case REMOVE: return "REMOVE"; default: return "UNKNOWN"; } } } public static void main(String[] args) { ReplicatedTree tree=null; HashMap m=new HashMap(); String props; props="udp.xml"; try { tree=new ReplicatedTree(null, props, 10000); // tree.setRemoteCalls(false); tree.addReplicatedTreeListener(new MyListener()); tree.put("/a/b/c", null); tree.put("/a/b/c1", null); tree.put("/a/b/c2", null); tree.put("/a/b1/chat", null); tree.put("/a/b1/chat2", null); tree.put("/a/b1/chat5", null); System.out.println(tree); m.put("name", "Bela Ban"); m.put("age", new Integer(36)); m.put("cube", "240-17"); tree.put("/a/b/c", m); System.out.println("info for for \"/a/b/c\" is " + tree.print("/a/b/c")); tree.put("/a/b/c", "age", new Integer(37)); System.out.println("info for for \"/a/b/c\" is " + tree.print("/a/b/c")); tree.remove("/a/b"); System.out.println(tree); } catch(Exception ex) { System.err.println(ex); } } static class MyListener implements ReplicatedTreeListener { public void nodeAdded(String fqn) { System.out.println("** node added: " + fqn); } public void nodeRemoved(String fqn) { System.out.println("** node removed: " + fqn); } public void nodeModified(String fqn) { System.out.println("** node modified: " + fqn); } public void viewChange(View new_view) { System.out.println("** view change: " + new_view); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/ReplicatedMap.java0000644000175000017500000000120111366547366026231 0ustar twernertwernerpackage org.jgroups.blocks; import java.io.Serializable; import java.util.Map; import java.util.concurrent.ConcurrentMap; /** * @author Bela Ban * @version $Id: ReplicatedMap.java,v 1.2 2007/08/22 10:06:42 belaban Exp $ */ public interface ReplicatedMap extends ConcurrentMap { V _put(K key, V value); void _putAll(Map map); void _clear(); V _remove(Object key); V _putIfAbsent(K key, V value); boolean _remove(Object key, Object value); boolean _replace(K key, V oldValue, V newValue); V _replace(K key, V value); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/RpcDispatcher.java0000644000175000017500000004465411366547366026275 0ustar twernertwerner// $Id: RpcDispatcher.java,v 1.30.2.4 2008/07/30 12:27:56 belaban Exp $ package org.jgroups.blocks; import org.jgroups.*; import org.jgroups.util.RspList; import org.jgroups.util.Util; import org.jgroups.util.Buffer; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.IllegalArgumentException ; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Vector; /** * This class allows a programmer to invoke remote methods in all (or single) * group members and optionally wait for the return value(s). * An application will typically create a channel and layer the * RpcDispatcher building block on top of it, which allows it to * dispatch remote methods (client role) and at the same time be * called by other members (server role). * This class is derived from MessageDispatcher. * Is the equivalent of RpcProtocol on the application rather than protocol level. * @author Bela Ban */ public class RpcDispatcher extends MessageDispatcher implements ChannelListener { protected Object server_obj=null; /** Marshaller to marshall requests at the caller and unmarshal requests at the receiver(s) */ protected Marshaller2 req_marshaller=null; /** Marshaller to marshal responses at the receiver(s) and unmarshal responses at the caller */ protected Marshaller2 rsp_marshaller=null; protected final List additionalChannelListeners=new ArrayList(); protected MethodLookup method_lookup=null; public RpcDispatcher() { } public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj) { super(channel, l, l2); channel.addChannelListener(this); this.server_obj=server_obj; } public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj, boolean deadlock_detection) { super(channel, l, l2, deadlock_detection); channel.addChannelListener(this); this.server_obj=server_obj; } public RpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object server_obj, boolean deadlock_detection, boolean concurrent_processing) { super(channel, l, l2, deadlock_detection, concurrent_processing); channel.addChannelListener(this); this.server_obj=server_obj; } public RpcDispatcher(PullPushAdapter adapter, Serializable id, MessageListener l, MembershipListener l2, Object server_obj) { super(adapter, id, l, l2); // Fixes bug #804956 // channel.setChannelListener(this); if(this.adapter != null) { Transport t=this.adapter.getTransport(); if(t != null && t instanceof Channel) { ((Channel)t).addChannelListener(this); } } this.server_obj=server_obj; } public interface Marshaller { byte[] objectToByteBuffer(Object obj) throws Exception; Object objectFromByteBuffer(byte[] buf) throws Exception; } public interface Marshaller2 extends Marshaller { /** * Marshals the object into a byte[] buffer and returns a Buffer with a ref to the underlying byte[] buffer, * offset and length.
* * Note that the underlying byte[] buffer must not be changed as this would change the buffer of a message which * potentially can get retransmitted, and such a retransmission would then carry a ref to a changed byte[] buffer ! * * @param obj * @return * @throws Exception */ Buffer objectToBuffer(Object obj) throws Exception; Object objectFromByteBuffer(byte[] buf, int offset, int length) throws Exception; } /** Used to provide a Marshaller2 interface to a Marshaller. This class is for internal use only, and will be * removed in 3.0 when Marshaller and Marshaller2 get merged. Do not use, but provide an implementation of * Marshaller directly, e.g. in setRequestMarshaller(). */ public static class MarshallerAdapter implements Marshaller2 { private final Marshaller marshaller; public MarshallerAdapter(Marshaller marshaller) { this.marshaller=marshaller; } public byte[] objectToByteBuffer(Object obj) throws Exception { return marshaller.objectToByteBuffer(obj); } public Object objectFromByteBuffer(byte[] buf) throws Exception { return buf == null? null : marshaller.objectFromByteBuffer(buf); } public Buffer objectToBuffer(Object obj) throws Exception { byte[] buf=marshaller.objectToByteBuffer(obj); return new Buffer(buf, 0, buf.length); } public Object objectFromByteBuffer(byte[] buf, int offset, int length) throws Exception { if(buf == null || (offset == 0 && length == buf.length)) return marshaller.objectFromByteBuffer(buf); byte[] tmp=new byte[length]; System.arraycopy(buf, offset, tmp, 0, length); return marshaller.objectFromByteBuffer(tmp); } } public String getName() {return "RpcDispatcher";} public Marshaller getRequestMarshaller() {return req_marshaller;} public void setRequestMarshaller(Marshaller m) { if(m == null) this.req_marshaller=null; else if(m instanceof Marshaller2) this.req_marshaller=(Marshaller2)m; else this.req_marshaller=new MarshallerAdapter(m); } public Marshaller getResponseMarshaller() {return rsp_marshaller;} public void setResponseMarshaller(Marshaller m) { if(m == null) this.rsp_marshaller=null; else if(m instanceof Marshaller2) this.rsp_marshaller=(Marshaller2)m; else this.rsp_marshaller=new MarshallerAdapter(m); if(corr != null) corr.setMarshaller(this.rsp_marshaller); } public Marshaller getMarshaller() {return req_marshaller;} public void setMarshaller(Marshaller m) {setRequestMarshaller(m);} public Object getServerObject() {return server_obj;} public void setServerObject(Object server_obj) { this.server_obj=server_obj; } public MethodLookup getMethodLookup() { return method_lookup; } public void setMethodLookup(MethodLookup method_lookup) { this.method_lookup=method_lookup; } public RspList castMessage(Vector dests, Message msg, int mode, long timeout) { if(log.isErrorEnabled()) log.error("this method should not be used with " + "RpcDispatcher, but MessageDispatcher. Returning null"); return null; } public Object sendMessage(Message msg, int mode, long timeout) throws TimeoutException, SuspectedException { if(log.isErrorEnabled()) log.error("this method should not be used with " + "RpcDispatcher, but MessageDispatcher. Returning null"); return null; } public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, Class[] types, int mode, long timeout) { return callRemoteMethods(dests, method_name, args, types, mode, timeout, false); } public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, Class[] types, int mode, long timeout, boolean use_anycasting) { return callRemoteMethods(dests, method_name, args, types, mode, timeout, use_anycasting, null); } public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, Class[] types, int mode, long timeout, boolean use_anycasting, RspFilter filter) { MethodCall method_call=new MethodCall(method_name, args, types); return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting, false, filter); } public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, String[] signature, int mode, long timeout) { return callRemoteMethods(dests, method_name, args, signature, mode, timeout, false); } public RspList callRemoteMethods(Vector dests, String method_name, Object[] args, String[] signature, int mode, long timeout, boolean use_anycasting) { MethodCall method_call=new MethodCall(method_name, args, signature); return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting); } public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout) { return callRemoteMethods(dests, method_call, mode, timeout, false); } public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout, boolean use_anycasting) { return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting, false); } public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout, boolean use_anycasting, boolean oob) { return callRemoteMethods(dests, method_call, mode, timeout, use_anycasting, oob, null); } public RspList callRemoteMethods(Vector dests, MethodCall method_call, int mode, long timeout, boolean use_anycasting, boolean oob, RspFilter filter) { if(dests != null && dests.isEmpty()) { // don't send if dest list is empty if(log.isTraceEnabled()) log.trace(new StringBuilder("destination list of ").append(method_call.getName()). append("() is empty: no need to send message")); return new RspList(); } if(log.isTraceEnabled()) log.trace(new StringBuilder("dests=").append(dests).append(", method_call=").append(method_call). append(", mode=").append(mode).append(", timeout=").append(timeout)); Object buf; try { buf=req_marshaller != null? req_marshaller.objectToBuffer(method_call) : Util.objectToByteBuffer(method_call); } catch(Exception e) { // if(log.isErrorEnabled()) log.error("exception", e); // we will change this in 2.4 to add the exception to the signature // (see http://jira.jboss.com/jira/browse/JGRP-193). The reason for a RTE is that we cannot change the // signature in 2.3, otherwise 2.3 would be *not* API compatible to prev releases throw new RuntimeException("failure to marshal argument(s)", e); } Message msg=new Message(); if(buf instanceof Buffer) msg.setBuffer((Buffer)buf); else msg.setBuffer((byte[])buf); if(oob) msg.setFlag(Message.OOB); RspList retval=super.castMessage(dests, msg, mode, timeout, use_anycasting, filter); if(log.isTraceEnabled()) log.trace("responses: " + retval); return retval; } public Object callRemoteMethod(Address dest, String method_name, Object[] args, Class[] types, int mode, long timeout) throws Throwable { MethodCall method_call=new MethodCall(method_name, args, types); return callRemoteMethod(dest, method_call, mode, timeout); } public Object callRemoteMethod(Address dest, String method_name, Object[] args, String[] signature, int mode, long timeout) throws Throwable { MethodCall method_call=new MethodCall(method_name, args, signature); return callRemoteMethod(dest, method_call, mode, timeout); } public Object callRemoteMethod(Address dest, MethodCall method_call, int mode, long timeout) throws Throwable { return callRemoteMethod(dest, method_call, mode, timeout, false); } public Object callRemoteMethod(Address dest, MethodCall method_call, int mode, long timeout, boolean oob) throws Throwable { Object buf=null; Message msg=null; Object retval=null; if(log.isTraceEnabled()) log.trace("dest=" + dest + ", method_call=" + method_call + ", mode=" + mode + ", timeout=" + timeout); buf=req_marshaller != null? req_marshaller.objectToBuffer(method_call) : Util.objectToByteBuffer(method_call); msg=new Message(dest, null, null); if(buf instanceof Buffer) msg.setBuffer((Buffer)buf); else msg.setBuffer((byte[])buf); if(oob) msg.setFlag(Message.OOB); retval=super.sendMessage(msg, mode, timeout); if(log.isTraceEnabled()) log.trace("retval: " + retval); if(retval instanceof Throwable) throw (Throwable)retval; return retval; } protected void correlatorStarted() { if(corr != null) corr.setMarshaller(rsp_marshaller); } /** * Message contains MethodCall. Execute it against *this* object and return result. * Use MethodCall.invoke() to do this. Return result. */ public Object handle(Message req) { Object body=null; MethodCall method_call; if(server_obj == null) { if(log.isErrorEnabled()) log.error("no method handler is registered. Discarding request."); return null; } if(req == null || req.getLength() == 0) { if(log.isErrorEnabled()) log.error("message or message buffer is null"); return null; } try { body=req_marshaller != null? req_marshaller.objectFromByteBuffer(req.getBuffer(), req.getOffset(), req.getLength()) : req.getObject(); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception marshalling object", e); return e; } if(!(body instanceof MethodCall)) { if(log.isErrorEnabled()) log.error("message does not contain a MethodCall object"); // create an exception to represent this and return it return new IllegalArgumentException("message does not contain a MethodCall object") ; } method_call=(MethodCall)body; try { if(log.isTraceEnabled()) log.trace("[sender=" + req.getSrc() + "], method_call: " + method_call); if(method_call.getMode() == MethodCall.ID) { if(method_lookup == null) throw new Exception("MethodCall uses ID=" + method_call.getId() + ", but method_lookup has not been set"); Method m=method_lookup.findMethod(method_call.getId()); if(m == null) throw new Exception("no method foudn for " + method_call.getId()); method_call.setMethod(m); } return method_call.invoke(server_obj); } catch(Throwable x) { return x; } } /** * Add a new channel listener to be notified on the channel's state change. * * @return true if the listener was added or false if the listener was already in the list. */ public boolean addChannelListener(ChannelListener l) { synchronized(additionalChannelListeners) { if (additionalChannelListeners.contains(l)) { return false; } additionalChannelListeners.add(l); return true; } } /** * * @return true if the channel was removed indeed. */ public boolean removeChannelListener(ChannelListener l) { synchronized(additionalChannelListeners) { return additionalChannelListeners.remove(l); } } /* --------------------- Interface ChannelListener ---------------------- */ public void channelConnected(Channel channel) { synchronized(additionalChannelListeners) { for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { ChannelListener l = (ChannelListener)i.next(); try { l.channelConnected(channel); } catch(Throwable t) { log.warn("channel listener failed", t); } } } } public void channelDisconnected(Channel channel) { stop(); synchronized(additionalChannelListeners) { for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { ChannelListener l = (ChannelListener)i.next(); try { l.channelDisconnected(channel); } catch(Throwable t) { log.warn("channel listener failed", t); } } } } public void channelClosed(Channel channel) { stop(); synchronized(additionalChannelListeners) { for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { ChannelListener l = (ChannelListener)i.next(); try { l.channelClosed(channel); } catch(Throwable t) { log.warn("channel listener failed", t); } } } } public void channelShunned() { synchronized(additionalChannelListeners) { for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { ChannelListener l = (ChannelListener)i.next(); try { l.channelShunned(); } catch(Throwable t) { log.warn("channel listener failed", t); } } } } public void channelReconnected(Address new_addr) { if(log.isTraceEnabled()) log.trace("channel has been rejoined, old local_addr=" + local_addr + ", new local_addr=" + new_addr); this.local_addr=new_addr; start(); synchronized(additionalChannelListeners) { for(Iterator i = additionalChannelListeners.iterator(); i.hasNext(); ) { ChannelListener l = (ChannelListener)i.next(); try { l.channelReconnected(new_addr); } catch(Throwable t) { log.warn("channel listener failed", t); } } } } /* ----------------------------------------------------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/RspCollector.java0000644000175000017500000000053111366547366026137 0ustar twernertwerner// $Id: RspCollector.java,v 1.4 2007/02/16 09:06:57 belaban Exp $ package org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.View; public interface RspCollector { void receiveResponse(Object response_value, Address sender); void suspect(Address mbr); void viewChange(View new_view); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/MethodLookup.java0000644000175000017500000000035111366547366026136 0ustar twernertwernerpackage org.jgroups.blocks; import java.lang.reflect.Method; /** * @author Bela Ban * @version $Id: MethodLookup.java,v 1.3 2005/07/22 08:59:20 belaban Exp $ */ public interface MethodLookup { Method findMethod(short id); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/ConnectionTable.java0000644000175000017500000004016211366547366026577 0ustar twernertwerner// $Id: ConnectionTable.java,v 1.62.2.6 2009/06/17 07:26:01 vlada Exp $ package org.jgroups.blocks; import org.jgroups.Address; import org.jgroups.util.PortsManager; import org.jgroups.stack.IpAddress; import java.io.IOException; import java.net.*; /** * Manages incoming and outgoing TCP connections. For each outgoing message to destination P, if there * is not yet a connection for P, one will be created. Subsequent outgoing messages will use this * connection. For incoming messages, one server socket is created at startup. For each new incoming * client connecting, a new thread from a thread pool is allocated and listens for incoming messages * until the socket is closed by the peer.
Sockets/threads with no activity will be killed * after some time. *

* Incoming messages from any of the sockets can be received by setting the message listener. * @author Bela Ban */ public class ConnectionTable extends BasicConnectionTable implements Runnable { /** * Regular ConnectionTable without expiration of idle connections * @param srv_port The port on which the server will listen. If this port is reserved, the next * free port will be taken (incrementing srv_port). */ public ConnectionTable(int srv_port) throws Exception { this.srv_port=srv_port; init(); } public ConnectionTable(InetAddress bind_addr, int srv_port) throws Exception { this.srv_port=srv_port; this.bind_addr=bind_addr; init(); } /** * ConnectionTable including a connection reaper. Connections that have been idle for more than conn_expire_time * milliseconds will be closed and removed from the connection table. On next access they will be re-created. * @param srv_port The port on which the server will listen * @param reaper_interval Number of milliseconds to wait for reaper between attepts to reap idle connections * @param conn_expire_time Number of milliseconds a connection can be idle (no traffic sent or received until * it will be reaped */ public ConnectionTable(int srv_port, long reaper_interval, long conn_expire_time) throws Exception { this.srv_port=srv_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; init(); } public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; init(); } /** * Create a ConnectionTable * @param r A reference to a receiver of all messages received by this class. Method receive() * will be called. * @param bind_addr The host name or IP address of the interface to which the server socket will bind. * This is interesting only in multi-homed systems. If bind_addr is null, the * server socket will bind to the first available interface (e.g. /dev/hme0 on * Solaris or /dev/eth0 on Linux systems). * @param external_addr The address which will be broadcast to the group (the externally visible address * which this host should be contacted on). If external_addr is null, it will default to * the same address that the server socket is bound to. * @param srv_port The port to which the server socket will bind to. If this port is reserved, the next * free port will be taken (incrementing srv_port). * @param max_port The largest port number that the server socket will be bound to. If max_port < srv_port * then there is no limit. */ public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, PortsManager pm) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.pm=pm; init(); } public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; init(); } /** * ConnectionTable including a connection reaper. Connections that have been idle for more than conn_expire_time * milliseconds will be closed and removed from the connection table. On next access they will be re-created. * * @param r The Receiver * @param bind_addr The host name or IP address of the interface to which the server socket will bind. * This is interesting only in multi-homed systems. If bind_addr is null, the * server socket will bind to the first available interface (e.g. /dev/hme0 on * Solaris or /dev/eth0 on Linux systems). * @param external_addr The address which will be broadcast to the group (the externally visible address * which this host should be contacted on). If external_addr is null, it will default to * the same address that the server socket is bound to. * @param srv_port The port to which the server socket will bind to. If this port is reserved, the next * free port will be taken (incrementing srv_port). * @param max_port The largest port number that the server socket will be bound to. If max_port < srv_port * then there is no limit. * @param reaper_interval Number of milliseconds to wait for reaper between attepts to reap idle connections * @param conn_expire_time Number of milliseconds a connection can be idle (no traffic sent or received until * it will be reaped */ public ConnectionTable(Receiver r, InetAddress bind_addr, InetAddress external_addr, int srv_port, int max_port, long reaper_interval, long conn_expire_time, PortsManager pm) throws Exception { setReceiver(r); this.bind_addr=bind_addr; this.external_addr=external_addr; this.srv_port=srv_port; this.max_port=max_port; this.pm=pm; this.reaper_interval=reaper_interval; this.conn_expire_time=conn_expire_time; use_reaper=true; init(); } /** Try to obtain correct Connection (or create one if not yet existent) */ Connection getConnection(Address dest) throws Exception { Connection conn=null; Socket sock; synchronized(conns) { conn=conns.get(dest); if(conn == null) { // changed by bela Jan 18 2004: use the bind address for the client sockets as well SocketAddress tmpBindAddr=new InetSocketAddress(bind_addr, 0); InetAddress tmpDest=((IpAddress)dest).getIpAddress(); SocketAddress destAddr=new InetSocketAddress(tmpDest, ((IpAddress)dest).getPort()); sock=new Socket(); sock.bind(tmpBindAddr); sock.setKeepAlive(true); sock.setTcpNoDelay(tcp_nodelay); if(linger > 0) sock.setSoLinger(true, linger); else sock.setSoLinger(false, -1); sock.connect(destAddr, sock_conn_timeout); try { sock.setSendBufferSize(send_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes", ex); } try { sock.setReceiveBufferSize(recv_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes", ex); } conn=new Connection(sock, dest); conn.sendLocalAddress(local_addr); notifyConnectionOpened(dest); addConnection(dest, conn); conn.init(); if(log.isTraceEnabled()) log.trace("created socket to " + dest); } return conn; } } public final void start() throws Exception { acceptor=getThreadFactory().newThread(thread_group,this, "ConnectionTable.AcceptorThread"); acceptor.start(); // start the connection reaper - will periodically remove unused connections if(use_reaper && reaper == null) { reaper=new Reaper(); reaper.start(); } super.start(); } protected void init() throws Exception { srv_sock=createServerSocket(srv_port, max_port); if (external_addr!=null) local_addr=new IpAddress(external_addr, srv_sock.getLocalPort()); else if (bind_addr != null) local_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); else local_addr=new IpAddress(srv_sock.getLocalPort()); if(log.isDebugEnabled()) log.debug("server socket listening on " + local_addr); } /** * Acceptor thread. Continuously accept new connections. Create a new thread for each new * connection and put it in conns. When the thread should stop, it is * interrupted by the thread creator. */ public void run() { Socket client_sock=null; Connection conn=null; Address peer_addr; while(srv_sock != null) { try { conn=null; client_sock=srv_sock.accept(); if(!running) { if(log.isWarnEnabled()) log.warn("cannot accept connection from " + client_sock.getRemoteSocketAddress() + " as I'm closed"); break; } if(log.isTraceEnabled()) log.trace("[" +local_addr + "] accepted connection from " + client_sock.getInetAddress() + ":" + client_sock.getPort()); try { client_sock.setSendBufferSize(send_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting send buffer size to " + send_buf_size + " bytes", ex); } try { client_sock.setReceiveBufferSize(recv_buf_size); } catch(IllegalArgumentException ex) { if(log.isErrorEnabled()) log.error("exception setting receive buffer size to " + send_buf_size + " bytes", ex); } client_sock.setKeepAlive(true); client_sock.setTcpNoDelay(tcp_nodelay); if(linger > 0) client_sock.setSoLinger(true, linger); else client_sock.setSoLinger(false, -1); // create new thread and add to conn table conn=new Connection(client_sock, null); // will call receive(msg) // get peer's address peer_addr=conn.readPeerAddress(client_sock); // client_addr=new IpAddress(client_sock.getInetAddress(), client_port); conn.setPeerAddress(peer_addr); synchronized(conns) { Connection tmp=conns.get(peer_addr); //Vladimir Nov, 5th, 2007 //we might have a connection to peer but is that //connection still open? boolean connectionOpen = tmp != null && !tmp.isSocketClosed(); if(connectionOpen) { if(peer_addr.compareTo(local_addr) > 0) { if(log.isTraceEnabled()) log.trace("peer's address (" + peer_addr + ") is greater than our local address (" + local_addr + "), replacing our existing connection"); // peer's address is greater, add peer's connection to ConnectionTable, destroy existing connection removeConnection(peer_addr); addConnection(peer_addr, conn); notifyConnectionOpened(peer_addr); } else { if(log.isTraceEnabled()) log.trace("peer's address (" + peer_addr + ") is smaller than our local address (" + local_addr + "), rejecting peer connection request"); conn.destroy(); continue; } } else { addConnection(peer_addr, conn); notifyConnectionOpened(peer_addr); } } conn.init(); // starts handler thread on this socket } catch(SocketTimeoutException timeout_ex) { if(log.isWarnEnabled()) log.warn("timed out waiting for peer address, closing connection " + conn + ": " + timeout_ex); if(conn != null) conn.destroy(); if(srv_sock == null) break; // socket was closed, therefore stop } catch(SocketException sock_ex) { if(log.isWarnEnabled() && srv_sock != null) log.warn("Could not read accept connection from peer " + sock_ex); if(conn != null) conn.destroy(); if(srv_sock == null) break; // socket was closed, therefore stop } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("Could not read accept connection from peer " + ex); if(srv_sock == null) break; // socket was closed, therefore stop } } if(log.isTraceEnabled()) log.trace(Thread.currentThread().getName() + " terminated"); } /** Finds first available port starting at start_port and returns server socket. * Will not bind to port >end_port. Sets srv_port */ protected ServerSocket createServerSocket(int start_port, int end_port) throws Exception { ServerSocket ret=null; while(true) { try { if(start_port > 0 && pm != null) start_port=pm.getNextAvailablePort(start_port); if(bind_addr == null) ret=new ServerSocket(start_port); else { // changed (bela Sept 7 2007): we accept connections on all NICs ret=new ServerSocket(start_port, backlog, bind_addr); } } catch(BindException bind_ex) { if (start_port==end_port) throw new BindException("No available port to bind to"); if(bind_addr != null && !bind_addr.isLoopbackAddress()) { NetworkInterface nic=NetworkInterface.getByInetAddress(bind_addr); if(nic == null) throw new BindException("bind_addr " + bind_addr + " is not a valid interface"); } start_port++; continue; } catch(IOException io_ex) { if(log.isErrorEnabled()) log.error("exception is " + io_ex); } srv_port=start_port; break; } if(ret == null) throw new IOException("Could not create server socket in port range " + start_port + " - " +end_port); return ret; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/blocks/MethodCall.java0000644000175000017500000003612511366547366025550 0ustar twernertwernerpackage org.jgroups.blocks; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; /** * A method call is the JGroups representation of a remote method. * It includes the name of the method (case sensitive) and a list of arguments. * A method call is serializable and can be passed over the wire. * @author Bela Ban * @version $Revision: 1.24.6.2 $ */ public class MethodCall implements Externalizable { private static final long serialVersionUID=7873471327078957662L; /** The name of the method, case sensitive. */ protected String method_name; /** The ID of a method, maps to a java.lang.reflect.Method */ protected short method_id=-1; /** The arguments of the method. */ protected Object[] args; /** The class types, e.g., new Class[]{String.class, int.class}. */ protected Class[] types; /** The signature, e.g., new String[]{String.class.getName(), int.class.getName()}. */ protected String[] signature; /** The Method of the call. */ protected Method method; /** To carry arbitrary data with a method call, data needs to be serializable if sent across the wire */ protected Map payload; protected static final Log log=LogFactory.getLog(MethodCall.class); /** Which mode to use. */ protected short mode=OLD; /** Infer the method from the arguments. */ protected static final short OLD=1; /** Explicitly ship the method, caller has to determine method himself. */ protected static final short METHOD=2; /** Use class information. */ protected static final short TYPES=3; /** Provide a signature, similar to JMX. */ protected static final short SIGNATURE=4; /** Use an ID to map to a method */ protected static final short ID=5; /** * Creates an empty method call, this is always invalid, until * setName() has been called. */ public MethodCall() { } public MethodCall(Method method) { this(method, null); } public MethodCall(Method method, Object[] arguments) { init(method); if(arguments != null) args=arguments; } /** * * @param method_name * @param args * @deprecated Use one of the constructors that take class types as arguments */ public MethodCall(String method_name, Object[] args) { this.method_name=method_name; this.mode=OLD; this.args=args; } public MethodCall(short method_id, Object[] args) { this.method_id=method_id; this.mode=ID; this.args=args; } public MethodCall(String method_name, Object[] args, Class[] types) { this.method_name=method_name; this.args=args; this.types=types; this.mode=TYPES; } public MethodCall(String method_name, Object[] args, String[] signature) { this.method_name=method_name; this.args=args; this.signature=signature; this.mode=SIGNATURE; } private void init(Method method) { this.method=method; this.mode=METHOD; method_name=method.getName(); } public int getMode() { return mode; } /** * returns the name of the method to be invoked using this method call object * @return a case sensitive name, can be null for an invalid method call */ public String getName() { return method_name; } /** * sets the name for this MethodCall and allowing you to reuse the same object for * a different method invokation of a different method * @param n - a case sensitive method name */ public void setName(String n) { method_name=n; } public short getId() { return method_id; } public void setId(short method_id) { this.method_id=method_id; } /** * returns an ordered list of arguments used for the method invokation * @return returns the list of ordered arguments */ public Object[] getArgs() { return args; } public void setArgs(Object[] args) { if(args != null) this.args=args; } public Method getMethod() { return method; } public void setMethod(Method m) { init(m); } public synchronized Object put(Object key, Object value) { if(payload == null) payload=new HashMap(); return payload.put(key, value); } public synchronized Object get(Object key) { return payload != null? payload.get(key) : null; } /** * * @param target_class * @return * @throws Exception */ Method findMethod(Class target_class) throws Exception { int len=args != null? args.length : 0; Method m; Method[] methods=getAllMethods(target_class); for(int i=0; i < methods.length; i++) { m=methods[i]; if(m.getName().equals(method_name)) { if(m.getParameterTypes().length == len) return m; } } return null; } /** * The method walks up the class hierarchy and returns all methods of this class * and those inherited from superclasses and superinterfaces. */ Method[] getAllMethods(Class target) { Class superclass = target; List methods = new ArrayList(); int size = 0; while(superclass != null) { try { Method[] m = superclass.getDeclaredMethods(); methods.add(m); size += m.length; superclass = superclass.getSuperclass(); } catch(SecurityException e) { // if it runs in an applet context, it won't be able to retrieve // methods from superclasses that belong to the java VM and it will // raise a security exception, so we catch it here. if(log.isWarnEnabled()) log.warn("unable to enumerate methods of superclass "+superclass+" of class "+target); superclass=null; } } Method[] result = new Method[size]; int index = 0; for(Iterator i = methods.iterator(); i.hasNext();) { Method[] m = (Method[])i.next(); System.arraycopy(m, 0, result, index, m.length); index += m.length; } return result; } /** * Returns the first method that matches the specified name and parameter types. The overriding * methods have priority. The method is chosen from all the methods of the current class and all * its superclasses and superinterfaces. * * @return the matching method or null if no mathching method has been found. */ Method getMethod(Class target, String methodName, Class[] types) { if (types == null) { types = new Class[0]; } Method[] methods = getAllMethods(target); methods: for(int i = 0; i < methods.length; i++) { Method m = methods[i]; if (!methodName.equals(m.getName())) { continue; } Class[] parameters = m.getParameterTypes(); if (types.length != parameters.length) { continue; } for(int j = 0; j < types.length; j++) { if(!parameters[j].isAssignableFrom(types[j])) { // if (!types[j].equals(parameters[j])) { continue methods; } } return m; } return null; } /** * Invokes the method with the supplied arguments against the target object. * If a method lookup is provided, it will be used. Otherwise, the default * method lookup will be used. * @param target - the object that you want to invoke the method on * @return an object */ public Object invoke(Object target) throws Throwable { Class cl; Method meth=null; Object retval=null; if(method_name == null || target == null) { if(log.isErrorEnabled()) log.error("method name or target is null"); return null; } cl=target.getClass(); try { switch(mode) { case OLD: meth=findMethod(cl); break; case METHOD: if(this.method != null) meth=this.method; break; case TYPES: //meth=cl.getDeclaredMethod(method_name, types); meth = getMethod(cl, method_name, types); break; case SIGNATURE: Class[] mytypes=null; if(signature != null) mytypes=getTypesFromString(cl, signature); //meth=cl.getDeclaredMethod(method_name, mytypes); meth = getMethod(cl, method_name, mytypes); break; case ID: break; default: if(log.isErrorEnabled()) log.error("mode " + mode + " is invalid"); break; } if(meth != null) { retval=meth.invoke(target, args); } else { throw new NoSuchMethodException(method_name); } return retval; } catch(InvocationTargetException inv_ex) { throw inv_ex.getTargetException(); } catch(NoSuchMethodException no) { StringBuilder sb=new StringBuilder(); sb.append("found no method called ").append(method_name).append(" in class "); sb.append(cl.getName()).append(" with ("); if(args != null) { for(int i=0; i < args.length; i++) { if(i > 0) sb.append(", "); sb.append((args[i] != null)? args[i].getClass().getName() : "null"); } } sb.append(") formal parameters"); log.error(sb.toString()); throw no; } catch(Throwable e) { // e.printStackTrace(System.err); if(log.isErrorEnabled()) log.error("exception in invoke()", e); throw e; } } public Object invoke(Object target, Object[] args) throws Throwable { if(args != null) this.args=args; return invoke(target); } Class[] getTypesFromString(Class cl, String[] signature) throws Exception { String name; Class parameter; Class[] mytypes=new Class[signature.length]; for(int i=0; i < signature.length; i++) { name=signature[i]; if("long".equals(name)) parameter=long.class; else if("int".equals(name)) parameter=int.class; else if("short".equals(name)) parameter=short.class; else if("char".equals(name)) parameter=char.class; else if("byte".equals(name)) parameter=byte.class; else if("float".equals(name)) parameter=float.class; else if("double".equals(name)) parameter=double.class; else if("boolean".equals(name)) parameter=boolean.class; else parameter=Class.forName(name, false, cl.getClassLoader()); mytypes[i]=parameter; } return mytypes; } public String toString() { StringBuilder ret=new StringBuilder(); boolean first=true; if(method_name != null) ret.append(method_name); else ret.append(method_id); ret.append('('); if(args != null) { for(int i=0; i < args.length; i++) { if(first) first=false; else ret.append(", "); ret.append(args[i]); } } ret.append(')'); return ret.toString(); } public String toStringDetails() { StringBuilder ret=new StringBuilder(); ret.append("MethodCall "); if(method_name != null) ret.append("name=").append(method_name); else ret.append("id=").append(method_id); ret.append(", number of args=").append((args != null? args.length : 0)).append(')'); if(args != null) { ret.append("\nArgs:"); for(int i=0; i < args.length; i++) { ret.append("\n[").append(args[i]).append(" ("). append((args[i] != null? args[i].getClass().getName() : "null")).append(")]"); } } return ret.toString(); } public void writeExternal(ObjectOutput out) throws IOException { if(method_name != null) { out.writeBoolean(true); out.writeUTF(method_name); } else { out.writeBoolean(false); out.writeShort(method_id); } out.writeObject(args); out.writeShort(mode); switch(mode) { case OLD: break; case METHOD: out.writeObject(method.getParameterTypes()); out.writeObject(method.getDeclaringClass()); break; case TYPES: out.writeObject(types); break; case SIGNATURE: out.writeObject(signature); break; case ID: break; default: if(log.isErrorEnabled()) log.error("mode " + mode + " is invalid"); break; } if(payload != null) { out.writeBoolean(true); out.writeObject(payload); } else { out.writeBoolean(false); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { boolean name_available=in.readBoolean(); if(name_available) method_name=in.readUTF(); else method_id=in.readShort(); args=(Object[])in.readObject(); mode=in.readShort(); switch(mode) { case OLD: break; case METHOD: Class[] parametertypes=(Class[])in.readObject(); Class declaringclass=(Class)in.readObject(); try { method=declaringclass.getDeclaredMethod(method_name, parametertypes); } catch(NoSuchMethodException e) { throw new IOException(e.toString()); } break; case TYPES: types=(Class[])in.readObject(); break; case SIGNATURE: signature=(String[])in.readObject(); break; case ID: break; default: if(log.isErrorEnabled()) log.error("mode " + mode + " is invalid"); break; } boolean payload_available=in.readBoolean(); if(payload_available) { payload=(Map)in.readObject(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/0000755000175000017500000000000011621261110023377 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FD_ICMP.java0000644000175000017500000001475211366547366025406 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.lang.reflect.Method; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.Map; import java.util.Properties; /** * Protocol which uses InetAddress.isReachable() to check whether a given host is up or not, * taking 1 argument; the host name of the host to be pinged. * Note that this protocol only works with JDK 5 ! * The implementation of this may or may not use ICMP ! An alternative is to create a TCP connection to port 7 (echo service) * and see whether it works ! This is obviously done in JDK 5, so unless an echo service is configured to run, this * won't work... * @author Bela Ban * @version $Id: FD_ICMP.java,v 1.9 2007/08/27 08:09:19 belaban Exp $ */ public class FD_ICMP extends FD { /** network interface to be used to send the ICMP packets */ private NetworkInterface intf=null; private InetAddress bind_addr; private Method is_reacheable; /** Time-to-live for InetAddress.isReachable() */ private int ttl=32; public String getName() { return "FD_ICMP"; } public boolean setProperties(Properties props) { boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); String str=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", ignore_systemprops, null); if(str != null) { try { bind_addr=InetAddress.getByName(str); } catch(UnknownHostException unknown) { if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); return false; } props.remove("bind_addr"); } str=props.getProperty("ttl"); if(str != null) { ttl=Integer.parseInt(str); props.remove("ttl"); } super.setProperties(props); try { Class is_reacheable_class=Util.loadClass("java.net.InetAddress", this.getClass()); is_reacheable=is_reacheable_class.getMethod("isReachable", new Class[]{NetworkInterface.class, int.class, int.class}); } catch(ClassNotFoundException e) { // log.error("failed checking for InetAddress.isReachable() method - requires JDK 5 or higher"); Error error=new NoClassDefFoundError("failed checking for InetAddress.isReachable() method - requires JDK 5 or higher"); error.initCause(e); throw error; } catch(NoSuchMethodException e) { // log.error("didn't find InetAddress.isReachable() method - requires JDK 5 or higher"); Error error= new NoSuchMethodError("didn't find InetAddress.isReachable() method - requires JDK 5 or higher"); error.initCause(e); throw error; } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { super.init(); if(bind_addr != null) intf=NetworkInterface.getByInetAddress(bind_addr); } public Object up(Event evt) { switch(evt.getType()) { case Event.CONFIG: if(bind_addr == null) { Map config=(Map)evt.getArg(); bind_addr=(InetAddress)config.get("bind_addr"); } break; } return super.up(evt); } protected Monitor createMonitor() { return new FD_ICMP.PingMonitor(); } /** * Runs InetAddress.isReachable(). Each time the command fails, we increment num_tries. If num_tries > max_tries, we * emit a SUSPECT message. If ping_dest changes, or we do receive traffic from ping_dest, we reset num_tries to 0. */ protected class PingMonitor extends Monitor { long start, stop; public void run() { if(ping_dest == null) { if(log.isWarnEnabled()) log.warn("ping_dest is null: members=" + members + ", pingable_mbrs=" + pingable_mbrs + ", local_addr=" + local_addr); return; } // 1. execute ping command InetAddress host=ping_dest instanceof IpAddress? ((IpAddress)ping_dest).getIpAddress() : null; if(host == null) throw new IllegalArgumentException("ping_dest is not of type IpAddress - FD_ICMP only works with these"); try { if(log.isTraceEnabled()) log.trace("pinging " + host + " (ping_dest=" + ping_dest + ") using interface " + intf); start=System.currentTimeMillis(); Boolean rc=(Boolean)is_reacheable.invoke(host, new Object[]{intf, new Integer(ttl), new Integer((int)timeout)}); stop=System.currentTimeMillis(); num_heartbeats++; if(rc.booleanValue()) { // success num_tries=0; if(log.isTraceEnabled()) log.trace("successfully received response from " + host + " (after " + (stop-start) + "ms)"); } else { // failure num_tries++; if(log.isDebugEnabled()) log.debug("could not ping " + ping_dest + " (tries=" + num_tries + ") after " + (stop-start) + "ms)"); } if(num_tries >= max_tries) { if(log.isDebugEnabled()) log.debug("[" + local_addr + "]: could not ping " + ping_dest + " for " + (num_tries +1) + " times (" + ((num_tries+1) * timeout) + " milliseconds), suspecting it"); // broadcast a SUSPECT message to all members - loop until // unsuspect or view change is received bcast_task.addSuspectedMember(ping_dest); num_tries=0; if(stats) { num_suspect_events++; suspect_history.add(ping_dest); } } } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed pinging " + ping_dest, ex); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TRACE.java0000644000175000017500000000164711366547366025142 0ustar twernertwerner// $Id: TRACE.java,v 1.6 2007/07/30 12:56:38 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.stack.Protocol; public class TRACE extends Protocol { public TRACE() {} public String getName() {return "TRACE";} public Object up(Event evt) { System.out.println("---------------- TRACE (received) ----------------------"); System.out.println(evt); System.out.println("--------------------------------------------------------"); return up_prot.up(evt); } public Object down(Event evt) { System.out.println("------------------- TRACE (sent) -----------------------"); System.out.println(evt); System.out.println("--------------------------------------------------------"); return down_prot.down(evt); } public String toString() { return "Protocol TRACE"; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TcpHeader.java0000644000175000017500000000207311366547366026135 0ustar twernertwerner// $Id: TcpHeader.java,v 1.5 2007/05/01 10:55:10 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.util.Streamable; import java.io.*; public class TcpHeader extends Header implements Streamable { public String group_addr=null; public TcpHeader() { } // used for externalization public TcpHeader(String n) { group_addr=n; } public String toString() { return "[TCP:group_addr=" + group_addr + ']'; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(group_addr); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { group_addr=(String)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeUTF(group_addr); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { group_addr=in.readUTF(); } public int size() { return group_addr.length() +2; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/EXAMPLE.java0000644000175000017500000000427511366547366025377 0ustar twernertwerner// $Id: EXAMPLE.java,v 1.7 2007/01/12 14:19:38 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.stack.Protocol; import java.io.Serializable; import java.util.Vector; class ExampleHeader implements Serializable { private static final long serialVersionUID=-8802317525466899597L; // your variables ExampleHeader() { } public String toString() { return "[EXAMPLE: ]"; } } /** * Example of a protocol layer. Contains no real functionality, can be used as a template. */ public class EXAMPLE extends Protocol { final Vector members=new Vector(); /** * All protocol names have to be unique ! */ public String getName() { return "EXAMPLE"; } /** * Just remove if you don't need to reset any state */ public static void reset() { } public Object up(Event evt) { Message msg; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); // Do something with the event, e.g. extract the message and remove a header. // Optionally pass up break; } return up_prot.up(evt); // Pass up to the layer above us } public Object down(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: Vector new_members=((View)evt.getArg()).getMembers(); synchronized(members) { members.removeAllElements(); if(new_members != null && !new_members.isEmpty()) for(int i=0; i < new_members.size(); i++) members.addElement(new_members.elementAt(i)); } return down_prot.down(evt); case Event.MSG: Message msg=(Message)evt.getArg(); // Do something with the event, e.g. add a header to the message // Optionally pass down break; } return down_prot.down(evt); // Pass on to the layer below us } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/0000755000175000017500000000000011621261110024653 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/STREAMING_STATE_TRANSFER.java0000644000175000017500000012076211366547366031355 0ustar twernertwernerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.ShutdownRejectedExecutionHandler; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import org.jgroups.util.Digest; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * STREAMING_STATE_TRANSFER, as its name implies, allows a * streaming state transfer between two channel instances. *

* * Major advantage of this approach is that transferring application state to a * joining member of a group does not entail loading of the complete application * state into memory. Application state, for example, might be located entirely * on some form of disk based storage. The default STATE_TRANSFER * requires this state to be loaded entirely into memory before being * transferred to a group member while STREAMING_STATE_TRANSFER * does not. Thus STREAMING_STATE_TRANSFER protocol is able to * transfer application state that is very large (>1Gb) without a likelihood of * such transfer resulting in OutOfMemoryException. *

* * STREAMING_STATE_TRANSFER allows use of either default channel * transport or separate tcp sockets for state transfer. If firewalls are not a * concern then separate tcp sockets should be used as they offer faster state * transfer. Transport for state transfer is selected using * use_default_transport boolean property. *

* * * Channel instance can be configured with either * STREAMING_STATE_TRANSFER or STATE_TRANSFER but not * both protocols at the same time. * *

* * In order to process streaming state transfer an application has to implement * ExtendedMessageListener if it is using channel in a push style * mode or it has to process StreamingSetStateEvent and * StreamingGetStateEvent if it is using channel in a pull style * mode. * * * @author Vladimir Blagojevic * @see org.jgroups.ExtendedMessageListener * @see org.jgroups.StreamingGetStateEvent * @see org.jgroups.StreamingSetStateEvent * @see org.jgroups.protocols.pbcast.STATE_TRANSFER * @since 2.4 * * @version $Id$ * */ public class STREAMING_STATE_TRANSFER extends Protocol { private final static String NAME = "STREAMING_STATE_TRANSFER"; /* * ----------------------------- Properties --------------------------------------------------- */ /* * The interface (NIC) used to accept state requests */ private InetAddress bind_addr; /* * The port listening for state requests. Default value of 0 binds to any * (ephemeral) port */ private int bind_port = 0; /* * Maximum number of pool threads serving state requests. Default is 5 */ private int max_pool = 5; /* * Keep alive for pool threads serving state requests. Default is 20000 msec */ private long pool_thread_keep_alive = 20 * 1000; /* * Buffer size for state transfer. Default is 8 KB */ private int socket_buffer_size = 8 * 1024; /* * If default transport is used the total state buffer size before state producer is blocked. Default is 81920 bytes */ private int buffer_queue_size = 81920; /* * If true default transport will be used for state transfer rather than * seperate TCP sockets. Default is false. */ boolean use_default_transport = false; /* * --------------------------------------------- JMX statistics ------------------------------- */ private final AtomicInteger num_state_reqs = new AtomicInteger(0); private final AtomicLong num_bytes_sent = new AtomicLong(0); private volatile double avg_state_size = 0; /* * --------------------------------------------- Fields --------------------------------------- */ private Address local_addr = null; @GuardedBy("members") private final Vector

members; /* * BlockingQueue to accept state transfer Message(s) if default transport * is used. Only state recipient uses this queue */ private BlockingQueue stateQueue; /* * Runnable that listens for state requests and spawns threads to serve * those requests if socket transport is used */ private StateProviderThreadSpawner spawner; /* * Set to true if FLUSH protocol is detected in protocol stack */ private AtomicBoolean flushProtocolInStack = new AtomicBoolean(false); public STREAMING_STATE_TRANSFER() { members = new Vector
(); } public final String getName() { return NAME; } public int getNumberOfStateRequests() { return num_state_reqs.get(); } public long getNumberOfStateBytesSent() { return num_bytes_sent.get(); } public double getAverageStateSize() { return avg_state_size; } public Vector requiredDownServices() { Vector retval = new Vector(); retval.addElement(new Integer(Event.GET_DIGEST)); retval.addElement(new Integer(Event.SET_DIGEST)); return retval; } public void resetStats() { super.resetStats(); num_state_reqs.set(0); num_bytes_sent.set(0); avg_state_size = 0; } public boolean setProperties(Properties props) { super.setProperties(props); String str = props.getProperty("use_flush"); if (str != null) { log.warn("use_flush has been deprecated and its value will be ignored"); props.remove("use_flush"); } str = props.getProperty("flush_timeout"); if (str != null) { log.warn("flush_timeout has been deprecated and its value will be ignored"); props.remove("flush_timeout"); } str = props.getProperty("use_reading_thread"); if (str != null) { log.warn("use_reading_thread has been deprecated and its value will be ignored"); props.remove("use_reading_thread"); } try { bind_addr = Util.parseBindAddress(props, "bind_addr"); } catch (UnknownHostException e) { log.error("(bind_addr): host " + e.getLocalizedMessage() + " not known"); return false; } use_default_transport = Util.parseBoolean(props, "use_default_transport", false); buffer_queue_size = Util.parseInt(props, "buffer_queue_size", 81920); bind_port = Util.parseInt(props, "start_port", 0); socket_buffer_size = Util.parseInt(props, "socket_buffer_size", 8 * 1024); // 8K max_pool = Util.parseInt(props, "max_pool", 5); pool_thread_keep_alive = Util.parseLong(props, "pool_thread_keep_alive", 1000 * 30); // 30sec if (!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { } public void start() throws Exception { Map map = new HashMap(); map.put("state_transfer", Boolean.TRUE); map.put("protocol_class", getClass().getName()); up_prot.up(new Event(Event.CONFIG, map)); if (use_default_transport) { int size = buffer_queue_size / socket_buffer_size; // idiot proof it if (size <= 1) { size = 10; } if (log.isDebugEnabled()) { log.debug("buffer_queue_size=" + buffer_queue_size + ", socket_buffer_size=" + socket_buffer_size + ", creating queue of size " + size); } stateQueue = new ArrayBlockingQueue(size); } } public void stop() { super.stop(); if (spawner != null) { spawner.stop(); } } public Object up(Event evt) { switch (evt.getType()) { case Event.MSG : Message msg = (Message) evt.getArg(); StateHeader hdr = (StateHeader) msg.getHeader(getName()); if (hdr != null) { switch (hdr.type) { case StateHeader.STATE_REQ : handleStateReq(hdr); break; case StateHeader.STATE_RSP : // fix for https://jira.jboss.org/jira/browse/JGRP-1013 if(isDigestNeeded()) down_prot.down(new Event(Event.CLOSE_BARRIER)); try { handleStateRsp(hdr); } finally { if(isDigestNeeded()) down_prot.down(new Event(Event.OPEN_BARRIER)); } break; case StateHeader.STATE_PART : try { stateQueue.put(msg); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } break; default : if (log.isErrorEnabled()) log.error("type " + hdr.type + " not known in StateHeader"); break; } return null; } break; case Event.SET_LOCAL_ADDRESS : local_addr = (Address) evt.getArg(); break; case Event.TMP_VIEW : case Event.VIEW_CHANGE : handleViewChange((View) evt.getArg()); break; case Event.CONFIG : Map config = (Map) evt.getArg(); if (bind_addr == null && (config != null && config.containsKey("bind_addr"))) { bind_addr = (InetAddress) config.get("bind_addr"); if (log.isDebugEnabled()) log.debug("using bind_addr from CONFIG event " + bind_addr); } if (config != null && config.containsKey("state_transfer")) { log.error("Protocol stack must have only one state transfer protocol"); } break; } return up_prot.up(evt); } public Object down(Event evt) { switch (evt.getType()) { case Event.TMP_VIEW : case Event.VIEW_CHANGE : handleViewChange((View) evt.getArg()); break; case Event.GET_STATE : StateTransferInfo info = (StateTransferInfo) evt.getArg(); Address target; if (info.target == null) { target = determineCoordinator(); } else { target = info.target; if (target.equals(local_addr)) { if (log.isErrorEnabled()) log.error("GET_STATE: cannot fetch state from myself !"); target = null; } } if (target == null) { if (log.isDebugEnabled()) log.debug("GET_STATE: first member (no state)"); up_prot.up(new Event(Event.GET_STATE_OK, new StateTransferInfo())); } else { Message state_req = new Message(target, null, null); state_req.putHeader(getName(), new StateHeader(StateHeader.STATE_REQ, local_addr, info.state_id)); if (log.isDebugEnabled()) log.debug("GET_STATE: asking " + target + " for state, passing down a SUSPEND_STABLE event, timeout=" + info.timeout); down_prot.down(new Event(Event.SUSPEND_STABLE, new Long(info.timeout))); down_prot.down(new Event(Event.MSG, state_req)); } return null; // don't pass down any further ! case Event.STATE_TRANSFER_INPUTSTREAM_CLOSED : if (log.isDebugEnabled()) log.debug("STATE_TRANSFER_INPUTSTREAM_CLOSED received,passing down a RESUME_STABLE event"); down_prot.down(new Event(Event.RESUME_STABLE)); return null; case Event.CONFIG : Map config = (Map) evt.getArg(); if (config != null && config.containsKey("flush_supported")) { flushProtocolInStack.set(true); } break; } return down_prot.down(evt); // pass on to the layer below us } /* * --------------------------- Private Methods ------------------------------------------------ */ /** * When FLUSH is used we do not need to pass digests between members * * see JGroups/doc/design/PArtialStateTransfer.txt see * JGroups/doc/design/FLUSH.txt * * @return true if use of digests is required, false otherwise */ private boolean isDigestNeeded() { return !flushProtocolInStack.get(); } private void respondToStateRequester(String id, Address stateRequester, boolean open_barrier) throws IOException { // setup the plumbing if needed if (spawner == null) { ServerSocket serverSocket = Util.createServerSocket(bind_addr, bind_port); spawner = new StateProviderThreadSpawner(setupThreadPool(), serverSocket); Thread t = getThreadFactory().newThread(spawner, "STREAMING_STATE_TRANSFER server socket acceptor"); t.start(); } Digest digest = isDigestNeeded() ? (Digest) down_prot.down(Event.GET_DIGEST_EVT) : null; Message state_rsp = new Message(stateRequester); StateHeader hdr = new StateHeader(StateHeader.STATE_RSP, local_addr, use_default_transport ? null : spawner.getServerSocketAddress(), digest, id); state_rsp.putHeader(getName(), hdr); if (log.isDebugEnabled()) log.debug("Responding to state requester " + state_rsp.getDest() + " with address " + (use_default_transport ? null : spawner.getServerSocketAddress()) + " and digest " + digest); down_prot.down(new Event(Event.MSG, state_rsp)); if (stats) { num_state_reqs.incrementAndGet(); } if (open_barrier) down_prot.down(new Event(Event.OPEN_BARRIER)); if (use_default_transport) { openAndProvideOutputStreamToStateProvider(stateRequester, id); } } private ThreadPoolExecutor setupThreadPool() { ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, max_pool, pool_thread_keep_alive, TimeUnit.MILLISECONDS, new SynchronousQueue()); ThreadFactory factory = new ThreadFactory() { public Thread newThread(final Runnable command) { return getThreadFactory().newThread(command, "STREAMING_STATE_TRANSFER sender"); } }; threadPool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(threadPool .getRejectedExecutionHandler())); threadPool.setThreadFactory(factory); return threadPool; } private Address determineCoordinator() { synchronized (members) { for (Address member : members) { if (!local_addr.equals(member)) { return member; } } } return null; } private void handleViewChange(View v) { Vector
new_members = v.getMembers(); synchronized (members) { members.clear(); members.addAll(new_members); } } private void handleStateReq(StateHeader hdr) { Address sender = hdr.sender; String id = hdr.state_id; if (sender == null) { if (log.isErrorEnabled()) log.error("sender is null !"); return; } if (isDigestNeeded()) { down_prot.down(new Event(Event.CLOSE_BARRIER)); // drain (and block) incoming msgs until after state has been // returned } try { respondToStateRequester(id, sender, isDigestNeeded()); } catch (Throwable t) { if (log.isErrorEnabled()) log.error("failed fetching state from application", t); if (isDigestNeeded()) down_prot.down(new Event(Event.OPEN_BARRIER)); } } void handleStateRsp(final StateHeader hdr) { Digest tmp_digest = hdr.my_digest; if (isDigestNeeded()) { if (tmp_digest == null) { if (log.isWarnEnabled()) log.warn("digest received from " + hdr.sender + " is null, skipping setting digest !"); } else { down_prot.down(new Event(Event.SET_DIGEST, tmp_digest)); } } if (use_default_transport) { // have to use another thread to read state while state recipient // has to accept state messages from state provider Thread t = getThreadFactory().newThread(new Runnable() { public void run() { openAndProvideInputStreamToStateReceiver(hdr.sender, hdr.getStateId()); } }, "STREAMING_STATE_TRANSFER state reader"); t.start(); } else { connectToStateProvider(hdr); } } private void openAndProvideInputStreamToStateReceiver(Address stateProvider, String state_id) { BufferedInputStream bis = null; try { bis = new BufferedInputStream(new StateInputStream(), socket_buffer_size); up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, new StateTransferInfo( stateProvider, bis, state_id))); } catch (IOException e) { // pass null stream up so that JChannel.getState() returns false log.error("Could not provide state recipient with appropriate stream", e); InputStream is = null; up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, new StateTransferInfo( stateProvider, is, state_id))); } finally { Util.close(bis); } } private void openAndProvideOutputStreamToStateProvider(Address stateRequester, String state_id) { BufferedOutputStream bos = null; try { bos = new BufferedOutputStream(new StateOutputStream(stateRequester, state_id),socket_buffer_size); up_prot.up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM, new StateTransferInfo( stateRequester, bos, state_id))); } catch (IOException e) { if (log.isWarnEnabled()) { log.warn("StateOutputStream could not be given to application", e); } } finally { Util.close(bos); } } private void connectToStateProvider(StateHeader hdr) { IpAddress address = hdr.bind_addr; String tmp_state_id = hdr.getStateId(); StreamingInputStreamWrapper wrapper = null; StateTransferInfo sti = null; Socket socket = new Socket(); try { socket.bind(new InetSocketAddress(bind_addr, 0)); int bufferSize = socket.getReceiveBufferSize(); socket.setReceiveBufferSize(socket_buffer_size); if (log.isDebugEnabled()) log.debug("Connecting to state provider " + address.getIpAddress() + ":" + address.getPort() + ", original buffer size was " + bufferSize + " and was reset to " + socket.getReceiveBufferSize()); socket.connect(new InetSocketAddress(address.getIpAddress(), address.getPort())); if (log.isDebugEnabled()) log.debug("Connected to state provider, my end of the socket is " + socket.getLocalAddress() + ":" + socket.getLocalPort() + " passing inputstream up..."); // write out our state_id and address ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); out.writeObject(tmp_state_id); out.writeObject(local_addr); wrapper = new StreamingInputStreamWrapper(socket); sti = new StateTransferInfo(hdr.sender, wrapper, tmp_state_id); up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, sti)); } catch (IOException e) { if (log.isWarnEnabled()) { log.warn("State reader socket thread spawned abnormaly", e); } // pass null stream up so that JChannel.getState() returns false InputStream is = null; sti = new StateTransferInfo(hdr.sender, is, tmp_state_id); up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM, sti)); } finally { if (!socket.isConnected()) { if (log.isWarnEnabled()) log.warn("Could not connect to state provider. Closing socket..."); } Util.close(wrapper); Util.close(socket); } } /* * ------------------------ End of Private Methods -------------------------------------------- */ private class StateProviderThreadSpawner implements Runnable { private final ExecutorService pool; private final ServerSocket serverSocket; private final IpAddress address; Thread runner; private volatile boolean running = true; public StateProviderThreadSpawner(ExecutorService pool, ServerSocket stateServingSocket) { super(); this.pool = pool; this.serverSocket = stateServingSocket; this.address = new IpAddress(STREAMING_STATE_TRANSFER.this.bind_addr, serverSocket .getLocalPort()); } public void run() { runner = Thread.currentThread(); for (; running;) { try { if (log.isDebugEnabled()) log.debug("StateProviderThreadSpawner listening at " + getServerSocketAddress() + "..."); final Socket socket = serverSocket.accept(); pool.execute(new Runnable() { public void run() { if (log.isDebugEnabled()) log.debug("Accepted request for state transfer from " + socket.getInetAddress() + ":" + socket.getPort() + " handing of to PooledExecutor thread"); new StateProviderHandler().process(socket); } }); } catch (IOException e) { if (log.isWarnEnabled()) { // we get this exception when we close server socket // exclude that case if (serverSocket != null && !serverSocket.isClosed()) { log.warn("Spawning socket from server socket finished abnormaly", e); } } } } } public IpAddress getServerSocketAddress() { return address; } public void stop() { running = false; try { serverSocket.close(); } catch (Exception ignored) { } finally { if (log.isDebugEnabled()) log.debug("Waiting for StateProviderThreadSpawner to die ... "); if (runner != null) { try { runner.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } } if (log.isDebugEnabled()) log.debug("Shutting the thread pool down... "); pool.shutdownNow(); try { pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } } if (log.isDebugEnabled()) log.debug("Thread pool is shutdown. All pool threads are cleaned up."); } } private class StateProviderHandler { public void process(Socket socket) { StreamingOutputStreamWrapper wrapper = null; ObjectInputStream ois = null; try { int bufferSize = socket.getSendBufferSize(); socket.setSendBufferSize(socket_buffer_size); if (log.isDebugEnabled()) log.debug("Running on " + Thread.currentThread() + ". Accepted request for state transfer from " + socket.getInetAddress() + ":" + socket.getPort() + ", original buffer size was " + bufferSize + " and was reset to " + socket.getSendBufferSize() + ", passing outputstream up... "); ois = new ObjectInputStream(socket.getInputStream()); String state_id = (String) ois.readObject(); Address stateRequester = (Address) ois.readObject(); wrapper = new StreamingOutputStreamWrapper(socket); StateTransferInfo sti = new StateTransferInfo(stateRequester, wrapper, state_id); up_prot.up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM, sti)); } catch (IOException e) { if (log.isWarnEnabled()) { log.warn("State writer socket thread spawned abnormaly", e); } } catch (ClassNotFoundException e) { // thrown by ois.readObject() // should never happen since String/Address are core classes } finally { if (!socket.isConnected()) { if (log.isWarnEnabled()) log.warn("Could not receive connection from state receiver. Closing socket..."); } Util.close(wrapper); Util.close(socket); } } } private class StreamingInputStreamWrapper extends InputStream { private final InputStream delegate; private final Socket inputStreamOwner; private final AtomicBoolean closed = new AtomicBoolean(false); public StreamingInputStreamWrapper(Socket inputStreamOwner) throws IOException { super(); this.inputStreamOwner = inputStreamOwner; this.delegate = new BufferedInputStream(inputStreamOwner.getInputStream()); } public int available() throws IOException { return delegate.available(); } public void close() throws IOException { if (closed.compareAndSet(false, true)) { if (log.isDebugEnabled()) { log.debug("State reader is closing the socket "); } Util.close(delegate); Util.close(inputStreamOwner); up_prot.up(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); down(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); } } public synchronized void mark(int readlimit) { delegate.mark(readlimit); } public boolean markSupported() { return delegate.markSupported(); } public int read() throws IOException { return delegate.read(); } public int read(byte[] b, int off, int len) throws IOException { return delegate.read(b, off, len); } public int read(byte[] b) throws IOException { return delegate.read(b); } public synchronized void reset() throws IOException { delegate.reset(); } public long skip(long n) throws IOException { return delegate.skip(n); } } private class StreamingOutputStreamWrapper extends OutputStream { private final Socket outputStreamOwner; private final OutputStream delegate; private final AtomicBoolean closed = new AtomicBoolean(false); private long bytesWrittenCounter = 0; public StreamingOutputStreamWrapper(Socket outputStreamOwner) throws IOException { super(); this.outputStreamOwner = outputStreamOwner; this.delegate = new BufferedOutputStream(outputStreamOwner.getOutputStream()); } public void close() throws IOException { if (closed.compareAndSet(false, true)) { if (log.isDebugEnabled()) { log.debug("State writer is closing the socket "); } Util.close(delegate); Util.close(outputStreamOwner); up_prot.up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); down(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); if (stats) { avg_state_size = num_bytes_sent.addAndGet(bytesWrittenCounter) / num_state_reqs.doubleValue(); } } } public void flush() throws IOException { delegate.flush(); } public void write(byte[] b, int off, int len) throws IOException { delegate.write(b, off, len); bytesWrittenCounter += len; } public void write(byte[] b) throws IOException { delegate.write(b); if (b != null) { bytesWrittenCounter += b.length; } } public void write(int b) throws IOException { delegate.write(b); bytesWrittenCounter += 1; } } private class StateInputStream extends InputStream { private final AtomicBoolean closed; public StateInputStream() throws IOException { super(); this.closed = new AtomicBoolean(false); } public void close() throws IOException { if (closed.compareAndSet(false, true)) { if (log.isDebugEnabled()) { log.debug("State reader is closing the stream"); } stateQueue.clear(); up(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); down(new Event(Event.STATE_TRANSFER_INPUTSTREAM_CLOSED)); super.close(); } } public int read() throws IOException { if (closed.get()) return -1; final byte[] array = new byte[1]; return read(array) == -1 ? -1 : array[0]; } public int read(byte[] b, int off, int len) throws IOException { if (closed.get()) return -1; Message m = null; try { m = stateQueue.take(); StateHeader hdr = (StateHeader) m.getHeader(getName()); if (hdr.type == StateHeader.STATE_PART) { return readAndTransferPayload(m, b, off, len); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new InterruptedIOException(); } return -1; } private int readAndTransferPayload(Message m, byte[] b, int off, int len) { byte[] buffer = m.getBuffer(); if (log.isDebugEnabled()) { log.debug(local_addr + " reading chunk of state " + "byte[] b=" + b.length + ", off=" + off + ", buffer.length=" + buffer.length); } System.arraycopy(buffer, 0, b, off, buffer.length); return buffer.length; } public int read(byte[] b) throws IOException { if (closed.get()) return -1; return read(b, 0, b.length); } } private class StateOutputStream extends OutputStream { private final Address stateRequester; private final String state_id; private final AtomicBoolean closed; private long bytesWrittenCounter = 0; public StateOutputStream(Address stateRequester, String state_id) throws IOException { super(); this.stateRequester = stateRequester; this.state_id = state_id; this.closed = new AtomicBoolean(false); } public void close() throws IOException { if (closed.compareAndSet(false, true)) { if (log.isDebugEnabled()) { log.debug("State writer " + local_addr + " is closing the output stream for state_id " + state_id); } up(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); down(new Event(Event.STATE_TRANSFER_OUTPUTSTREAM_CLOSED)); if (stats) { avg_state_size = num_bytes_sent.addAndGet(bytesWrittenCounter) / num_state_reqs.doubleValue(); } super.close(); } } public void write(byte[] b, int off, int len) throws IOException { if (closed.get()) throw closed(); sendMessage(b, off, len); } public void write(byte[] b) throws IOException { if (closed.get()) throw closed(); sendMessage(b, 0, b.length); } public void write(int b) throws IOException { if (closed.get()) throw closed(); byte buf[] = new byte[]{(byte) b}; write(buf); } private void sendMessage(byte[] b, int off, int len) throws IOException { Message m = new Message(stateRequester); m.putHeader(getName(), new StateHeader(StateHeader.STATE_PART, local_addr, state_id)); m.setBuffer(b, off, len); bytesWrittenCounter += (len - off); if (Thread.interrupted()) { throw interrupted((int) bytesWrittenCounter); } down_prot.down(new Event(Event.MSG, m)); if (log.isDebugEnabled()) { log.debug(local_addr + " sent chunk of state to " + stateRequester + "byte[] b=" + b.length + ", off=" + off + ", len=" + len); } } private IOException closed() { return new IOException("The output stream is closed"); } private InterruptedIOException interrupted(int cnt) { final InterruptedIOException ex = new InterruptedIOException(); ex.bytesTransferred = cnt; return ex; } } public static class StateHeader extends Header implements Streamable { public static final byte STATE_REQ = 1; public static final byte STATE_RSP = 2; public static final byte STATE_PART = 3; long id = 0; // state transfer ID (to separate multiple state transfers // at the same time) byte type = 0; Address sender; // sender of state STATE_REQ or STATE_RSP Digest my_digest = null; // digest of sender (if type is STATE_RSP) IpAddress bind_addr = null; String state_id = null; // for partial state transfer public StateHeader() { } // for externalization public StateHeader(byte type, Address sender, String state_id) { this.type = type; this.sender = sender; this.state_id = state_id; } public StateHeader(byte type, Address sender, long id, Digest digest) { this.type = type; this.sender = sender; this.id = id; this.my_digest = digest; } public StateHeader(byte type, Address sender, IpAddress bind_addr, Digest digest, String state_id) { this.type = type; this.sender = sender; this.my_digest = digest; this.bind_addr = bind_addr; this.state_id = state_id; } public int getType() { return type; } public Digest getDigest() { return my_digest; } public String getStateId() { return state_id; } public boolean equals(Object o) { StateHeader other; if (sender != null && o != null) { if (!(o instanceof StateHeader)) return false; other = (StateHeader) o; return sender.equals(other.sender) && id == other.id; } return false; } public int hashCode() { if (sender != null) return sender.hashCode() + (int) id; else return (int) id; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("type=").append(type2Str(type)); if (sender != null) sb.append(", sender=").append(sender).append(" id=").append(id); if (my_digest != null) sb.append(", digest=").append(my_digest); return sb.toString(); } static String type2Str(int t) { switch (t) { case STATE_REQ : return "STATE_REQ"; case STATE_RSP : return "STATE_RSP"; case STATE_PART : return "STATE_PART"; default : return ""; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(sender); out.writeLong(id); out.writeByte(type); out.writeObject(my_digest); out.writeObject(bind_addr); out.writeUTF(state_id); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { sender = (Address) in.readObject(); id = in.readLong(); type = in.readByte(); my_digest = (Digest) in.readObject(); bind_addr = (IpAddress) in.readObject(); state_id = in.readUTF(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(id); Util.writeAddress(sender, out); Util.writeStreamable(my_digest, out); Util.writeStreamable(bind_addr, out); Util.writeString(state_id, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type = in.readByte(); id = in.readLong(); sender = Util.readAddress(in); my_digest = (Digest) Util.readStreamable(Digest.class, in); bind_addr = (IpAddress) Util.readStreamable(IpAddress.class, in); state_id = Util.readString(in); } public int size() { int retval = Global.LONG_SIZE + Global.BYTE_SIZE; // id and type retval += Util.size(sender); retval += Global.BYTE_SIZE; // presence byte for my_digest if (my_digest != null) retval += my_digest.serializedSize(); retval += Util.size(bind_addr); retval += Global.BYTE_SIZE; // presence byte for state_id if (state_id != null) retval += state_id.length() + 2; return retval; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/STATE_TRANSFER.java0000644000175000017500000005264611366547366027751 0ustar twernertwerner package org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import org.jgroups.util.Digest; import java.io.*; import java.util.*; /** * New STATE_TRANSFER protocol based on PBCAST. Compared to the one in * ./protocols, it doesn't need a QUEUE layer above it. A state request is sent * to a chosen member (coordinator if null). That member makes a copy D of its * current digest and asks the application for a copy of its current state S. * Then the member returns both S and D to the requester. The requester first * sets its digest to D and then returns the state to the application. * * @author Bela Ban * @version $Id: STATE_TRANSFER.java,v 1.76.2.2 2009/07/20 14:52:07 belaban Exp $ */ public class STATE_TRANSFER extends Protocol { Address local_addr=null; final Vector
members=new Vector
(); long state_id=1; // used to differentiate between state transfers (not currently used) /** Map of state requesters. Keys are state IDs, values are Sets of Addresses (one for each requester) */ final Map> state_requesters=new HashMap>(); /** set to true while waiting for a STATE_RSP */ boolean waiting_for_state_response=false; final Map map=new HashMap(); // to store configuration information long start, stop; // to measure state transfer time int num_state_reqs=0; long num_bytes_sent=0; double avg_state_size=0; final static String name="STATE_TRANSFER"; boolean flushProtocolInStack = false; /** All protocol names have to be unique ! */ public String getName() { return name; } public int getNumberOfStateRequests() {return num_state_reqs;} public long getNumberOfStateBytesSent() {return num_bytes_sent;} public double getAverageStateSize() {return avg_state_size;} public Vector requiredDownServices() { Vector retval=new Vector(); retval.addElement(new Integer(Event.GET_DIGEST)); retval.addElement(new Integer(Event.SET_DIGEST)); return retval; } public void resetStats() { super.resetStats(); num_state_reqs=0; num_bytes_sent=0; avg_state_size=0; } public boolean setProperties(Properties props) { super.setProperties(props); String str=props.getProperty("use_flush"); if(str != null) { log.warn("use_flush has been deprecated and its value will be ignored"); props.remove("use_flush"); } str=props.getProperty("flush_timeout"); if(str != null) { log.warn("flush_timeout has been deprecated and its value will be ignored"); props.remove("flush_timeout"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { map.put("state_transfer", Boolean.TRUE); map.put("protocol_class", getClass().getName()); } public void start() throws Exception { up_prot.up(new Event(Event.CONFIG, map)); } public void stop() { super.stop(); waiting_for_state_response=false; } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); StateHeader hdr=(StateHeader)msg.getHeader(name); if(hdr == null) break; switch(hdr.type) { case StateHeader.STATE_REQ: handleStateReq(hdr); break; case StateHeader.STATE_RSP: // fix for https://jira.jboss.org/jira/browse/JGRP-1013 if(isDigestNeeded()) down_prot.down(new Event(Event.CLOSE_BARRIER)); try { handleStateRsp(hdr, msg.getBuffer()); } finally { if(isDigestNeeded()) down_prot.down(new Event(Event.OPEN_BARRIER)); } break; default: if(log.isErrorEnabled()) log.error("type " + hdr.type + " not known in StateHeader"); break; } return null; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.TMP_VIEW: case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; case Event.CONFIG : Map config=(Map)evt.getArg(); if(config != null && config.containsKey("state_transfer")) { log.error("Protocol stack cannot contain two state transfer protocols. Remove either one of them"); } break; } return up_prot.up(evt); } public Object down(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; // generated by JChannel.getState(). currently, getting the state from more than 1 mbr is not implemented case Event.GET_STATE: Address target; StateTransferInfo info=(StateTransferInfo)evt.getArg(); if(info.target == null) { target=determineCoordinator(); } else { target=info.target; if(target.equals(local_addr)) { if(log.isErrorEnabled()) log.error("GET_STATE: cannot fetch state from myself !"); target=null; } } if(target == null) { if(log.isDebugEnabled()) log.debug("GET_STATE: first member (no state)"); up_prot.up(new Event(Event.GET_STATE_OK, new StateTransferInfo())); } else { Message state_req=new Message(target, null, null); state_req.putHeader(name, new StateHeader(StateHeader.STATE_REQ, local_addr, state_id++, null, info.state_id)); if(log.isDebugEnabled()) log.debug("GET_STATE: asking " + target + " for state"); // suspend sending and handling of message garbage collection gossip messages, // fixes bugs #943480 and #938584). Wake up when state has been received if(log.isDebugEnabled()) log.debug("passing down a SUSPEND_STABLE event"); down_prot.down(new Event(Event.SUSPEND_STABLE, new Long(info.timeout))); waiting_for_state_response=true; start=System.currentTimeMillis(); down_prot.down(new Event(Event.MSG, state_req)); } return null; // don't pass down any further ! case Event.CONFIG : Map config = (Map) evt.getArg(); if(config != null && config.containsKey("flush_supported")){ flushProtocolInStack = true; } break; } return down_prot.down(evt); // pass on to the layer below us } /* --------------------------- Private Methods -------------------------------- */ /** * When FLUSH is used we do not need to pass digests between members * * see JGroups/doc/design/PartialStateTransfer.txt see * JGroups/doc/design/FLUSH.txt * * @return true if use of digests is required, false otherwise */ private boolean isDigestNeeded() { return !flushProtocolInStack; } private void requestApplicationStates(Address requester, Digest digest, boolean open_barrier) { Set appl_ids=new HashSet(state_requesters.keySet()); List responses=new LinkedList(); for(Iterator it=appl_ids.iterator(); it.hasNext();) { String id=it.next(); StateTransferInfo info=new StateTransferInfo(requester, id, 0L, null); StateTransferInfo rsp=(StateTransferInfo)up_prot.up(new Event(Event.GET_APPLSTATE, info)); responses.add(rsp); } if(open_barrier) down_prot.down(new Event(Event.OPEN_BARRIER)); for(StateTransferInfo rsp: responses) { sendApplicationStateResponse(rsp, digest); } } private void sendApplicationStateResponse(StateTransferInfo rsp, Digest digest) { byte[] state=rsp.state; String id=rsp.state_id; List responses=null; synchronized(state_requesters) { if(state_requesters.isEmpty()) { if(log.isWarnEnabled()) log.warn("GET_APPLSTATE_OK: received application state, but there are no requesters !"); return; } if(stats) { num_state_reqs++; if(state != null) num_bytes_sent+=state.length; avg_state_size=num_bytes_sent / num_state_reqs; } Set
requesters=state_requesters.get(id); if(requesters == null || requesters.isEmpty()) { log.warn("received state for id=" + id + ", but there are no requesters for this ID"); } else { responses=new LinkedList(); for(Iterator
it=requesters.iterator(); it.hasNext();) { Address requester=it.next(); Message state_rsp=new Message(requester, null, state); StateHeader hdr=new StateHeader(StateHeader.STATE_RSP, local_addr, 0, digest, id); state_rsp.putHeader(name, hdr); responses.add(state_rsp); } state_requesters.remove(id); } } if(responses != null && !responses.isEmpty()) { for(Message state_rsp: responses) { if(log.isTraceEnabled()) { int length=state != null? state.length : 0; log.trace("sending state for ID=" + id + " to " + state_rsp.getDest() + " (" + length + " bytes)"); } down_prot.down(new Event(Event.MSG, state_rsp)); // This has to be done in a separate thread, so we don't block on FC // (see http://jira.jboss.com/jira/browse/JGRP-225 for details). This will be reverted once // we have the threadless stack (http://jira.jboss.com/jira/browse/JGRP-181) // and out-of-band messages (http://jira.jboss.com/jira/browse/JGRP-205) // new Thread() { // public void run() { // down_prot.down(new Event(Event.MSG, state_rsp)); // } // }.start(); // down_prot.down(new Event(Event.MSG, state_rsp)); } } } /** Return the first element of members which is not me. Otherwise return null. */ private Address determineCoordinator() { Address ret=null; synchronized(members) { if(members != null && members.size() > 1) { for(int i=0; i < members.size(); i++) if(!local_addr.equals(members.elementAt(i))) return (Address)members.elementAt(i); } } return ret; } private void handleViewChange(View v) { Address old_coord; Vector
new_members=v.getMembers(); boolean send_up_null_state_rsp=false; synchronized(members) { old_coord=(!members.isEmpty()? members.firstElement() : null); members.clear(); members.addAll(new_members); // this handles the case where a coord dies during a state transfer; prevents clients from hanging forever // Note this only takes a coordinator crash into account, a getState(target, timeout), where target is not // null is not handled ! (Usually we get the state from the coordinator) // http://jira.jboss.com/jira/browse/JGRP-148 if(waiting_for_state_response && old_coord != null && !members.contains(old_coord)) { send_up_null_state_rsp=true; } } if(send_up_null_state_rsp) { if(log.isWarnEnabled()) log.warn("discovered that the state provider (" + old_coord + ") crashed; will return null state to application"); StateHeader hdr=new StateHeader(StateHeader.STATE_RSP, local_addr, 0, null, null); handleStateRsp(hdr, null); // sends up null GET_STATE_OK } } /** * If a state transfer is in progress, we don't need to send a GET_APPLSTATE event to the application, but * instead we just add the sender to the requester list so it will receive the same state when done. If not, * we add the sender to the requester list and send a GET_APPLSTATE event up. */ private void handleStateReq(StateHeader hdr) { Address sender=hdr.sender; if(sender == null) { if(log.isErrorEnabled()) log.error("sender is null !"); return; } String id=hdr.state_id; // id could be null, which means get the entire state synchronized(state_requesters) { boolean empty=state_requesters.isEmpty(); Set
requesters=state_requesters.get(id); if(requesters == null) { requesters=new HashSet
(); state_requesters.put(id, requesters); } requesters.add(sender); if(!isDigestNeeded()) { // state transfer is in progress, digest was already requested requestApplicationStates(sender, null, false); } else if(empty) { if(!flushProtocolInStack) { down_prot.down(new Event(Event.CLOSE_BARRIER)); } Digest digest=(Digest)down_prot.down(new Event(Event.GET_DIGEST)); if(log.isDebugEnabled()) log.debug("digest is " + digest + ", getting application state"); try { requestApplicationStates(sender, digest, !flushProtocolInStack); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed getting state from application", t); if(!flushProtocolInStack) { down_prot.down(new Event(Event.OPEN_BARRIER)); } } } } } /** Set the digest and the send the state up to the application */ void handleStateRsp(StateHeader hdr, byte[] state) { Address sender=hdr.sender; Digest tmp_digest=hdr.my_digest; String id=hdr.state_id; Address state_sender=hdr.sender; waiting_for_state_response=false; if(isDigestNeeded()){ if(tmp_digest == null) { if(log.isWarnEnabled()) log.warn("digest received from " + sender + " is null, skipping setting digest !"); } else down_prot.down(new Event(Event.SET_DIGEST, tmp_digest)); // set the digest (e.g. in NAKACK) } stop=System.currentTimeMillis(); // resume sending and handling of message garbage collection gossip messages, // fixes bugs #943480 and #938584). Wakes up a previously suspended message garbage // collection protocol (e.g. STABLE) if(log.isDebugEnabled()) log.debug("passing down a RESUME_STABLE event"); down_prot.down(new Event(Event.RESUME_STABLE)); if(state == null) { if(log.isWarnEnabled()) log.warn("state received from " + sender + " is null, will return null state to application"); } else log.debug("received state, size=" + state.length + " bytes. Time=" + (stop-start) + " milliseconds"); StateTransferInfo info=new StateTransferInfo(state_sender, id, 0L, state); up_prot.up(new Event(Event.GET_STATE_OK, info)); } /* ------------------------ End of Private Methods ------------------------------ */ /** * Wraps data for a state request/response. Note that for a state response the actual state will not"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(sender); out.writeLong(id); out.writeByte(type); out.writeObject(my_digest); if(state_id == null) { out.writeBoolean(false); } else { out.writeBoolean(true); out.writeUTF(state_id); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { sender=(Address)in.readObject(); id=in.readLong(); type=in.readByte(); my_digest=(Digest)in.readObject(); if(in.readBoolean()) state_id=in.readUTF(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(id); Util.writeAddress(sender, out); Util.writeStreamable(my_digest, out); Util.writeString(state_id, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); id=in.readLong(); sender=Util.readAddress(in); my_digest=(Digest)Util.readStreamable(Digest.class, in); state_id=Util.readString(in); } public int size() { int retval=Global.LONG_SIZE + Global.BYTE_SIZE; // id and type retval+=Util.size(sender); retval+=Global.BYTE_SIZE; // presence byte for my_digest if(my_digest != null) retval+=my_digest.serializedSize(); retval+=Global.BYTE_SIZE; // presence byte for state_id if(state_id != null) retval+=state_id.length() +2; return retval; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/FLUSH.java0000644000175000017500000011376411366547366026445 0ustar twernertwernerpackage org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.Digest; import org.jgroups.util.Promise; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * Flush, as it name implies, forces group members to flush their pending messages while blocking * them to send any additional messages. The process of flushing acquiesces the group so that state * transfer or a join can be done. It is also called stop-the-world model as nobody will be able to * send messages while a flush is in process. * *

* Flush is needed for: *

* (1) State transfer. When a member requests state transfer, the coordinator tells everyone to stop * sending messages and waits for everyone's ack. Then it asks the application for its state and * ships it back to the requester. After the requester has received and set the state successfully, * the coordinator tells everyone to resume sending messages. *

* (2) View changes (e.g.a join). Before installing a new view V2, flushing would ensure that all * messages *sent* in the current view V1 are indeed *delivered* in V1, rather than in V2 (in all * non-faulty members). This is essentially Virtual Synchrony. * * * * @author Vladimir Blagojevic * @version $Id$ * @since 2.4 */ public class FLUSH extends Protocol { public static final String NAME = "FLUSH"; /* * ------------------------------------------ Properties * ------------------------------------------ */ private long timeout = 8000; private long start_flush_timeout = 2000; private boolean enable_reconciliation = true; /* * --------------------------------------------- JMX * ---------------------------------------------- */ private long startFlushTime; private long totalTimeInFlush; private int numberOfFlushes; private double averageFlushDuration; /* * --------------------------------------------- Fields * ------------------------------------------------------ */ @GuardedBy("sharedLock") private View currentView; private Address localAddress; /** * Group member that requested FLUSH. For view installations flush coordinator is the group * coordinator For state transfer flush coordinator is the state requesting member */ @GuardedBy("sharedLock") private Address flushCoordinator; @GuardedBy("sharedLock") private final List

flushMembers; private final AtomicInteger viewCounter = new AtomicInteger(0); @GuardedBy("sharedLock") private final Map flushCompletedMap; @GuardedBy("sharedLock") private final List
flushNotCompletedMap; @GuardedBy("sharedLock") private final Set
suspected; @GuardedBy("sharedLock") private final List
reconcileOks; private final Object sharedLock = new Object(); private final Object blockMutex = new Object(); /** * Indicates if FLUSH.down() is currently blocking threads Condition predicate associated with * blockMutex */ @GuardedBy("blockMutex") private volatile boolean isBlockingFlushDown = true; @GuardedBy("sharedLock") private boolean flushCompleted = false; private final Promise flush_promise = new Promise(); private final AtomicBoolean flushInProgress = new AtomicBoolean(false); private final AtomicBoolean sentBlock = new AtomicBoolean(false); private final AtomicBoolean sentUnblock = new AtomicBoolean(false); public FLUSH() { super(); currentView = new View(new ViewId(), new Vector
()); flushCompletedMap = new HashMap(); flushNotCompletedMap = new ArrayList
(); reconcileOks = new ArrayList
(); flushMembers = new ArrayList
(); suspected = new TreeSet
(); } public String getName() { return NAME; } public long getStartFlushTimeout() { return start_flush_timeout; } public void setStartFlushTimeout(long start_flush_timeout) { this.start_flush_timeout = start_flush_timeout; } public boolean setProperties(Properties props) { super.setProperties(props); timeout = Util.parseLong(props, "timeout", timeout); start_flush_timeout = Util.parseLong(props, "start_flush_timeout", start_flush_timeout); enable_reconciliation = Util.parseBoolean(props, "enable_reconciliation", enable_reconciliation); String str = props.getProperty("retry_timeout"); if (str != null) { log.warn("retry_timeout has been deprecated and its value will be ignored"); props.remove("retry_timeout"); } str = props.getProperty("flush_retry_count"); if (str != null) { log.warn("flush_retry_count has been deprecated and its value will be ignored"); props.remove("flush_retry_count"); } str = props.getProperty("auto_flush_conf"); if (str != null) { log.warn("auto_flush_conf has been deprecated and its value will be ignored"); props.remove("auto_flush_conf"); } if (!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void start() throws Exception { Map map = new HashMap(); map.put("flush_supported", Boolean.TRUE); up_prot.up(new Event(Event.CONFIG, map)); down_prot.down(new Event(Event.CONFIG, map)); viewCounter.set(0); synchronized (blockMutex) { isBlockingFlushDown = true; } } public void stop() { synchronized (sharedLock) { currentView = new View(new ViewId(), new Vector
()); flushCompletedMap.clear(); flushNotCompletedMap.clear(); flushMembers.clear(); suspected.clear(); flushCoordinator = null; } } /* -------------------JMX attributes and operations --------------------- */ public double getAverageFlushDuration() { return averageFlushDuration; } public long getTotalTimeInFlush() { return totalTimeInFlush; } public int getNumberOfFlushes() { return numberOfFlushes; } public boolean startFlush() { return startFlush(new Event(Event.SUSPEND)); } private boolean startFlush(Event evt) { if (log.isDebugEnabled()) log.debug("Received " + evt + " at " + localAddress + ". Running FLUSH..."); List
flushParticipants = (List
) evt.getArg(); return startFlush(flushParticipants); } private boolean startFlush(List
flushParticipants) { boolean successfulFlush = false; if (!flushInProgress.get()) { flush_promise.reset(); synchronized(sharedLock) { if(flushParticipants == null) flushParticipants=new ArrayList
(currentView.getMembers()); } onSuspend(flushParticipants); try { Boolean r = flush_promise.getResultWithTimeout(start_flush_timeout); successfulFlush = r.booleanValue(); } catch (TimeoutException e) { if (log.isDebugEnabled()) log.debug("At " + localAddress + " timed out waiting for flush responses after " + start_flush_timeout + " msec. Rejecting flush to participants " + flushParticipants); rejectFlush(flushParticipants, currentViewId()); } } return successfulFlush; } public void stopFlush() { down(new Event(Event.RESUME)); } /* * ------------------- end JMX attributes and operations --------------------- */ public Object down(Event evt) { switch (evt.getType()) { case Event.MSG: Message msg = (Message) evt.getArg(); Address dest = msg.getDest(); if (dest == null || dest.isMulticastAddress()) { // mcasts FlushHeader fh = (FlushHeader) msg.getHeader(getName()); if (fh != null && fh.type == FlushHeader.FLUSH_BYPASS) { return down_prot.down(evt); } else { blockMessageDuringFlush(); } } else { // unicasts are irrelevant in virtual synchrony, let them through return down_prot.down(evt); } break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: if (sentBlock.compareAndSet(false, true)) { sendBlockUpToChannel(); } Object result = down_prot.down(evt); if (result instanceof Throwable) { sentBlock.set(false); // set the var back to its original state if we cannot // connect successfully } return result; case Event.SUSPEND: return startFlush(evt); case Event.RESUME: onResume(evt); return null; } return down_prot.down(evt); } private void blockMessageDuringFlush() { boolean shouldSuspendByItself = false; long start = 0, stop = 0; synchronized (blockMutex) { while (isBlockingFlushDown) { if (log.isDebugEnabled()) log.debug("FLUSH block at " + localAddress + " for " + (timeout <= 0 ? "ever" : timeout + "ms")); try { start = System.currentTimeMillis(); if (timeout <= 0) blockMutex.wait(); else blockMutex.wait(timeout); stop = System.currentTimeMillis(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } if (isBlockingFlushDown) { isBlockingFlushDown = false; shouldSuspendByItself = true; blockMutex.notifyAll(); } } } if (shouldSuspendByItself) { log.warn("unblocking FLUSH.down() at " + localAddress + " after timeout of " + (stop - start) + "ms"); flush_promise.setResult(Boolean.TRUE); } } public Object up(Event evt) { switch (evt.getType()) { case Event.MSG: Message msg = (Message) evt.getArg(); final FlushHeader fh = (FlushHeader) msg.getHeader(getName()); if (fh != null) { switch (fh.type) { case FlushHeader.FLUSH_BYPASS: return up_prot.up(evt); case FlushHeader.START_FLUSH: Collection
fp = fh.flushParticipants; boolean amIParticipant = (fp != null && fp.contains(localAddress)) || msg.getSrc().equals(localAddress); if (amIParticipant) { handleStartFlush(msg, fh); } else { if (log.isDebugEnabled()) log.debug("Received START_FLUSH at " + localAddress + " but I am not flush participant, not responding"); } break; case FlushHeader.FLUSH_RECONCILE: handleFlushReconcile(msg, fh); break; case FlushHeader.FLUSH_RECONCILE_OK: onFlushReconcileOK(msg); break; case FlushHeader.STOP_FLUSH: onStopFlush(); break; case FlushHeader.ABORT_FLUSH: Collection
flushParticipants = fh.flushParticipants; if (flushParticipants != null && flushParticipants.contains(localAddress)) { if (log.isDebugEnabled()) { log.debug("At " + localAddress + " received ABORT_FLUSH from flush coordinator " + msg.getSrc() + ", am i flush participant=" + flushParticipants.contains(localAddress)); } flushInProgress.set(false); flushNotCompletedMap.clear(); flushCompletedMap.clear(); } break; case FlushHeader.FLUSH_NOT_COMPLETED: if (log.isDebugEnabled()) { log.debug("At " + localAddress + " received FLUSH_NOT_COMPLETED from " + msg.getSrc()); } boolean flushCollision = false; synchronized (sharedLock) { flushNotCompletedMap.add(msg.getSrc()); flushCollision = !flushCompletedMap.isEmpty(); if (flushCollision) { flushNotCompletedMap.clear(); flushCompletedMap.clear(); } } if (log.isDebugEnabled()) { log.debug("At " + localAddress + " received FLUSH_NOT_COMPLETED from " + msg.getSrc() + " collision=" + flushCollision); } // reject flush if we have at least one OK and at least one FAIL if (flushCollision) { Runnable r = new Runnable() { public void run() { // wait a bit so ABORTs do not get received before other // possible FLUSH_COMPLETED Util.sleep(1000); rejectFlush(fh.flushParticipants, fh.viewID); } }; new Thread(r).start(); } // however, flush should fail/retry as soon as one FAIL is received flush_promise.setResult(Boolean.FALSE); break; case FlushHeader.FLUSH_COMPLETED: if (isCurrentFlushMessage(fh)) onFlushCompleted(msg.getSrc(), fh); break; } return null; // do not pass FLUSH msg up } else { // http://jira.jboss.com/jira/browse/JGRP-575 // for processing of application messages after we join, // lets wait for STOP_FLUSH to complete // before we start allowing message up. Address dest = msg.getDest(); if (dest != null && !dest.isMulticastAddress()) { return up_prot.up(evt); // allow unicasts to pass, virtual synchrony olny // applies to multicasts } } break; case Event.VIEW_CHANGE: /* * [JGRP-618] - FLUSH coordinator transfer reorders block/unblock/view events in * applications (TCP stack only) */ up_prot.up(evt); View newView = (View) evt.getArg(); boolean coordinatorLeft = onViewChange(newView); boolean singletonMember = newView.size() == 1 && newView.containsMember(localAddress); boolean isThisOurFirstView = viewCounter.addAndGet(1) == 1; // if this is channel's first view and its the only member of the group - no flush // was run // but the channel application should still receive BLOCK,VIEW,UNBLOCK // also if coordinator of flush left each member should run stopFlush individually. if ((isThisOurFirstView && singletonMember) || coordinatorLeft) { onStopFlush(); } return null; case Event.TMP_VIEW: /* * April 25, 2007 * * Accommodating current NAKACK (1.127) * * Updates field currentView of a leaving coordinator. Leaving coordinator, after it * sends out the view, does not need to participate in second flush phase. * * see onStopFlush(); * * TODO: revisit if still needed post NAKACK 1.127 */ View tmpView = (View) evt.getArg(); if (!tmpView.containsMember(localAddress)) { onViewChange(tmpView); } break; case Event.SET_LOCAL_ADDRESS: localAddress = (Address) evt.getArg(); break; case Event.SUSPECT: onSuspect((Address) evt.getArg()); break; case Event.SUSPEND: return startFlush(evt); case Event.RESUME: onResume(evt); return null; } return up_prot.up(evt); } private void onFlushReconcileOK(Message msg) { if (log.isDebugEnabled()) log.debug(localAddress + " received reconcile ok from " + msg.getSrc()); synchronized (sharedLock) { reconcileOks.add(msg.getSrc()); if (reconcileOks.size() >= flushMembers.size()) { flush_promise.setResult(Boolean.TRUE); if (log.isDebugEnabled()) log.debug("All FLUSH_RECONCILE_OK received at " + localAddress); } } } private void handleFlushReconcile(Message msg, FlushHeader fh) { Address requester = msg.getSrc(); Digest reconcileDigest = fh.digest; if (log.isDebugEnabled()) log.debug("Received FLUSH_RECONCILE at " + localAddress + " passing digest to NAKACK " + reconcileDigest); // Let NAKACK reconcile missing messages down_prot.down(new Event(Event.REBROADCAST, reconcileDigest)); if (log.isDebugEnabled()) log.debug("Returned from FLUSH_RECONCILE at " + localAddress + " Sending RECONCILE_OK to " + requester + ", thread " + Thread.currentThread()); Message reconcileOk = new Message(requester); reconcileOk.setFlag(Message.OOB); reconcileOk.putHeader(getName(), new FlushHeader(FlushHeader.FLUSH_RECONCILE_OK)); down_prot.down(new Event(Event.MSG, reconcileOk)); } private void handleStartFlush(Message msg, FlushHeader fh) { Address flushRequester = msg.getSrc(); boolean proceed = flushInProgress.compareAndSet(false, true); if (proceed) { synchronized (sharedLock) { flushCoordinator = flushRequester; } onStartFlush(flushRequester, fh); } else { FlushHeader fhr = new FlushHeader(FlushHeader.FLUSH_NOT_COMPLETED, fh.viewID,fh.flushParticipants); Message response = new Message(flushRequester); response.putHeader(getName(), fhr); down_prot.down(new Event(Event.MSG, response)); if (log.isDebugEnabled()) log.debug("Received START_FLUSH at " + localAddress + " responded with FLUSH_NOT_COMPLETED to " + flushRequester); } } private void rejectFlush(Collection
participants, long viewId) { for (Address flushMember : participants) { Message reject = new Message(flushMember, localAddress, null); reject.putHeader(getName(), new FlushHeader(FlushHeader.ABORT_FLUSH, viewId, participants)); down_prot.down(new Event(Event.MSG, reject)); } } public Vector providedDownServices() { Vector retval = new Vector(2); retval.addElement(new Integer(Event.SUSPEND)); retval.addElement(new Integer(Event.RESUME)); return retval; } private void sendBlockUpToChannel() { up_prot.up(new Event(Event.BLOCK)); sentUnblock.set(false); } private void sendUnBlockUpToChannel() { sentBlock.set(false); up_prot.up(new Event(Event.UNBLOCK)); } private boolean isCurrentFlushMessage(FlushHeader fh) { return fh.viewID == currentViewId(); } private long currentViewId() { long viewId = -1; synchronized (sharedLock) { ViewId view = currentView.getVid(); if (view != null) { viewId = view.getId(); } } return viewId; } private boolean onViewChange(View view) { boolean coordinatorLeft = false; synchronized (sharedLock) { suspected.retainAll(view.getMembers()); currentView = view; coordinatorLeft = !view.getMembers().isEmpty() && !view.containsMember(view.getCreator()); } if (log.isDebugEnabled()) log.debug("Installing view at " + localAddress + " view is " + view); return coordinatorLeft; } private void onStopFlush() { if (stats) { long stopFlushTime = System.currentTimeMillis(); totalTimeInFlush += (stopFlushTime - startFlushTime); if (numberOfFlushes > 0) { averageFlushDuration = totalTimeInFlush / (double) numberOfFlushes; } } synchronized (sharedLock) { flushCompletedMap.clear(); flushNotCompletedMap.clear(); flushMembers.clear(); suspected.clear(); flushCoordinator = null; flushCompleted = false; } if (log.isDebugEnabled()) log.debug("At " + localAddress + " received STOP_FLUSH, unblocking FLUSH.down() and sending UNBLOCK up"); synchronized (blockMutex) { isBlockingFlushDown = false; blockMutex.notifyAll(); } if (sentUnblock.compareAndSet(false, true)) { // ensures that we do not repeat unblock event sendUnBlockUpToChannel(); } flushInProgress.set(false); } private void onSuspend(List
members) { Message msg = null; Collection
participantsInFlush = null; synchronized (sharedLock) { // start FLUSH only on group members that we need to flush if (members != null) { participantsInFlush = members; participantsInFlush.retainAll(currentView.getMembers()); } else { participantsInFlush = new ArrayList
(currentView.getMembers()); } msg = new Message(null, localAddress, null); msg.putHeader(getName(), new FlushHeader(FlushHeader.START_FLUSH, currentViewId(), participantsInFlush)); } if (participantsInFlush.isEmpty()) { flush_promise.setResult(Boolean.TRUE); } else { down_prot.down(new Event(Event.MSG, msg)); if (log.isDebugEnabled()) log.debug("Flush coordinator " + localAddress + " is starting FLUSH with participants " + participantsInFlush); } } private void onResume(Event evt) { List
members = (List
) evt.getArg(); long viewID = currentViewId(); if (members == null || members.isEmpty()) { Message msg = new Message(null, localAddress, null); // Cannot be OOB since START_FLUSH is not OOB // we have to FIFO order two subsequent flushes msg.putHeader(getName(), new FlushHeader(FlushHeader.STOP_FLUSH, viewID)); down_prot.down(new Event(Event.MSG, msg)); if (log.isDebugEnabled()) log.debug("Received RESUME at " + localAddress + ", sent STOP_FLUSH to all"); } else { for (Address address : members) { Message msg = new Message(address, localAddress, null); // Cannot be OOB since START_FLUSH is not OOB // we have to FIFO order two subsequent flushes msg.putHeader(getName(), new FlushHeader(FlushHeader.STOP_FLUSH, viewID)); down_prot.down(new Event(Event.MSG, msg)); if (log.isDebugEnabled()) log.debug("Received RESUME at " + localAddress + ", sent STOP_FLUSH to " + address); } } } private void onStartFlush(Address flushStarter, FlushHeader fh) { if (stats) { startFlushTime = System.currentTimeMillis(); numberOfFlushes += 1; } boolean proceed = false; synchronized (sharedLock) { flushCoordinator = flushStarter; flushMembers.clear(); if (fh.flushParticipants != null) { flushMembers.addAll(fh.flushParticipants); } proceed = flushMembers.contains(localAddress); flushMembers.removeAll(suspected); } if (proceed) { if (sentBlock.compareAndSet(false, true)) { // ensures that we do not repeat block event // and that we do not send block event to non participants sendBlockUpToChannel(); synchronized (blockMutex) { isBlockingFlushDown = true; } } else { if (log.isDebugEnabled()) log.debug("Received START_FLUSH at " + localAddress + " but not sending BLOCK up"); } Digest digest = (Digest) down_prot.down(new Event(Event.GET_DIGEST)); FlushHeader fhr = new FlushHeader(FlushHeader.FLUSH_COMPLETED, fh.viewID,fh.flushParticipants); fhr.addDigest(digest); Message msg = new Message(flushStarter); msg.putHeader(getName(), fhr); down_prot.down(new Event(Event.MSG, msg)); if (log.isDebugEnabled()) log.debug("Received START_FLUSH at " + localAddress + " responded with FLUSH_COMPLETED to " + flushStarter); } } private void onFlushCompleted(Address address, final FlushHeader header) { Message msg = null; boolean needsReconciliationPhase = false; boolean collision = false; Digest digest = header.digest; synchronized (sharedLock) { flushCompletedMap.put(address, digest); flushCompleted = flushCompletedMap.size() >= flushMembers.size() && !flushMembers.isEmpty() && flushCompletedMap.keySet().containsAll(flushMembers); collision = !flushNotCompletedMap.isEmpty(); if (log.isDebugEnabled()) log.debug("At " + localAddress + " FLUSH_COMPLETED from " + address + ",completed " + flushCompleted + ",flushMembers " + flushMembers + ",flushCompleted " + flushCompletedMap.keySet()); needsReconciliationPhase = enable_reconciliation && flushCompleted && hasVirtualSynchronyGaps(); if (needsReconciliationPhase) { Digest d = findHighestSequences(); msg = new Message(); msg.setFlag(Message.OOB); FlushHeader fh = new FlushHeader(FlushHeader.FLUSH_RECONCILE, currentViewId(), flushMembers); reconcileOks.clear(); fh.addDigest(d); msg.putHeader(getName(), fh); if (log.isDebugEnabled()) log.debug("At " + localAddress + " reconciling flush mebers due to virtual synchrony gap, digest is " + d + " flush members are " + flushMembers); flushCompletedMap.clear(); } else if (flushCompleted) { flushCompletedMap.clear(); } else if (collision) { flushNotCompletedMap.clear(); flushCompletedMap.clear(); } } if (needsReconciliationPhase) { down_prot.down(new Event(Event.MSG, msg)); } else if (flushCompleted) { flush_promise.setResult(Boolean.TRUE); if (log.isDebugEnabled()) log.debug("All FLUSH_COMPLETED received at " + localAddress); } else if (collision) { // reject flush if we have at least one OK and at least one FAIL Runnable r = new Runnable() { public void run() { // wait a bit so ABORTs do not get received before other possible // FLUSH_COMPLETED Util.sleep(1000); rejectFlush(header.flushParticipants, header.viewID); } }; new Thread(r).start(); } } private boolean hasVirtualSynchronyGaps() { ArrayList digests = new ArrayList(); digests.addAll(flushCompletedMap.values()); Digest firstDigest = digests.get(0); List remainingDigests = digests.subList(1, digests.size()); for (Digest digest : remainingDigests) { Digest diff = firstDigest.difference(digest); if (diff != Digest.EMPTY_DIGEST) { return true; } } return false; } private Digest findHighestSequences() { Digest result = null; List digests = new ArrayList(flushCompletedMap.values()); result = digests.get(0); List remainingDigests = digests.subList(1, digests.size()); for (Digest digestG : remainingDigests) { result = result.highestSequence(digestG); } return result; } private void onSuspect(Address address) { // handles FlushTest#testFlushWithCrashedFlushCoordinator boolean amINeighbourOfCrashedFlushCoordinator = false; ArrayList
flushMembersCopy = null; synchronized (sharedLock) { boolean flushCoordinatorSuspected = address.equals(flushCoordinator); if (flushCoordinatorSuspected && flushMembers != null) { int indexOfCoordinator = flushMembers.indexOf(flushCoordinator); int myIndex = flushMembers.indexOf(localAddress); int diff = myIndex - indexOfCoordinator; amINeighbourOfCrashedFlushCoordinator = (diff == 1 || (myIndex == 0 && indexOfCoordinator == flushMembers.size())); if (amINeighbourOfCrashedFlushCoordinator) { flushMembersCopy = new ArrayList
(flushMembers); } } } if (amINeighbourOfCrashedFlushCoordinator) { if (log.isDebugEnabled()) log.debug("Flush coordinator " + flushCoordinator + " suspected, " + localAddress + " is neighbour, completing flush "); onResume(new Event(Event.RESUME, flushMembersCopy)); } // handles FlushTest#testFlushWithCrashedNonCoordinators boolean flushOkCompleted = false; Message m = null; long viewID = 0; synchronized (sharedLock) { suspected.add(address); flushMembers.removeAll(suspected); viewID = currentViewId(); flushOkCompleted = !flushCompletedMap.isEmpty() && flushCompletedMap.keySet().containsAll(flushMembers); if (flushOkCompleted) { m = new Message(flushCoordinator, localAddress, null); } if (log.isDebugEnabled()) log.debug("Suspect is " + address + ",completed " + flushOkCompleted + ", flushOkSet " + flushCompletedMap + " flushMembers " + flushMembers); } if (flushOkCompleted) { Digest digest = (Digest) down_prot.down(new Event(Event.GET_DIGEST)); FlushHeader fh = new FlushHeader(FlushHeader.FLUSH_COMPLETED, viewID); fh.addDigest(digest); m.putHeader(getName(), fh); down_prot.down(new Event(Event.MSG, m)); if (log.isDebugEnabled()) log.debug(localAddress + " sent FLUSH_COMPLETED message to " + flushCoordinator); } } public static class FlushHeader extends Header implements Streamable { public static final byte START_FLUSH = 0; public static final byte STOP_FLUSH = 2; public static final byte FLUSH_COMPLETED = 3; public static final byte ABORT_FLUSH = 5; public static final byte FLUSH_BYPASS = 6; public static final byte FLUSH_RECONCILE = 7; public static final byte FLUSH_RECONCILE_OK = 8; public static final byte FLUSH_NOT_COMPLETED = 9; byte type; long viewID; Collection
flushParticipants; Digest digest = null; private static final long serialVersionUID = -6248843990215637687L; public FlushHeader() { this(START_FLUSH, 0); } // used for externalization public FlushHeader(byte type) { this(type, 0); } public FlushHeader(byte type, long viewID) { this(type, viewID, null); } public FlushHeader(byte type, long viewID, Collection
flushView) { this.type = type; this.viewID = viewID; if (flushView != null) { this.flushParticipants = new ArrayList
(flushView); } } @Override public int size() { int retval = Global.BYTE_SIZE; // type retval += Global.LONG_SIZE; // viewID retval += Util.size(flushParticipants); retval += Global.BYTE_SIZE; // presence for digest if (digest != null) { retval += digest.serializedSize(); } return retval; } public void addDigest(Digest digest) { this.digest = digest; } public String toString() { switch (type) { case START_FLUSH: return "FLUSH[type=START_FLUSH,viewId=" + viewID + ",members=" + flushParticipants + "]"; case STOP_FLUSH: return "FLUSH[type=STOP_FLUSH,viewId=" + viewID + "]"; case ABORT_FLUSH: return "FLUSH[type=ABORT_FLUSH,viewId=" + viewID + "]"; case FLUSH_COMPLETED: return "FLUSH[type=FLUSH_COMPLETED,viewId=" + viewID + "]"; case FLUSH_BYPASS: return "FLUSH[type=FLUSH_BYPASS,viewId=" + viewID + "]"; case FLUSH_RECONCILE: return "FLUSH[type=FLUSH_RECONCILE,viewId=" + viewID + ",digest=" + digest + "]"; case FLUSH_RECONCILE_OK: return "FLUSH[type=FLUSH_RECONCILE_OK,viewId=" + viewID + "]"; default: return "[FLUSH: unknown type (" + type + ")]"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeLong(viewID); out.writeObject(flushParticipants); out.writeObject(digest); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type = in.readByte(); viewID = in.readLong(); flushParticipants = (Collection
) in.readObject(); digest = (Digest) in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(viewID); Util.writeAddresses(flushParticipants, out); Util.writeStreamable(digest, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type = in.readByte(); viewID = in.readLong(); flushParticipants = Util.readAddresses(in, ArrayList.class); digest = (Digest) Util.readStreamable(Digest.class, in); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/MergeData.java0000644000175000017500000000442211366547366027403 0ustar twernertwerner// $Id: MergeData.java,v 1.6.4.1 2008/01/22 10:01:28 belaban Exp $ package org.jgroups.protocols.pbcast; import org.jgroups.Address; import org.jgroups.View; import org.jgroups.util.Digest; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * Encapsulates data sent with a MERGE_RSP (handleMergeResponse()) and INSTALL_MERGE_VIEW * (handleMergeView()). * * @author Bela Ban Oct 22 2001 */ public class MergeData implements Externalizable { Address sender=null; boolean merge_rejected=false; View view=null; Digest digest=null; /** * Empty constructor needed for externalization */ public MergeData() { } public MergeData(Address sender, View view, Digest digest) { this.sender=sender; this.view=view; this.digest=digest; } public Address getSender() { return sender; } public View getView() { return view; } public Digest getDigest() { return digest; } public void setView(View v) { view=v; } public void setDigest(Digest d) { digest=d; } public boolean equals(Object other) { return sender != null && other != null && other instanceof MergeData && ((MergeData)other).sender != null && ((MergeData)other).sender.equals(sender); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(sender); out.writeBoolean(merge_rejected); if(!merge_rejected) { out.writeObject(view); out.writeObject(digest); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { sender=(Address)in.readObject(); merge_rejected=in.readBoolean(); if(!merge_rejected) { view=(View)in.readObject(); digest=(Digest)in.readObject(); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("sender=").append(sender); if(merge_rejected) sb.append(" (merge_rejected)"); else { sb.append(", view=").append(view).append(", digest=").append(digest); } return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/NakAckHeader.java0000644000175000017500000001023111366547366030006 0ustar twernertwerner package org.jgroups.protocols.pbcast; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.util.Range; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; /** * @author Bela Ban * @version $Id: NakAckHeader.java,v 1.21 2007/08/20 10:46:53 belaban Exp $ */ public class NakAckHeader extends Header implements Streamable { public static final byte MSG=1; // regular msg public static final byte XMIT_REQ=2; // retransmit request public static final byte XMIT_RSP=3; // retransmit response (contains one or more messages) byte type=0; long seqno=-1; // seqno of regular message (MSG) Range range=null; // range of msgs to be retransmitted (XMIT_REQ) or retransmitted (XMIT_RSP) Address sender; // the original sender of the message (for XMIT_REQ) private static final long serialVersionUID=-4305600151593420827L; public NakAckHeader() { } /** * Constructor for regular messages */ public NakAckHeader(byte type, long seqno) { this.type=type; this.seqno=seqno; } /** * Constructor for retransmit requests/responses (low and high define the range of msgs) */ public NakAckHeader(byte type, long low, long high) { this.type=type; range=new Range(low, high); } public NakAckHeader(byte type, long low, long high, Address sender) { this(type, low, high); this.sender=sender; } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeLong(seqno); if(range != null) { out.writeBoolean(true); // wasn't here before, bad bug ! range.writeExternal(out); } else out.writeBoolean(false); out.writeObject(sender); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { boolean read_range; type=in.readByte(); seqno=in.readLong(); read_range=in.readBoolean(); if(read_range) { range=new Range(); range.readExternal(in); } sender=(Address)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(seqno); Util.writeStreamable(range, out); Util.writeAddress(sender, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); seqno=in.readLong(); range=(Range)Util.readStreamable(Range.class, in); sender=Util.readAddress(in); } public int size() { // type (1 byte) + seqno (8 bytes) int retval=Global.BYTE_SIZE; retval+=Global.LONG_SIZE; retval+=Global.BYTE_SIZE; // presence for range if(range != null) retval+=2 * Global.LONG_SIZE; // 2 times 8 bytes for seqno retval+=Util.size(sender); return retval; } public NakAckHeader copy() { NakAckHeader ret=new NakAckHeader(type, seqno); ret.range=range; ret.sender=sender; return ret; } public static String type2Str(byte t) { switch(t) { case MSG: return "MSG"; case XMIT_REQ: return "XMIT_REQ"; case XMIT_RSP: return "XMIT_RSP"; default: return ""; } } public String toString() { StringBuilder ret=new StringBuilder(); ret.append("[").append(type2Str(type)); switch(type) { case MSG: // seqno and sender ret.append(", seqno=").append(seqno); break; case XMIT_REQ: // range and sender if(range != null) ret.append(", range=" + range); break; case XMIT_RSP: // seqno and sender break; } if(sender != null) ret.append(", sender=").append(sender); ret.append(']'); return ret.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/ClientGmsImpl.java0000644000175000017500000003471111366547366030265 0ustar twernertwerner package org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.protocols.PingRsp; import org.jgroups.util.Promise; import org.jgroups.util.Util; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import java.util.*; /** * Client part of GMS. Whenever a new member wants to join a group, it starts in the CLIENT role. * No multicasts to the group will be received and processed until the member has been joined and * turned into a SERVER (either coordinator or participant, mostly just participant). This class * only implements Join (called by clients who want to join a certain group, and * ViewChange which is called by the coordinator that was contacted by this client, to * tell the client what its initial membership is. * @author Bela Ban * @version $Id: ClientGmsImpl.java,v 1.56.2.5 2009/09/08 12:24:39 belaban Exp $ */ public class ClientGmsImpl extends GmsImpl { private final Promise join_promise=new Promise(); public ClientGmsImpl(GMS g) { super(g); } public void init() throws Exception { super.init(); join_promise.reset(); } public void join(Address address) { join(address, false); } public void joinWithStateTransfer(Address address) { join(address, true); } /** * Joins this process to a group. Determines the coordinator and sends a * unicast handleJoin() message to it. The coordinator returns a JoinRsp and * then broadcasts the new view, which contains a message digest and the * current membership (including the joiner). The joiner is then supposed to * install the new view and the digest and starts accepting mcast messages. * Previous mcast messages were discarded (this is done in PBCAST). *

* If successful, impl is changed to an instance of ParticipantGmsImpl. * Otherwise, we continue trying to send join() messages to the coordinator, * until we succeed (or there is no member in the group. In this case, we * create our own singleton group). *

* When GMS.disable_initial_coord is set to true, then we won't become * coordinator on receiving an initial membership of 0, but instead will * retry (forever) until we get an initial membership of > 0. * * @param mbr Our own address (assigned through SET_LOCAL_ADDRESS) */ private void join(Address mbr, boolean joinWithStateTransfer) { Address coord=null; JoinRsp rsp=null; View tmp_view; leaving=false; join_promise.reset(); while(!leaving) { if(rsp == null && !join_promise.hasResult()) { // null responses means that the discovery was cancelled List responses=findInitialMembers(join_promise); if(log.isDebugEnabled()) log.debug("initial_mbrs are " + responses); if(responses == null || responses.isEmpty()) { if(gms.disable_initial_coord) { if(log.isTraceEnabled()) log.trace("received an initial membership of 0, but cannot become coordinator " + "(disable_initial_coord=true), will retry fetching the initial membership"); continue; } if(log.isDebugEnabled()) log.debug("no initial members discovered: creating group as first member"); becomeSingletonMember(mbr); return; } coord=determineCoord(responses); if(coord == null) { // e.g. because we have all clients only if(gms.handle_concurrent_startup == false) { if(log.isTraceEnabled()) log.trace("handle_concurrent_startup is false; ignoring responses of initial clients"); becomeSingletonMember(mbr); return; } if(log.isTraceEnabled()) log.trace("could not determine coordinator from responses " + responses); // so the member to become singleton member (and thus coord) is the first of all clients Set

clients=new TreeSet
(); // sorted clients.add(mbr); // add myself again (was removed by findInitialMembers()) for(PingRsp response: responses) { Address client_addr=response.getAddress(); if(client_addr != null) clients.add(client_addr); } if(log.isTraceEnabled()) log.trace("clients to choose new coord from are: " + clients); Address new_coord=clients.iterator().next(); if(new_coord.equals(mbr)) { if(log.isTraceEnabled()) log.trace("I (" + mbr + ") am the first of the clients, will become coordinator"); becomeSingletonMember(mbr); return; } else { if(log.isTraceEnabled()) log.trace("I (" + mbr + ") am not the first of the clients, waiting for another client to become coordinator"); Util.sleep(500); } continue; } if(log.isDebugEnabled()) log.debug("sending handleJoin(" + mbr + ") to " + coord); sendJoinMessage(coord, mbr, joinWithStateTransfer); } try { if(rsp == null) rsp=join_promise.getResult(gms.join_timeout); if(rsp == null) { if(log.isWarnEnabled()) log.warn("join(" + mbr + ") sent to " + coord + " timed out (after " + gms.join_timeout + " ms), retrying"); } else { // 1. check whether JOIN was rejected String failure=rsp.getFailReason(); if(failure != null) throw new SecurityException(failure); // 2. Install digest // See if the digest does not have senders, this seems to happen on high volume startup if(rsp.getDigest() == null || rsp.getDigest().getSenders() == null) { if(log.isWarnEnabled()) log.warn("digest response has no senders: digest=" + rsp.getDigest()); rsp=null; // just skip the response we guess continue; } MutableDigest tmp_digest=new MutableDigest(rsp.getDigest()); tmp_view=rsp.getView(); if(tmp_view == null) { if(log.isErrorEnabled()) log.error("JoinRsp has a null view, skipping it"); rsp=null; } else { if(!tmp_digest.contains(gms.local_addr)) { throw new IllegalStateException("digest returned from " + coord + " with JOIN_RSP does not contain myself (" + gms.local_addr + "): join response: " + rsp); } tmp_digest.incrementHighestDeliveredSeqno(coord); // see DESIGN for details tmp_digest.seal(); gms.setDigest(tmp_digest); if(log.isDebugEnabled()) log.debug("[" + gms.local_addr + "]: JoinRsp=" + tmp_view + " [size=" + tmp_view.size() + "]\n\n"); if(!installView(tmp_view)) { if(log.isErrorEnabled()) log.error("view installation failed, retrying to join group"); rsp=null; continue; } // send VIEW_ACK to sender of view Message view_ack=new Message(coord, null, null); view_ack.setFlag(Message.OOB); GMS.GmsHeader tmphdr=new GMS.GmsHeader(GMS.GmsHeader.VIEW_ACK); view_ack.putHeader(GMS.name, tmphdr); // if(!gms.members.contains(coord)) gms.getDownProtocol().down(new Event(Event.MSG, view_ack)); return; } } } catch(SecurityException security_ex) { throw security_ex; } catch(IllegalArgumentException illegal_arg) { throw illegal_arg; } catch(Throwable e) { if(log.isDebugEnabled()) log.debug("exception=" + e + ", retrying"); rsp=null; } } } private List findInitialMembers(Promise promise) { List responses=(List)gms.getDownProtocol().down(new Event(Event.FIND_INITIAL_MBRS, promise)); if(responses != null) { for(Iterator iter=responses.iterator(); iter.hasNext();) { PingRsp response=iter.next(); if(response.own_addr != null && response.own_addr.equals(gms.local_addr)) iter.remove(); } } return responses; } public void leave(Address mbr) { leaving=true; wrongMethod("leave"); } public void handleJoinResponse(JoinRsp join_rsp) { join_promise.setResult(join_rsp); // will wake up join() method } public void handleLeaveResponse() { } public void suspect(Address mbr) { } public void unsuspect(Address mbr) { } public void handleMembershipChange (Collection requests) { } /** * Does nothing. Discards all views while still client. */ public synchronized void handleViewChange(View new_view, Digest digest) { if(log.isTraceEnabled()) log.trace("view " + new_view.getVid() + " is discarded as we are not a participant"); } /** * Called by join(). Installs the view returned by calling Coord.handleJoin() and * becomes coordinator. */ private boolean installView(View new_view) { Vector
mems=new_view.getMembers(); if(log.isDebugEnabled()) log.debug("new_view=" + new_view); if(gms.local_addr == null || mems == null || !mems.contains(gms.local_addr)) { if(log.isErrorEnabled()) log.error("I (" + gms.local_addr + ") am not member of " + mems + ", will not install view"); return false; } gms.installView(new_view); gms.becomeParticipant(); gms.getUpProtocol().up(new Event(Event.BECOME_SERVER)); gms.getDownProtocol().down(new Event(Event.BECOME_SERVER)); return true; } /** Returns immediately. Clients don't handle suspect() requests */ // public void handleSuspect(Address mbr) { // } /* --------------------------- Private Methods ------------------------------------ */ void sendJoinMessage(Address coord, Address mbr,boolean joinWithTransfer) { Message msg; GMS.GmsHeader hdr; msg=new Message(coord, null, null); msg.setFlag(Message.OOB); if(joinWithTransfer) hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER, mbr); else hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_REQ, mbr); msg.putHeader(gms.getName(), hdr); // we have to enable unicasts to coord, as coord is not in our membership (the unicast message would get dropped) gms.getDownProtocol().down(new Event(Event.MSG, msg)); } /** The coordinator is determined by a majority vote. If there are an equal number of votes for more than 1 candidate, we determine the winner randomly. */ private Address determineCoord(List mbrs) { int count, most_votes; Address winner=null, tmp; if(mbrs == null || mbrs.size() < 1) return null; Map votes=new HashMap(5); // count *all* the votes (unlike the 2000 election) for(PingRsp mbr:mbrs) { if(mbr.is_server && mbr.coord_addr != null) { if(!votes.containsKey(mbr.coord_addr)) votes.put(mbr.coord_addr, 1); else { count=votes.get(mbr.coord_addr); votes.put(mbr.coord_addr, count + 1); } } } if(votes.size() > 1) { if(log.isWarnEnabled()) log.warn("there was more than 1 candidate for coordinator: " + votes); } else { if(log.isDebugEnabled()) log.debug("election results: " + votes); } // determine who got the most votes most_votes=0; for(Map.Entry entry: votes.entrySet()) { tmp=entry.getKey(); count=entry.getValue(); if(count > most_votes) { winner=tmp; // fixed July 15 2003 (patch submitted by Darren Hobbs, patch-id=771418) most_votes=count; } } votes.clear(); return winner; } void becomeSingletonMember(Address mbr) { Digest initial_digest; ViewId view_id; Vector
mbrs=new Vector
(1); // set the initial digest (since I'm the first member) initial_digest=new Digest(gms.local_addr, 0, 0); // initial seqno mcast by me will be 1 (highest seen +1) gms.setDigest(initial_digest); view_id=new ViewId(mbr); // create singleton view with mbr as only member mbrs.addElement(mbr); gms.installView(new View(view_id, mbrs)); gms.becomeCoordinator(); // not really necessary - installView() should do it gms.getUpProtocol().up(new Event(Event.BECOME_SERVER)); gms.getDownProtocol().down(new Event(Event.BECOME_SERVER)); if(log.isDebugEnabled()) log.debug("created group (first member). My view is " + gms.view_id + ", impl is " + gms.getImpl().getClass().getName()); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/package.html0000644000175000017500000000010411366547366027161 0ustar twernertwerner Supports probabilistic broadcasts. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/CoordGmsImpl.java0000644000175000017500000011115611366547366030114 0ustar twernertwerner// $Id: CoordGmsImpl.java,v 1.82.2.20 2009/09/08 12:25:44 belaban Exp $ package org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Coordinator role of the Group MemberShip (GMS) protocol. Accepts JOIN and LEAVE requests and emits view changes * accordingly. * @author Bela Ban */ public class CoordGmsImpl extends GmsImpl { private volatile boolean merging=false; private final MergeTask merge_task=new MergeTask(); private final Vector merge_rsps=new Vector(11); // for MERGE_REQ/MERGE_RSP correlation, contains MergeData elements private ViewId merge_id=null; @GuardedBy("merge_canceller_lock") private Future merge_canceller_future=null; private final Lock merge_canceller_lock=new ReentrantLock(); /** the max time in ms to suspend message garbage collection */ private final Long MAX_SUSPEND_TIMEOUT=new Long(30000); public CoordGmsImpl(GMS g) { super(g); } private void setMergeId(ViewId merge_id) { this.merge_id=merge_id; if(this.merge_id != null) { stopMergeCanceller(); startMergeCanceller(); } else { // merge completed stopMergeCanceller(); } } private void startMergeCanceller() { merge_canceller_lock.lock(); try { if(merge_canceller_future == null || merge_canceller_future.isDone()) { MergeCanceller task=new MergeCanceller(this.merge_id, gms.merge_timeout); merge_canceller_future=gms.timer.schedule(task, gms.merge_timeout, TimeUnit.MILLISECONDS); } } finally { merge_canceller_lock.unlock(); } } private void stopMergeCanceller() { merge_canceller_lock.lock(); try { if(merge_canceller_future != null) { merge_canceller_future.cancel(true); merge_canceller_future=null; } } finally { merge_canceller_lock.unlock(); } } public void init() throws Exception { super.init(); cancelMerge(); } public void join(Address mbr) { wrongMethod("join"); } public void joinWithStateTransfer(Address mbr) { wrongMethod("join"); } /** The coordinator itself wants to leave the group */ public void leave(Address mbr) { if(mbr == null) { if(log.isErrorEnabled()) log.error("member's address is null !"); return; } if(mbr.equals(gms.local_addr)) leaving=true; gms.getViewHandler().add(new Request(Request.LEAVE, mbr, false, null)); gms.getViewHandler().stop(true); // wait until all requests have been processed, then close the queue and leave gms.getViewHandler().waitUntilCompleted(gms.leave_timeout); } public void handleJoinResponse(JoinRsp join_rsp) { } public void handleLeaveResponse() { } public void suspect(Address mbr) { if(mbr.equals(gms.local_addr)) { if(log.isWarnEnabled()) log.warn("I am the coord and I'm suspected -- will probably leave shortly"); return; } Collection suspected=new LinkedHashSet(1); suspected.add(new Request(Request.SUSPECT,mbr,true,null)); handleMembershipChange(suspected); } public void unsuspect(Address mbr) { } /** * Invoked upon receiving a MERGE event from the MERGE layer. Starts the merge protocol. * See description of protocol in DESIGN. * @param other_coords A list of coordinators (including myself) found by MERGE protocol */ public void merge(Vector
other_coords) { Membership tmp; if(merging) { if(log.isWarnEnabled()) log.warn("merge already in progress, discarded MERGE event (I am " + gms.local_addr + ")"); return; } if(other_coords == null) { if(log.isWarnEnabled()) log.warn("list of other coordinators is null. Will not start merge."); return; } if(other_coords.size() <= 1) { if(log.isErrorEnabled()) log.error("number of coordinators found is " + other_coords.size() + "; will not perform merge"); return; } /* Establish deterministic order, so that coords can elect leader */ tmp=new Membership(other_coords); tmp.sort(); Address merge_leader=tmp.elementAt(0); if(log.isDebugEnabled()) log.debug("Determining merge leader from coordinators: " + tmp); if(merge_leader.equals(gms.local_addr) || gms.merge_leader) { if(log.isDebugEnabled()) log.debug("I (" + gms.local_addr + ") will be the leader. Starting the merge task for " + other_coords); startMergeTask(other_coords); } else { if(log.isDebugEnabled()) log.debug("I (" + gms.local_addr + ") am not the merge leader, " + "waiting for merge leader (" + merge_leader + ") to initiate merge"); } } /** * Get the view and digest and send back both (MergeData) in the form of a MERGE_RSP to the sender. * If a merge is already in progress, send back a MergeData with the merge_rejected field set to true. */ public void handleMergeRequest(Address sender, ViewId merge_id) { Digest digest; View view; if(sender == null) { if(log.isErrorEnabled()) log.error("sender == null; cannot send back a response"); return; } if(merging) { if(log.isErrorEnabled()) log.error("For merge participant " + gms.local_addr +" merge is already in progress"); sendMergeRejectedResponse(sender, merge_id); return; } merging=true; if(log.isDebugEnabled()) log.debug("Suspending view handler at " + gms.local_addr); /* Clears the view handler queue and discards all JOIN/LEAVE/MERGE requests until after the MERGE */ gms.getViewHandler().suspend(merge_id); setMergeId(merge_id); if(log.isDebugEnabled()) log.debug(gms.local_addr + " got merge request from " + sender + ", merge_id=" + merge_id); view=new View(gms.view_id.copy(), gms.members.getMembers()); //[JGRP-524] - FLUSH and merge: flush doesn't wrap entire merge process //[JGRP-770] - Concurrent startup of many channels doesn't stabilize //[JGRP-700] - FLUSH: flushing should span merge /*if flush is in stack, let this coordinator flush its cluster island */ boolean suceesfulFlush = false; try { suceesfulFlush = gms.startFlush(view); } finally { if (suceesfulFlush) { digest = gms.getDigest(); sendMergeResponse(sender, view, digest); if (log.isDebugEnabled()) log.debug(gms.local_addr + " responded to " + sender+ ", merge_id=" + merge_id); } else { sendMergeRejectedResponse(sender, merge_id); gms.getViewHandler().resume(merge_id); merging = false; if (log.isWarnEnabled()) log.warn("Since flush failed at " + gms.local_addr + " rejected merge to " + sender + ", merge_id=" + merge_id); } } } public void handleMergeResponse(MergeData data, ViewId merge_id) { if(merge_id == null || this.merge_id == null) { if(log.isErrorEnabled()) log.error("merge_id (" + merge_id + ") or this.merge_id (" + this.merge_id + ") is null (sender=" + data.getSender() + ")."); return; } if(!this.merge_id.equals(merge_id)) { if(log.isErrorEnabled()) log.error("this.merge_id (" + this.merge_id + ") is different from merge_id (" + merge_id + ')'); return; } synchronized(merge_rsps) { if(!merge_rsps.contains(data)) { merge_rsps.addElement(data); merge_rsps.notifyAll(); } } } /** * If merge_id is not equal to this.merge_id then discard. * Else cast the view/digest to all members of this group. */ public void handleMergeView(final MergeData data,final ViewId merge_id) { if(merge_id == null || this.merge_id == null || !this.merge_id.equals(merge_id)) { if(log.isErrorEnabled()) log.error("merge_ids don't match (or are null); merge view discarded"); return; } // only send to our *current* members, if we have A and B being merged (we are B), then we would *not* // receive a VIEW_ACK from A because A doesn't see us in the pre-merge view yet and discards the view //[JGRP-700] - FLUSH: flushing should span merge //we have to send new view only to current members and we should not wait //for view acks from newly merged mebers List
newViewMembers=new Vector
(data.view.getMembers()); newViewMembers.removeAll(gms.members.getMembers()); gms.castViewChangeWithDest(data.view, data.digest, null, newViewMembers); /* * if we have flush in stack send ack back to merge coordinator * */ if(gms.flushProtocolInStack) { Message ack=new Message(data.getSender(), null, null); ack.setFlag(Message.OOB); GMS.GmsHeader ack_hdr=new GMS.GmsHeader(GMS.GmsHeader.INSTALL_MERGE_VIEW_OK); ack.putHeader(gms.getName(), ack_hdr); gms.getDownProtocol().down(new Event(Event.MSG, ack)); } merging=false; gms.getViewHandler().resume(merge_id); } public void handleMergeCancelled(ViewId merge_id) { gms.stopFlush(); if(merge_id != null && this.merge_id != null && this.merge_id.equals(merge_id)) { if(log.isDebugEnabled()) log.debug("merge was cancelled at merge participant " + gms.local_addr+ " (merge_id="+ merge_id+ ")"); setMergeId(null); merging=false; gms.getViewHandler().resume(merge_id); } else { if(log.isWarnEnabled()) log.warn("merge was supposed to be cancelled at merge participant " + gms.local_addr + " (merge_id=" + merge_id + "), but it is not since merge ids do not match"); } } private void cancelMerge() { Object tmp=merge_id; if(merge_id != null && log.isDebugEnabled()) log.debug("cancelling merge (merge_id=" + merge_id + ')'); setMergeId(null); stopMergeTask(); merging=false; synchronized(merge_rsps) { merge_rsps.clear(); } gms.getViewHandler().resume(tmp); } public void handleMembershipChange(Collection requests) { boolean joinAndStateTransferInitiated=false; Collection
new_mbrs=new LinkedHashSet
(requests.size()); Collection
suspected_mbrs=new LinkedHashSet
(requests.size()); Collection
leaving_mbrs=new LinkedHashSet
(requests.size()); for(Request req: requests) { switch(req.type) { case Request.JOIN: new_mbrs.add(req.mbr); break; case Request.JOIN_WITH_STATE_TRANSFER: new_mbrs.add(req.mbr); joinAndStateTransferInitiated=true; break; case Request.LEAVE: if(req.suspected) suspected_mbrs.add(req.mbr); else leaving_mbrs.add(req.mbr); break; case Request.SUSPECT: suspected_mbrs.add(req.mbr); break; } } new_mbrs.remove(gms.local_addr); // remove myself - cannot join myself (already joined) if(gms.view_id == null) { // we're probably not the coord anymore (we just left ourselves), let someone else do it // (client will retry when it doesn't get a response) if(log.isDebugEnabled()) log.debug("gms.view_id is null, I'm not the coordinator anymore (leaving=" + leaving + "); the new coordinator will handle the leave request"); return; } Vector
current_members=gms.members.getMembers(); leaving_mbrs.retainAll(current_members); // remove all elements of leaving_mbrs which are not current members if(suspected_mbrs.remove(gms.local_addr)) { if(log.isWarnEnabled()) log.warn("I am the coord and I'm being suspected -- will probably leave shortly"); } suspected_mbrs.retainAll(current_members); // remove all elements of suspected_mbrs which are not current members // for the members that have already joined, return the current digest and membership for(Iterator
it=new_mbrs.iterator(); it.hasNext();) { Address mbr=it.next(); if(gms.members.contains(mbr)) { // already joined: return current digest and membership JoinRsp join_rsp; if(gms.reject_join_from_existing_member) { join_rsp=new JoinRsp("member " + mbr + " is already part of the group, JOIN request is rejected"); } else { if(log.isWarnEnabled()) log.warn(mbr + " already present; returning existing view " + gms.view); join_rsp=new JoinRsp(new View(gms.view_id, gms.members.getMembers()), gms.getDigest()); } gms.sendJoinResponse(join_rsp, mbr); it.remove(); } } if(new_mbrs.isEmpty() && leaving_mbrs.isEmpty() && suspected_mbrs.isEmpty()) { if(log.isTraceEnabled()) log.trace("found no members to add or remove, will not create new view"); return; } View new_view=gms.getNextView(new_mbrs, leaving_mbrs, suspected_mbrs); gms.up(new Event(Event.PREPARE_VIEW,new_view)); gms.down(new Event(Event.PREPARE_VIEW,new_view)); if(log.isDebugEnabled()) log.debug("new=" + new_mbrs + ", suspected=" + suspected_mbrs + ", leaving=" + leaving_mbrs + ", new view: " + new_view); JoinRsp join_rsp=null; boolean hasJoiningMembers=!new_mbrs.isEmpty(); try { boolean successfulFlush = gms.startFlush(new_view); if(!successfulFlush && hasJoiningMembers){ //see http://jira.jboss.org/jira/browse/JGRP-759 //We should NOT send back a join response if the flush fails. //The joiner should block until the previous FLUSH completed //we still have to send potential leave responses sendLeaveResponses(leaving_mbrs); //but let the joining client timeout and send another join request return; } // we cannot garbage collect during joining a new member *if* we're the only member // Example: {A}, B joins, after returning JoinRsp to B, A garbage collects messages higher than those // in the digest returned to the client, so the client will *not* be able to ask for retransmission // of those messages if he misses them if(hasJoiningMembers) { gms.getDownProtocol().down(new Event(Event.SUSPEND_STABLE, MAX_SUSPEND_TIMEOUT)); Digest tmp=gms.getDigest(); // get existing digest MutableDigest join_digest=null; if(tmp == null){ log.error("received null digest from GET_DIGEST: will cause JOIN to fail"); } else { // create a new digest, which contains the new member join_digest=new MutableDigest(tmp.size() + new_mbrs.size()); join_digest.add(tmp); // add the existing digest to the new one for(Address member:new_mbrs) join_digest.add(member, 0, 0); // ... and add the new members. their first seqno will be 1 } join_rsp=new JoinRsp(new_view, join_digest != null? join_digest.copy() : null); } sendLeaveResponses(leaving_mbrs); // no-op if no leaving members gms.castViewChangeWithDest(new_view, null,join_rsp,new_mbrs); } finally { if(hasJoiningMembers) gms.getDownProtocol().down(new Event(Event.RESUME_STABLE)); if(!joinAndStateTransferInitiated) gms.stopFlush(); if(leaving) { gms.initState(); // in case connect() is called again } } } /** * Called by the GMS when a VIEW is received. * @param new_view The view to be installed * @param digest If view is a MergeView, digest contains the seqno digest of all members and has to * be set by GMS */ public void handleViewChange(View new_view, Digest digest) { Vector
mbrs=new_view.getMembers(); if(log.isDebugEnabled()) { if(digest != null) log.debug("view=" + new_view + ", digest=" + digest); else log.debug("view=" + new_view); } if(leaving && !mbrs.contains(gms.local_addr)) return; gms.installView(new_view, digest); } public void handleExit() { cancelMerge(); } public void stop() { super.stop(); // sets leaving=false stopMergeTask(); } /* ------------------------------------------ Private methods ----------------------------------------- */ void startMergeTask(Vector
coords) { synchronized(merge_task) { merge_task.start(coords); } } void stopMergeTask() { synchronized(merge_task) { merge_task.stop(); } } private void sendLeaveResponses(Collection
leaving_members) { for(Address address:leaving_members){ Message msg=new Message(address, null, null); // send an ack to the leaving member msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.LEAVE_RSP); msg.putHeader(gms.getName(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } } /** * Sends a MERGE_REQ to all coords and populates a list of MergeData (in merge_rsps). Returns after coords.size() * response have been received, or timeout msecs have elapsed (whichever is first).

* If a subgroup coordinator rejects the MERGE_REQ (e.g. because of participation in a different merge), * that member will be removed from coords ! * @param coords A list of Addresses of subgroup coordinators (inluding myself) * @param timeout Max number of msecs to wait for the merge responses from the subgroup coords */ private boolean getMergeDataFromSubgroupCoordinators(final Vector

coords, long timeout) { boolean gotAllResponses = false; long start=System.currentTimeMillis(); synchronized(merge_rsps) { merge_rsps.removeAllElements(); if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " sending MERGE_REQ to " + coords); for(Address coord:coords) { // this allows UNICAST to remove coord from previous_members in case of a merge Message msg=new Message(coord, null, null); msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_REQ); hdr.mbr=gms.local_addr; hdr.merge_id=merge_id; msg.putHeader(gms.getName(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " sent MERGE_REQ to " + coord); } // wait until num_rsps_expected >= num_rsps or timeout elapsed int num_rsps_expected=coords.size(); long curr_time=System.currentTimeMillis(); long end_time=curr_time + timeout; while(end_time > curr_time && !gotAllResponses) { long time_to_wait=end_time - curr_time; if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr +" waiting " + time_to_wait + " msecs for merge responses"); if(merge_rsps.size() < num_rsps_expected) { try { merge_rsps.wait(500); } catch(Exception ex) { } } if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr +" expects " + num_rsps_expected + " responses, so far got " + merge_rsps.size() + " responses"); gotAllResponses = merge_rsps.size() >= num_rsps_expected; curr_time=System.currentTimeMillis(); } long stop=System.currentTimeMillis(); if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " collected " + merge_rsps.size() + " merge response(s) in " + (stop-start) + " ms"); } return gotAllResponses; } /** * Generates a unique merge id by taking the local address and the current time */ private ViewId generateMergeId() { return new ViewId(gms.local_addr, System.currentTimeMillis()); // we're (ab)using ViewId as a merge id } /** * Merge all MergeData. All MergeData elements should be disjunct (both views and digests). However, * this method is prepared to resolve duplicate entries (for the same member). Resolution strategy for * views is to merge only 1 of the duplicate members. Resolution strategy for digests is to take the higher * seqnos for duplicate digests.

* After merging all members into a Membership and subsequent sorting, the first member of the sorted membership * will be the new coordinator. This method has a lock on merge_rsps. * @param merge_rsps A list of MergeData items. Elements with merge_rejected=true were removed before. Is guaranteed * not to be null and to contain at least 1 member. */ private MergeData consolidateMergeData(Vector merge_rsps) { MergeData ret; long logical_time=0; // for new_vid ViewId new_vid, tmp_vid; MergeView new_view; View tmp_view; Membership new_mbrs=new Membership(); Address new_coord; Vector subgroups=new Vector(11); // contains a list of Views, each View is a subgroup for(MergeData tmp_data:merge_rsps) { if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " is consolidating merge data " + tmp_data); tmp_view=tmp_data.getView(); if(tmp_view != null) { tmp_vid=tmp_view.getVid(); if(tmp_vid != null) { // compute the new view id (max of all vids +1) logical_time=Math.max(logical_time, tmp_vid.getId()); } // merge all membership lists into one (prevent duplicates) new_mbrs.add(tmp_view.getMembers()); subgroups.addElement((View)tmp_view.clone()); } } // the new coordinator is the first member of the consolidated & sorted membership list new_mbrs.sort(); new_coord = new_mbrs.size() > 0 ? new_mbrs.elementAt(0) : null; if(new_coord == null) { if(log.isErrorEnabled()) log.error("new_coord == null"); return null; } // should be the highest view ID seen up to now plus 1 new_vid=new ViewId(new_coord, logical_time + 1); // determine the new view new_view=new MergeView(new_vid, new_mbrs.getMembers(), subgroups); if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " computed new merged view that will be " + new_view); // determine the new digest Digest new_digest=consolidateDigests(merge_rsps, new_mbrs.size()); if(new_digest == null) { if(log.isErrorEnabled()) log.error("Merge leader " + gms.local_addr + "could not consolidate digest for merge"); return null; } if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + "consolidated digest=" + new_digest); ret=new MergeData(gms.local_addr, new_view, new_digest); return ret; } /** * Merge all digests into one. For each sender, the new value is min(low_seqno), max(high_seqno), * max(high_seqno_seen). This method has a lock on merge_rsps */ private Digest consolidateDigests(Vector merge_rsps, int num_mbrs) { MutableDigest retval=new MutableDigest(num_mbrs); for(MergeData data:merge_rsps) { Digest tmp_digest=data.getDigest(); if(tmp_digest == null) { if(log.isErrorEnabled()) log.error("tmp_digest == null; skipping"); continue; } retval.merge(tmp_digest); } return retval.copy(); } /** * Sends the new view and digest to all subgroup coordinors in coords. Each coord will in turn *

    *
  1. cast the new view and digest to all the members of its subgroup (MergeView) *
  2. on reception of the view, if it is a MergeView, each member will set the digest and install * the new view *
*/ private void sendMergeView(Vector
coords, MergeData combined_merge_data) { View v; Digest d; if(coords == null || combined_merge_data == null) return; v=combined_merge_data.view; d=combined_merge_data.digest; if(v == null || d == null) { if(log.isErrorEnabled()) log.error("view or digest is null, cannot send consolidated merge view/digest"); return; } if(log.isDebugEnabled()) log.debug(gms.local_addr + " is sending merge view " + v.getVid() + " to coordinators " + coords); gms.merge_ack_collector.reset(coords); int size=gms.merge_ack_collector.size(); long timeout=gms.view_ack_collection_timeout; long start = System.currentTimeMillis(); for(Address coord:coords) { Message msg=new Message(coord, null, null); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.INSTALL_MERGE_VIEW); hdr.view=v; hdr.my_digest=d; hdr.merge_id=merge_id; msg.putHeader(gms.getName(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } //[JGRP-700] - FLUSH: flushing should span merge //if flush is in stack wait for acks from separated island coordinators if(gms.flushProtocolInStack) { try { gms.merge_ack_collector.waitForAllAcks(timeout); long stop=System.currentTimeMillis(); if(log.isTraceEnabled()) log.trace("received all ACKs (" + size + ") for merged view " + v + " in " + (stop - start) + "ms"); } catch(TimeoutException e) { log.warn("Merge coordinator " + gms.local_addr + " failed to collect all ACKs for merge (" + size + ") for view " + v + " after " + timeout + "ms, missing ACKs from " + gms.merge_ack_collector.printMissing() + ", local_addr=" + gms.local_addr); } } } /** * Send back a response containing view and digest to sender */ private void sendMergeResponse(Address sender, View view, Digest digest) { Message msg=new Message(sender, null, null); msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_RSP); hdr.merge_id=merge_id; hdr.view=view; hdr.my_digest=digest; msg.putHeader(gms.getName(), hdr); if(log.isDebugEnabled()) log.debug("response=" + hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } private void sendMergeCancelledMessage(Vector
coords, ViewId merge_id) { if(coords == null || merge_id == null) { if(log.isErrorEnabled()) log.error("coords or merge_id == null"); return; } for(Address coord:coords) { Message msg=new Message(coord, null, null); // msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.CANCEL_MERGE); hdr.merge_id=merge_id; msg.putHeader(gms.getName(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " send cancel merge to " + coord); } } /** Removed rejected merge requests from merge_rsps and coords. This method has a lock on merge_rsps */ private void removeRejectedMergeRequests(Vector
coords) { for(Iterator it=merge_rsps.iterator(); it.hasNext();) { MergeData data=it.next(); if(data.merge_rejected) { if(data.getSender() != null && coords != null) coords.removeElement(data.getSender()); it.remove(); if(log.isDebugEnabled()) log.debug("removed element " + data); } } } /* --------------------------------------- End of Private methods ------------------------------------- */ /** * Starts the merge protocol (only run by the merge leader). Essentially sends a MERGE_REQ to all * coordinators of all subgroups found. Each coord receives its digest and view and returns it. * The leader then computes the digest and view for the new group from the return values. Finally, it * sends this merged view/digest to all subgroup coordinators; each coordinator will install it in their * subgroup. */ private class MergeTask implements Runnable { Thread t=null; Vector
coords=null; // list of subgroup coordinators to be contacted public void start(Vector
groupCoord) { this.coords = groupCoord != null ? new Vector
(groupCoord) : null; if(!isRunning()) { t=gms.getThreadFactory().newThread(this, "MergeTask"); t.setDaemon(true); t.start(); } } public void stop() { Thread tmp=t; if(isRunning()) { t=null; tmp.interrupt(); } t=null; } public boolean isRunning() { return t != null && t.isAlive(); } /** * Runs the merge protocol as a leader */ public void run() { if(merging) { if(log.isWarnEnabled()) log.warn(gms.local_addr + " running merge task, but merge is is already in progress, terminating"); return; } if(coords == null || coords.size() <= 1) { if(log.isErrorEnabled()) log.error("coords == null or size <= 1"); return; } if(log.isDebugEnabled()) log.debug(gms.local_addr + " running merge task, coordinators are " + this.coords); Vector
coordsCopy=new Vector
(coords); /* 1. Generate a merge_id that uniquely identifies the merge in progress */ ViewId generatedMergeId=generateMergeId(); try { setMergeId(generatedMergeId); /* 2. Fetch the current Views/Digests from all subgroup coordinators */ boolean success=getMergeDataFromSubgroupCoordinators(coords, gms.merge_timeout); if(!success) { throw new Exception("Merge aborted. Merge leader did not get MergeData from all subgroup coordinators " + coords); } /* * 3. Remove rejected MergeData elements from merge_rsp and * coords (so we'll send the new view only to members who * accepted the merge request) */ MergeData combined_merge_data=null; synchronized(merge_rsps) { removeRejectedMergeRequests(coords); if(merge_rsps.size() <= 1) { throw new Exception("Merge leader " + gms.local_addr + " did not get all merge responses from subgroup coordinators (" + merge_rsps + ")"); } else { /* 4. Combine all views and digests into 1 View/1 Digest */ combined_merge_data=consolidateMergeData(merge_rsps); if(combined_merge_data == null) { throw new Exception("Merge leader " + gms.local_addr + " could not consolidate merge"); } } } /* 4. Send the new View/Digest to all coordinators (including myself). On reception, they will install the digest and view in all of their subgroup members */ sendMergeView(coords, combined_merge_data); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn(ex.getLocalizedMessage()); sendMergeCancelledMessage(coordsCopy, generatedMergeId); } finally { gms.getViewHandler().resume(generatedMergeId); stopMergeCanceller(); // this is probably not necessary /*5. if flush is in stack stop the flush for entire cluster [JGRP-700] - FLUSH: flushing should span merge */ gms.stopFlush(); merging=false; if(log.isDebugEnabled()) log.debug("Merge leader " + gms.local_addr + " completed merge task"); t=null; } } } private class MergeCanceller implements Runnable { private Object my_merge_id=null; private long timeout; MergeCanceller(Object my_merge_id, long timeout) { this.my_merge_id=my_merge_id; this.timeout=timeout; } public void run() { if(merge_id != null && my_merge_id.equals(merge_id)) { if(log.isDebugEnabled()) log.debug("At " + gms.local_addr + " cancelling merge due to timer timeout (" + timeout + " ms)"); cancelMerge(); } else { if(log.isWarnEnabled()) log.warn("At " + gms.local_addr +" timer kicked in after " + timeout + " ms, but no (or different) merge was in progress: " + "merge_id=" + merge_id + ", my_merge_id=" + my_merge_id); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/GMS.java0000644000175000017500000016226211366547366026207 0ustar twernertwerner package org.jgroups.protocols.pbcast; import org.apache.commons.logging.Log; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.jgroups.util.Queue; import java.io.*; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.jgroups.protocols.pbcast.GmsImpl.Request; /** * Group membership protocol. Handles joins/leaves/crashes (suspicions) and emits new views * accordingly. Use VIEW_ENFORCER on top of this layer to make sure new members don't receive * any messages until they are members * @author Bela Ban * @version $Id: GMS.java,v 1.126.2.25 2009/12/15 17:08:30 belaban Exp $ */ public class GMS extends Protocol { private GmsImpl impl=null; Address local_addr=null; final Membership members=new Membership(); // real membership private final Membership tmp_members=new Membership(); // base for computing next view /** Members joined but for which no view has been received yet */ private final Vector
joining=new Vector
(7); /** Members excluded from group, but for which no view has been received yet */ private final Vector
leaving=new Vector
(7); View view=null; ViewId view_id=null; private long ltime=0; long join_timeout=5000; long leave_timeout=5000; long merge_timeout=5000; // time to wait for all MERGE_RSPS private final Object impl_mutex=new Object(); // synchronizes event entry into impl private final Hashtable impls=new Hashtable(3); private boolean shun=false; boolean merge_leader=false; // can I initiate a merge ? private boolean print_local_addr=true; boolean disable_initial_coord=false; // can the member become a coord on startup or not ? /** Setting this to false disables concurrent startups. This is only used by unit testing code * for testing merging. To everybody else: don't change it to false ! */ boolean handle_concurrent_startup=true; /** Whether view bundling (http://jira.jboss.com/jira/browse/JGRP-144) should be enabled or not. Setting this to * false forces each JOIN/LEAVE/SUPSECT request to be handled separately. By default these requests are processed * together if they are queued at approximately the same time */ private boolean view_bundling=true; private long max_bundling_time=50; // 50ms max to wait for other JOIN, LEAVE or SUSPECT requests static final String CLIENT="Client"; static final String COORD="Coordinator"; static final String PART="Participant"; TimeScheduler timer=null; /** Max number of old members to keep in history */ protected int num_prev_mbrs=50; /** Keeps track of old members (up to num_prev_mbrs) */ BoundedList
prev_members=null; /** If we receive a JOIN request from P and P is already in the current membership, then we send back a JOIN * response with an error message when this property is set to true (Channel.connect() will fail). Otherwise, * we return the current view */ boolean reject_join_from_existing_member=true; int num_views=0; /** Stores the last 20 views */ BoundedList prev_views=new BoundedList(20); /** Class to process JOIN, LEAVE and MERGE requests */ private final ViewHandler view_handler=new ViewHandler(); private Class> flushInvokerClass; /** To collect VIEW_ACKs from all members */ final AckCollector ack_collector=new AckCollector(); //[JGRP-700] - FLUSH: flushing should span merge final AckCollector merge_ack_collector=new AckCollector(); /** Time in ms to wait for all VIEW acks (0 == wait forever) */ long view_ack_collection_timeout=2000; /** How long should a Resumer wait until resuming the ViewHandler */ long resume_task_timeout=20000; boolean flushProtocolInStack=false; public static final String name="GMS"; public GMS() { initState(); } public String getName() { return name; } public String getView() {return view_id != null? view_id.toString() : "null";} public int getNumberOfViews() {return num_views;} public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} public String getMembers() {return members != null? members.toString() : "[]";} public int getNumMembers() {return members != null? members.size() : 0;} public long getJoinTimeout() {return join_timeout;} public void setJoinTimeout(long t) {join_timeout=t;} /** @deprecated */ public long getJoinRetryTimeout() {return -1;} /** @deprecated */ public void setJoinRetryTimeout(long t) {} public boolean isShun() {return shun;} public void setShun(boolean s) {shun=s;} public boolean isPrintLocalAddr() { return print_local_addr; } public void setPrintLocalAddr(boolean print_local_addr) { this.print_local_addr=print_local_addr; } public String printPreviousMembers() { StringBuilder sb=new StringBuilder(); if(prev_members != null) { for(Address addr: prev_members) { sb.append(addr).append("\n"); } } return sb.toString(); } public long getViewAckCollectionTimeout() { return view_ack_collection_timeout; } public void setViewAckCollectionTimeout(long view_ack_collection_timeout) { this.view_ack_collection_timeout=view_ack_collection_timeout; } public boolean isViewBundling() { return view_bundling; } public void setViewBundling(boolean view_bundling) { this.view_bundling=view_bundling; } public long getMaxBundlingTime() { return max_bundling_time; } public void setMaxBundlingTime(long max_bundling_time) { this.max_bundling_time=max_bundling_time; } public int viewHandlerSize() {return view_handler.size();} public boolean isViewHandlerSuspended() {return view_handler.suspended();} public String dumpViewHandlerQueue() { return view_handler.dumpQueue(); } public String dumpViewHandlerHistory() { return view_handler.dumpHistory(); } public void suspendViewHandler() { view_handler.suspend(null); } public void resumeViewHandler() { view_handler.resumeForce(); } Log getLog() {return log;} ViewHandler getViewHandler() {return view_handler;} public String printPreviousViews() { StringBuilder sb=new StringBuilder(); for(View view: prev_views) { sb.append(view).append("\n"); } return sb.toString(); } public boolean isCoordinator() { Address coord=determineCoordinator(); return coord != null && local_addr != null && local_addr.equals(coord); } public void resetStats() { super.resetStats(); num_views=0; prev_views.clear(); } public Vector requiredDownServices() { Vector retval=new Vector(3); retval.addElement(new Integer(Event.GET_DIGEST)); retval.addElement(new Integer(Event.SET_DIGEST)); retval.addElement(new Integer(Event.FIND_INITIAL_MBRS)); return retval; } public void setImpl(GmsImpl new_impl) { synchronized(impl_mutex) { if(impl == new_impl) // superfluous return; impl=new_impl; if(log.isDebugEnabled()) { String msg=(local_addr != null? local_addr.toString()+" " : "") + "changed role to " + new_impl.getClass().getName(); log.debug(msg); } } } public GmsImpl getImpl() { return impl; } public void init() throws Exception { prev_members=new BoundedList
(num_prev_mbrs); timer=getTransport().getTimer(); if(timer == null) throw new Exception("GMS.init(): timer is null"); if(impl != null) impl.init(); } public void start() throws Exception { if(impl != null) impl.start(); } public void stop() { view_handler.stop(true); if(impl != null) impl.stop(); if(prev_members != null) prev_members.clear(); } public void becomeCoordinator() { CoordGmsImpl tmp=(CoordGmsImpl)impls.get(COORD); if(tmp == null) { tmp=new CoordGmsImpl(this); impls.put(COORD, tmp); } try { tmp.init(); } catch(Exception e) { log.error("exception switching to coordinator role", e); } setImpl(tmp); } public void becomeParticipant() { ParticipantGmsImpl tmp=(ParticipantGmsImpl)impls.get(PART); if(tmp == null) { tmp=new ParticipantGmsImpl(this); impls.put(PART, tmp); } try { tmp.init(); } catch(Exception e) { log.error("exception switching to participant", e); } setImpl(tmp); } public void becomeClient() { ClientGmsImpl tmp=(ClientGmsImpl)impls.get(CLIENT); if(tmp == null) { tmp=new ClientGmsImpl(this); impls.put(CLIENT, tmp); } try { tmp.init(); } catch(Exception e) { log.error("exception switching to client role", e); } setImpl(tmp); } boolean haveCoordinatorRole() { return impl != null && impl instanceof CoordGmsImpl; } /** * Computes the next view. Returns a copy that has old_mbrs and * suspected_mbrs removed and new_mbrs added. */ public View getNextView(Collection
new_mbrs, Collection
old_mbrs, Collection
suspected_mbrs) { Vector
mbrs; long vid; View v; Membership tmp_mbrs; synchronized(members) { if(view_id == null) { log.error("view_id is null"); return null; // this should *never* happen ! } vid=Math.max(view_id.getId(), ltime) + 1; ltime=vid; tmp_mbrs=tmp_members.copy(); // always operate on the temporary membership tmp_mbrs.remove(suspected_mbrs); tmp_mbrs.remove(old_mbrs); tmp_mbrs.add(new_mbrs); mbrs=tmp_mbrs.getMembers(); v=new View(local_addr, vid, mbrs); // Update membership (see DESIGN for explanation): tmp_members.set(mbrs); // Update joining list (see DESIGN for explanation) if(new_mbrs != null) { for(Iterator
it=new_mbrs.iterator(); it.hasNext();) { Address tmp_mbr=it.next(); if(!joining.contains(tmp_mbr)) joining.addElement(tmp_mbr); } } // Update leaving list (see DESIGN for explanations) if(old_mbrs != null) { for(Iterator
it=old_mbrs.iterator(); it.hasNext();) { Address addr=it.next(); if(!leaving.contains(addr)) leaving.add(addr); } } if(suspected_mbrs != null) { for(Iterator
it=suspected_mbrs.iterator(); it.hasNext();) { Address addr=it.next(); if(!leaving.contains(addr)) leaving.add(addr); } } return v; } } /** * Broadcasts the new view and digest, and waits for acks from all members in the list given as argument. * If the list is null, we take the members who are part of new_view * @param new_view * @param digest * @param newMembers */ public void castViewChangeWithDest(View new_view, Digest digest, JoinRsp jr, Collection
newMembers) { if(log.isTraceEnabled()) log.trace("mcasting view {" + new_view + "} (" + new_view.size() + " mbrs)\n"); Message view_change_msg=new Message(); // bcast to all members GmsHeader hdr=new GmsHeader(GmsHeader.VIEW, new_view); hdr.my_digest=digest; view_change_msg.putHeader(name, hdr); List
ackMembers = new ArrayList
(new_view.getMembers()); if(newMembers != null && !newMembers.isEmpty()) { ackMembers.removeAll(newMembers); } ack_collector.reset(ackMembers); // Send down a local TMP_VIEW event. This is needed by certain layers (e.g. NAKACK) to compute correct digest // in case client's next request (e.g. getState()) reaches us *before* our own view change multicast. // Check NAKACK's TMP_VIEW handling for details down_prot.up(new Event(Event.TMP_VIEW, new_view)); down_prot.down(new Event(Event.TMP_VIEW, new_view)); down_prot.down(new Event(Event.MSG, view_change_msg)); try { ack_collector.waitForAllAcks(view_ack_collection_timeout); if(log.isTraceEnabled()) log.trace("received all ACKs (" + ack_collector.size() + ") for " + new_view.getVid()); } catch(TimeoutException e) { log.warn(local_addr + " failed to collect all ACKs (" + ack_collector.size() + ") for mcasted view " + new_view + " after " + view_ack_collection_timeout + "ms, missing ACKs from " + ack_collector.printMissing() + ", local_addr=" + local_addr); } if(jr != null && (newMembers != null && !newMembers.isEmpty())) { ack_collector.reset(new ArrayList
(newMembers)); for(Address joiner:newMembers) { sendJoinResponse(jr, joiner); } try { ack_collector.waitForAllAcks(view_ack_collection_timeout); if(log.isTraceEnabled()) log.trace("received all ACKs (" + ack_collector.size() + ") for " + new_view.getVid()); } catch(TimeoutException e) { log.warn(local_addr + " failed to collect all ACKs (" + ack_collector.size() + ") for unicasted view " + new_view + " after " + view_ack_collection_timeout + "ms, missing ACKs from " + ack_collector.printMissing() + ", local_addr=" + local_addr); } } } public void sendJoinResponse(JoinRsp rsp, Address dest) { Message m=new Message(dest, null, null); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP, rsp); m.putHeader(getName(), hdr); getDownProtocol().down(new Event(Event.MSG, m)); } public void installView(View new_view) { installView(new_view, null); } /** * Sets the new view and sends a VIEW_CHANGE event up and down the stack. If the view is a MergeView (subclass * of View), then digest will be non-null and has to be set before installing the view. */ public void installView(View new_view, Digest digest) { Address coord; int rc; ViewId vid=new_view.getVid(); Vector
mbrs=new_view.getMembers(); // Discards view with id lower than our own. Will be installed without check if first view if(view_id != null) { rc=vid.compareTo(view_id); if(rc <= 0) { if(log.isWarnEnabled() && rc < 0) // only scream if view is smaller, silently discard same views log.warn("[" + local_addr + "] received view < current view;" + " discarding it (current vid: " + view_id + ", new vid: " + vid + ')'); return; } } if(digest != null) mergeDigest(digest); if(log.isDebugEnabled()) log.debug("[local_addr=" + local_addr + "] view is " + new_view); if(stats) { num_views++; prev_views.add(new_view); } ack_collector.handleView(new_view); merge_ack_collector.handleView(new_view); ltime=Math.max(vid.getId(), ltime); // compute Lamport logical time /* Check for self-inclusion: if I'm not part of the new membership, I just discard it. This ensures that messages sent in view V1 are only received by members of V1 */ if(checkSelfInclusion(mbrs) == false) { // only shun if this member was previously part of the group. avoids problem where multiple // members (e.g. X,Y,Z) join {A,B} concurrently, X is joined first, and Y and Z get view // {A,B,X}, which would cause Y and Z to be shunned as they are not part of the membership // bela Nov 20 2003 if(shun && local_addr != null && prev_members.contains(local_addr)) { if(log.isWarnEnabled()) log.warn("I (" + local_addr + ") am not a member of view " + new_view + ", shunning myself and leaving the group (prev_members are " + prev_members + ", current view is " + view + ")"); if(impl != null) impl.handleExit(); up_prot.up(new Event(Event.EXIT)); } else { if(log.isWarnEnabled()) log.warn("I (" + local_addr + ") am not a member of view " + new_view + "; discarding view"); } return; } synchronized(members) { // serialize access to views // assign new_view to view_id if(new_view instanceof MergeView) view=new View(new_view.getVid(), new_view.getMembers()); else view=new_view; view_id=vid.copy(); // Set the membership. Take into account joining members if(mbrs != null && !mbrs.isEmpty()) { members.set(mbrs); tmp_members.set(members); joining.removeAll(mbrs); // remove all members in mbrs from joining // remove all elements from 'leaving' that are not in 'mbrs' leaving.retainAll(mbrs); tmp_members.add(joining); // add members that haven't yet shown up in the membership tmp_members.remove(leaving); // remove members that haven't yet been removed from the membership // add to prev_members for(Iterator
it=mbrs.iterator(); it.hasNext();) { Address addr=it.next(); if(!prev_members.contains(addr)) prev_members.add(addr); } } // Send VIEW_CHANGE event up and down the stack: Event view_event=new Event(Event.VIEW_CHANGE, new_view); // changed order of passing view up and down (http://jira.jboss.com/jira/browse/JGRP-347) // changed it back (bela Sept 4 2007): http://jira.jboss.com/jira/browse/JGRP-564 down_prot.down(view_event); // needed e.g. by failure detector or UDP up_prot.up(view_event); coord=determineCoordinator(); // if(coord != null && coord.equals(local_addr) && !(coord.equals(vid.getCoordAddress()))) { // changed on suggestion by yaronr and Nicolas Piedeloupe if(coord != null && coord.equals(local_addr) && !haveCoordinatorRole()) { becomeCoordinator(); } else { if(haveCoordinatorRole() && !local_addr.equals(coord)) { becomeParticipant(); merge_ack_collector.reset(null); } } } } protected Address determineCoordinator() { synchronized(members) { return members != null && members.size() > 0? members.elementAt(0) : null; } } /** Checks whether the potential_new_coord would be the new coordinator (2nd in line) */ protected boolean wouldBeNewCoordinator(Address potential_new_coord) { Address new_coord; if(potential_new_coord == null) return false; synchronized(members) { if(members.size() < 2) return false; new_coord=members.elementAt(1); // member at 2nd place return new_coord != null && new_coord.equals(potential_new_coord); } } /** Returns true if local_addr is member of mbrs, else false */ protected boolean checkSelfInclusion(Vector
mbrs) { Object mbr; if(mbrs == null) return false; for(int i=0; i < mbrs.size(); i++) { mbr=mbrs.elementAt(i); if(mbr != null && local_addr.equals(mbr)) return true; } return false; } public View makeView(Vector
mbrs) { Address coord=null; long id=0; if(view_id != null) { coord=view_id.getCoordAddress(); id=view_id.getId(); } return new View(coord, id, mbrs); } public static View makeView(Vector
mbrs, ViewId vid) { Address coord=null; long id=0; if(vid != null) { coord=vid.getCoordAddress(); id=vid.getId(); } return new View(coord, id, mbrs); } /** Send down a SET_DIGEST event */ public void setDigest(Digest d) { down_prot.down(new Event(Event.SET_DIGEST, d)); } /** Send down a MERGE_DIGEST event */ public void mergeDigest(Digest d) { down_prot.down(new Event(Event.MERGE_DIGEST, d)); } /** Sends down a GET_DIGEST event and waits for the GET_DIGEST_OK response, or timeout, whichever occurs first */ public Digest getDigest() { return (Digest)down_prot.down(Event.GET_DIGEST_EVT); } boolean startFlush(final View new_view) { if (flushInvokerClass == null) { Callable invoker = new Callable() { public Boolean call() throws Exception { int maxAttempts = 4; long randomFloor = 1000L; long randomCeiling = 5000L; boolean successfulFlush = true; boolean validView = new_view != null && new_view.size() > 0; if (validView && flushProtocolInStack) { int attemptCount = 0; while (attemptCount < maxAttempts) { successfulFlush = (Boolean) up_prot.up(new Event(Event.SUSPEND, new ArrayList
(new_view.getMembers()))); if (successfulFlush) break; Util.sleepRandom(randomFloor, randomCeiling); attemptCount++; } if (successfulFlush) { if (log.isTraceEnabled()) log.trace("Successful GMS flush by coordinator at " + getLocalAddress()); } else { if (log.isWarnEnabled()) log.warn("GMS flush by coordinator at " + getLocalAddress() + " failed"); } } return successfulFlush; } }; try { return invoker.call(); } catch (Exception e) { return false; } } else { Callable invoker = null; try { invoker = flushInvokerClass.getDeclaredConstructor(View.class).newInstance(new_view); return invoker.call(); } catch (Exception e) { return false; } } } void stopFlush() { if(flushProtocolInStack) { if(log.isDebugEnabled()) { log.debug(getLocalAddress() + " sending RESUME event"); } up_prot.up(new Event(Event.RESUME)); } } void stopFlush(List
members) { if(log.isDebugEnabled()){ log.debug(getLocalAddress() + " sending RESUME event"); } up_prot.up(new Event(Event.RESUME,members)); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); GmsHeader hdr=(GmsHeader)msg.getHeader(name); if(hdr == null) break; switch(hdr.type) { case GmsHeader.JOIN_REQ: view_handler.add(new Request(Request.JOIN, hdr.mbr, false, null)); break; case GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: view_handler.add(new Request(Request.JOIN_WITH_STATE_TRANSFER, hdr.mbr, false, null)); break; case GmsHeader.JOIN_RSP: impl.handleJoinResponse(hdr.join_rsp); break; case GmsHeader.LEAVE_REQ: if(log.isDebugEnabled()) log.debug("received LEAVE_REQ for " + hdr.mbr + " from " + msg.getSrc()); if(hdr.mbr == null) { if(log.isErrorEnabled()) log.error("LEAVE_REQ's mbr field is null"); return null; } view_handler.add(new Request(Request.LEAVE, hdr.mbr, false, null)); break; case GmsHeader.LEAVE_RSP: impl.handleLeaveResponse(); break; case GmsHeader.VIEW: View new_view=hdr.view; if(new_view == null) { if(log.isErrorEnabled()) log.error("[VIEW]: view == null"); return null; } Address coord=msg.getSrc(); if(!new_view.containsMember(coord)) { sendViewAck(coord); // we need to send the ack first, otherwise the connection is removed impl.handleViewChange(new_view, hdr.my_digest); } else { impl.handleViewChange(new_view, hdr.my_digest); sendViewAck(coord); // send VIEW_ACK to sender of view } break; case GmsHeader.VIEW_ACK: Address sender=msg.getSrc(); ack_collector.ack(sender); return null; // don't pass further up case GmsHeader.MERGE_REQ: down_prot.down(new Event(Event.SUSPEND_STABLE, 20000)); if(log.isDebugEnabled()){ log.debug("Merge participant " + local_addr + " got merge request from " + msg.getSrc()); } impl.handleMergeRequest(msg.getSrc(), hdr.merge_id); break; case GmsHeader.MERGE_RSP: MergeData merge_data=new MergeData(msg.getSrc(), hdr.view, hdr.my_digest); merge_data.merge_rejected=hdr.merge_rejected; if(log.isDebugEnabled()) { log.debug("Got merge response at " + local_addr + " from " + msg.getSrc() + ", merge_id=" + hdr.view+ ", merge data is "+ merge_data); } impl.handleMergeResponse(merge_data, hdr.merge_id); break; case GmsHeader.INSTALL_MERGE_VIEW: impl.handleMergeView(new MergeData(msg.getSrc(), hdr.view, hdr.my_digest), hdr.merge_id); down_prot.down(new Event(Event.RESUME_STABLE)); break; case GmsHeader.INSTALL_MERGE_VIEW_OK: //[JGRP-700] - FLUSH: flushing should span merge merge_ack_collector.ack(msg.getSrc()); break; case GmsHeader.CANCEL_MERGE: //[JGRP-524] - FLUSH and merge: flush doesn't wrap entire merge process impl.handleMergeCancelled(hdr.merge_id); down_prot.down(new Event(Event.RESUME_STABLE)); break; default: if(log.isErrorEnabled()) log.error("GmsHeader with type=" + hdr.type + " not known"); } return null; // don't pass up case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; // pass up case Event.SUSPECT: Object retval=up_prot.up(evt); Address suspected=(Address)evt.getArg(); view_handler.add(new Request(Request.SUSPECT, suspected, true, null)); ack_collector.suspect(suspected); merge_ack_collector.suspect(suspected); return retval; case Event.UNSUSPECT: impl.unsuspect((Address)evt.getArg()); return null; // discard case Event.MERGE: view_handler.add(new Request(Request.MERGE, null, false, (Vector
)evt.getArg())); return null; // don't pass up } if(impl.handleUpEvent(evt)) return up_prot.up(evt); return null; } /** This method is overridden to avoid hanging on getDigest(): when a JOIN is received, the coordinator needs to retrieve the digest from the NAKACK layer. It therefore sends down a GET_DIGEST event, to which the NAKACK layer responds with a GET_DIGEST_OK event.

However, the GET_DIGEST_OK event will not be processed because the thread handling the JOIN request won't process the GET_DIGEST_OK event until the JOIN event returns. The receiveUpEvent() method is executed by the up-handler thread of the lower protocol and therefore can handle the event. All we do here is unblock the mutex on which JOIN is waiting, allowing JOIN to return with a valid digest. The GET_DIGEST_OK event is then discarded, because it won't be processed twice. */ // public void receiveUpEvent(Event evt) { // switch(evt.getType()) { // case Event.GET_DIGEST_OK: // digest_promise.setResult(evt.getArg()); // return; // don't pass further up // } // super.receiveUpEvent(evt); // } public Object down(Event evt) { Object arg=null; switch(evt.getType()) { case Event.CONNECT: if(print_local_addr) { System.out.println("\n---------------------------------------------------------\n" + "GMS: address is " + local_addr + " (cluster=" + evt.getArg() + ")" + "\n---------------------------------------------------------"); } down_prot.down(evt); if(local_addr == null) if(log.isFatalEnabled()) log.fatal("[CONNECT] local_addr is null"); try { impl.join(local_addr); } catch(Throwable e) { arg=e; } return arg; // don't pass down: was already passed down case Event.CONNECT_WITH_STATE_TRANSFER: if(print_local_addr) { System.out.println("\n---------------------------------------------------------\n" + "GMS: address is " + local_addr + " (cluster=" + evt.getArg() + ")" + "\n---------------------------------------------------------"); } down_prot.down(evt); if(local_addr == null) if(log.isFatalEnabled()) log.fatal("[CONNECT] local_addr is null"); try { impl.joinWithStateTransfer(local_addr); } catch(Throwable e) { arg=e; } return arg; // don't pass down: was already passed down case Event.DISCONNECT: impl.leave((Address)evt.getArg()); if(!(impl instanceof CoordGmsImpl)) { initState(); // in case connect() is called again } down_prot.down(evt); // notify the other protocols, but ignore the result return null; case Event.CONFIG : Map config=(Map)evt.getArg(); if((config != null && config.containsKey("flush_supported"))){ flushProtocolInStack=true; } break; } return down_prot.down(evt); } /** Setup the Protocol instance according to the configuration string */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("shun"); if(str != null) { shun=Boolean.valueOf(str).booleanValue(); props.remove("shun"); } str=props.getProperty("merge_leader"); if(str != null) { merge_leader=Boolean.valueOf(str).booleanValue(); props.remove("merge_leader"); } str=props.getProperty("print_local_addr"); if(str != null) { print_local_addr=Boolean.valueOf(str).booleanValue(); props.remove("print_local_addr"); } str=props.getProperty("join_timeout"); // time to wait for JOIN if(str != null) { join_timeout=Long.parseLong(str); props.remove("join_timeout"); } str=props.getProperty("join_retry_timeout"); // time to wait between JOINs if(str != null) { props.remove("join_retry_timeout"); if(log.isWarnEnabled()) log.warn("join_retry_timeout has been deprecated and its value will be ignored"); } str=props.getProperty("leave_timeout"); // time to wait until coord responds to LEAVE req. if(str != null) { leave_timeout=Long.parseLong(str); props.remove("leave_timeout"); } str=props.getProperty("merge_timeout"); // time to wait for MERGE_RSPS from subgroup coordinators if(str != null) { merge_timeout=Long.parseLong(str); props.remove("merge_timeout"); } str=props.getProperty("digest_timeout"); // time to wait for GET_DIGEST_OK from PBCAST if(str != null) { log.warn("digest_timeout has been deprecated and its value will be ignored"); props.remove("digest_timeout"); } str=props.getProperty("view_ack_collection_timeout"); if(str != null) { view_ack_collection_timeout=Long.parseLong(str); props.remove("view_ack_collection_timeout"); } str=props.getProperty("resume_task_timeout"); if(str != null) { resume_task_timeout=Long.parseLong(str); props.remove("resume_task_timeout"); } str=props.getProperty("disable_initial_coord"); if(str != null) { disable_initial_coord=Boolean.valueOf(str).booleanValue(); props.remove("disable_initial_coord"); if(log.isWarnEnabled()) log.warn("disable_initial_coord has been deprecated and will be phased out by 3.0, please don't use it anymore"); } str=props.getProperty("handle_concurrent_startup"); if(str != null) { handle_concurrent_startup=Boolean.valueOf(str).booleanValue(); props.remove("handle_concurrent_startup"); } str=props.getProperty("num_prev_mbrs"); if(str != null) { num_prev_mbrs=Integer.parseInt(str); props.remove("num_prev_mbrs"); } str=props.getProperty("reject_join_from_existing_member"); if(str != null) { reject_join_from_existing_member=Boolean.parseBoolean(str); props.remove("reject_join_from_existing_member"); } str=props.getProperty("use_flush"); if(str != null) { log.warn("use_flush has been deprecated and its value will be ignored"); props.remove("use_flush"); } str=props.getProperty("flush_timeout"); if(str != null) { log.warn("flush_timeout has been deprecated and its value will be ignored"); props.remove("flush_timeout"); } str=props.getProperty("view_bundling"); if(str != null) { view_bundling=Boolean.valueOf(str).booleanValue(); props.remove("view_bundling"); } str=props.getProperty("max_bundling_time"); if(str != null) { max_bundling_time=Long.parseLong(str); props.remove("max_bundling_time"); } str=props.getProperty("flush_invoker_class"); if (str != null) { try { flushInvokerClass = (Class>) Class.forName(str); flushInvokerClass.getDeclaredConstructor(View.class); } catch (Exception e) { log.error("Invalid flush invoker class " + str + ". Maker sure it implements > with public constructor using View as a parameter"); } props.remove("flush_invoker_class"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } /* ------------------------------- Private Methods --------------------------------- */ final void initState() { becomeClient(); view_id=null; view=null; } private void sendViewAck(Address dest) { Message view_ack=new Message(dest, null, null); view_ack.setFlag(Message.OOB); GmsHeader tmphdr=new GmsHeader(GmsHeader.VIEW_ACK); view_ack.putHeader(name, tmphdr); if(log.isTraceEnabled()) log.trace("sending VIEW_ACK to " + dest); down_prot.down(new Event(Event.MSG, view_ack)); } /* --------------------------- End of Private Methods ------------------------------- */ public static class GmsHeader extends Header implements Streamable { public static final byte JOIN_REQ=1; public static final byte JOIN_RSP=2; public static final byte LEAVE_REQ=3; public static final byte LEAVE_RSP=4; public static final byte VIEW=5; public static final byte MERGE_REQ=6; public static final byte MERGE_RSP=7; public static final byte INSTALL_MERGE_VIEW=8; public static final byte CANCEL_MERGE=9; public static final byte VIEW_ACK=10; public static final byte JOIN_REQ_WITH_STATE_TRANSFER = 11; public static final byte INSTALL_MERGE_VIEW_OK=12; byte type=0; View view=null; // used when type=VIEW or MERGE_RSP or INSTALL_MERGE_VIEW Address mbr=null; // used when type=JOIN_REQ or LEAVE_REQ JoinRsp join_rsp=null; // used when type=JOIN_RSP Digest my_digest=null; // used when type=MERGE_RSP or INSTALL_MERGE_VIEW ViewId merge_id=null; // used when type=MERGE_REQ or MERGE_RSP or INSTALL_MERGE_VIEW or CANCEL_MERGE boolean merge_rejected=false; // used when type=MERGE_RSP private static final long serialVersionUID=2369798797842183276L; public GmsHeader() { } // used for Externalization public GmsHeader(byte type) { this.type=type; } /** Used for VIEW header */ public GmsHeader(byte type, View view) { this.type=type; this.view=view; } /** Used for JOIN_REQ or LEAVE_REQ header */ public GmsHeader(byte type, Address mbr) { this.type=type; this.mbr=mbr; } /** Used for JOIN_RSP header */ public GmsHeader(byte type, JoinRsp join_rsp) { this.type=type; this.join_rsp=join_rsp; } public byte getType() { return type; } public Address getMember() { return mbr; } public String toString() { StringBuilder sb=new StringBuilder("GmsHeader"); sb.append('[' + type2String(type) + ']'); switch(type) { case JOIN_REQ: sb.append(": mbr=" + mbr); break; case JOIN_RSP: sb.append(": join_rsp=" + join_rsp); break; case LEAVE_REQ: sb.append(": mbr=" + mbr); break; case LEAVE_RSP: break; case VIEW: case VIEW_ACK: sb.append(": view=" + view); break; case MERGE_REQ: sb.append(": merge_id=" + merge_id); break; case MERGE_RSP: sb.append(": view=" + view + ", digest=" + my_digest + ", merge_rejected=" + merge_rejected + ", merge_id=" + merge_id); break; case INSTALL_MERGE_VIEW: sb.append(": view=" + view + ", digest=" + my_digest); break; case CANCEL_MERGE: sb.append(", , merge_id=" + merge_id); break; } return sb.toString(); } public static String type2String(int type) { switch(type) { case JOIN_REQ: return "JOIN_REQ"; case JOIN_RSP: return "JOIN_RSP"; case LEAVE_REQ: return "LEAVE_REQ"; case LEAVE_RSP: return "LEAVE_RSP"; case VIEW: return "VIEW"; case MERGE_REQ: return "MERGE_REQ"; case MERGE_RSP: return "MERGE_RSP"; case INSTALL_MERGE_VIEW: return "INSTALL_MERGE_VIEW"; case CANCEL_MERGE: return "CANCEL_MERGE"; case VIEW_ACK: return "VIEW_ACK"; case JOIN_REQ_WITH_STATE_TRANSFER: return "JOIN_REQ_WITH_STATE_TRANSFER"; default: return ""; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeObject(view); out.writeObject(mbr); out.writeObject(join_rsp); out.writeObject(my_digest); out.writeObject(merge_id); out.writeBoolean(merge_rejected); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); view=(View)in.readObject(); mbr=(Address)in.readObject(); join_rsp=(JoinRsp)in.readObject(); my_digest=(Digest)in.readObject(); merge_id=(ViewId)in.readObject(); merge_rejected=in.readBoolean(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); boolean isMergeView=view != null && view instanceof MergeView; out.writeBoolean(isMergeView); Util.writeStreamable(view, out); Util.writeAddress(mbr, out); Util.writeStreamable(join_rsp, out); Util.writeStreamable(my_digest, out); Util.writeStreamable(merge_id, out); // kludge: we know merge_id is a ViewId out.writeBoolean(merge_rejected); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); boolean isMergeView=in.readBoolean(); if(isMergeView) view=(View)Util.readStreamable(MergeView.class, in); else view=(View)Util.readStreamable(View.class, in); mbr=Util.readAddress(in); join_rsp=(JoinRsp)Util.readStreamable(JoinRsp.class, in); my_digest=(Digest)Util.readStreamable(Digest.class, in); merge_id=(ViewId)Util.readStreamable(ViewId.class, in); merge_rejected=in.readBoolean(); } public int size() { int retval=Global.BYTE_SIZE *2; // type + merge_rejected retval+=Global.BYTE_SIZE; // presence view retval+=Global.BYTE_SIZE; // MergeView or View if(view != null) retval+=view.serializedSize(); retval+=Util.size(mbr); retval+=Global.BYTE_SIZE; // presence of join_rsp if(join_rsp != null) retval+=join_rsp.serializedSize(); retval+=Global.BYTE_SIZE; // presence for my_digest if(my_digest != null) retval+=my_digest.serializedSize(); retval+=Global.BYTE_SIZE; // presence for merge_id if(merge_id != null) retval+=merge_id.serializedSize(); return retval; } } /** * Class which processes JOIN, LEAVE and MERGE requests. Requests are queued and processed in FIFO order * @author Bela Ban * @version $Id: GMS.java,v 1.126.2.25 2009/12/15 17:08:30 belaban Exp $ */ class ViewHandler implements Runnable { volatile Thread thread; Queue q=new Queue(); // Queue boolean suspended=false; final static long INTERVAL=5000; private static final long MAX_COMPLETION_TIME=10000; /** Maintains a list of the last 20 requests */ private final BoundedList history=new BoundedList(20); /** Map. Keeps track of Resumer tasks which have not fired yet */ private final Map resume_tasks=new HashMap(); private Object merge_id=null; void add(Request req) { add(req, false, false); } synchronized void add(Request req, boolean at_head, boolean unsuspend) { if(suspended && !unsuspend) { log.warn("queue is suspended; request " + req + " is discarded"); return; } start(unsuspend); try { if(at_head) q.addAtHead(req); else q.add(req); history.add(new Date() + ": " + req.toString()); } catch(QueueClosedException e) { if(log.isTraceEnabled()) log.trace("queue is closed; request " + req + " is discarded"); } } void waitUntilCompleted(long timeout) { waitUntilCompleted(timeout, false); } synchronized void waitUntilCompleted(long timeout, boolean resume) { if(thread != null) { try { thread.join(timeout); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } //Added after Brian noticed that ViewHandler leaks class loaders thread = null; } if(resume) resumeForce(); } /** * Waits until the current request has been processes, then clears the queue and discards new * requests from now on */ public synchronized void suspend(Object merge_id) { if(!suspended) { suspended=true; this.merge_id=merge_id; q.clear(); waitUntilCompleted(MAX_COMPLETION_TIME); q.close(true); if(log.isDebugEnabled()) log.debug("suspended ViewHandler at " + local_addr); Resumer resumer=new Resumer(merge_id, resume_tasks, this); Future future=timer.schedule(resumer, resume_task_timeout, TimeUnit.MILLISECONDS); Future old_future=resume_tasks.put(merge_id, future); if(old_future != null) old_future.cancel(true); } else { if(log.isWarnEnabled()) { log.warn("attempted suspend on ViewHandler at " + local_addr+ ", however, it is already suspended"); } } } public synchronized void resume(Object merge_id) { if(suspended) { boolean same_merge_id=this.merge_id != null && merge_id != null && this.merge_id.equals(merge_id); same_merge_id=same_merge_id || (this.merge_id == null && merge_id == null); if(same_merge_id) { synchronized(resume_tasks) { Future future=resume_tasks.get(merge_id); if(future != null) { future.cancel(false); resume_tasks.remove(merge_id); } } } else{ if(log.isWarnEnabled()) log.warn("resume(" + merge_id+ ") does not match "+ this.merge_id); } resumeForce(); } } public synchronized void resumeForce() { if(q.closed()) q.reset(); suspended=false; if(log.isTraceEnabled()) log.trace("resumed ViewHandler"); } public void run() { long end_time, wait_time; List requests=new LinkedList(); while(Thread.currentThread().equals(thread) && !suspended) { try { boolean keepGoing=false; end_time=System.currentTimeMillis() + max_bundling_time; do { Request firstRequest=(Request)q.remove(INTERVAL); // throws a TimeoutException if it runs into timeout requests.add(firstRequest); if(!view_bundling) break; if(q.size() > 0) { Request nextReq=(Request)q.peek(); keepGoing=view_bundling && firstRequest.canBeProcessedTogether(nextReq); } else { wait_time=end_time - System.currentTimeMillis(); if(wait_time > 0) q.waitUntilClosed(wait_time); // misnomer: waits until element has been added or q closed keepGoing=q.size() > 0 && firstRequest.canBeProcessedTogether((Request)q.peek()); } } while(keepGoing && System.currentTimeMillis() < end_time); try { process(requests); } finally { requests.clear(); } } catch(QueueClosedException e) { break; } catch(TimeoutException e) { break; } catch(Throwable catchall) { Util.sleep(50); } } } public int size() {return q.size();} public boolean suspended() {return suspended;} public String dumpQueue() { StringBuilder sb=new StringBuilder(); List v=q.values(); for(Iterator it=v.iterator(); it.hasNext();) { sb.append(it.next() + "\n"); } return sb.toString(); } public String dumpHistory() { StringBuilder sb=new StringBuilder(); for(String line: history) { sb.append(line + "\n"); } return sb.toString(); } private void process(List requests) { if(requests.isEmpty()) return; if(log.isTraceEnabled()) log.trace("processing " + requests); Request firstReq=requests.get(0); switch(firstReq.type) { case Request.JOIN: case Request.JOIN_WITH_STATE_TRANSFER: case Request.LEAVE: case Request.SUSPECT: impl.handleMembershipChange(requests); break; case Request.MERGE: if(requests.size() > 1) log.error("more than one MERGE request to process, ignoring the others"); impl.merge(firstReq.coordinators); break; default: log.error("request " + firstReq.type + " is unknown; discarded"); } } synchronized void start(boolean unsuspend) { if(q.closed()) q.reset(); if(unsuspend) { suspended=false; Future future; synchronized(resume_tasks) { future=resume_tasks.remove(merge_id); } if(future != null) future.cancel(true); } merge_id=null; if(thread == null || !thread.isAlive()) { thread=getThreadFactory().newThread(this, "ViewHandler"); thread.setDaemon(false); // thread cannot terminate if we have tasks left, e.g. when we as coord leave thread.start(); } } synchronized void stop(boolean flush) { q.close(flush); synchronized(resume_tasks) { for(Future future: resume_tasks.values()) { future.cancel(true); } resume_tasks.clear(); } merge_id=null; // resumeForce(); // commented as this is already done in waitUnitlCompleted() // thread = null; // Workaround for potential classloader leak to our thread } } /** * Resumer is a second line of defense: when the ViewHandler is suspended, it will be resumed when the current * merge is cancelled, or when the merge completes. However, in a case where this never happens (this * shouldn't be the case !), the Resumer will nevertheless resume the ViewHandler. * We chose this strategy because ViewHandler is critical: if it is suspended indefinitely, we would * not be able to process new JOIN requests ! So, this is for peace of mind, although it most likely * will never be used... */ static class Resumer implements Runnable { final Object token; final Map tasks; final ViewHandler handler; public Resumer(final Object token, final Map t, final ViewHandler handler) { this.token=token; this.tasks=t; this.handler=handler; } public void run() { boolean execute=true; synchronized(tasks) { Future future=tasks.get(token); if(future != null) { future.cancel(false); execute=true; } else { execute=false; } tasks.remove(token); } if(execute) { handler.resume(token); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/GmsImpl.java0000644000175000017500000001200411366547366027115 0ustar twernertwerner// $Id: GmsImpl.java,v 1.28.2.3 2009/09/18 10:53:28 belaban Exp $ package org.jgroups.protocols.pbcast; import org.apache.commons.logging.Log; import org.jgroups.*; import org.jgroups.util.Digest; import java.util.Collection; import java.util.Vector; public abstract class GmsImpl { protected GMS gms=null; protected final Log log; final boolean trace; final boolean warn; volatile boolean leaving=false; protected GmsImpl() { log=null; trace=warn=false; } protected GmsImpl(GMS gms) { this.gms=gms; log=gms.getLog(); trace=log.isTraceEnabled(); warn=log.isWarnEnabled(); } public abstract void join(Address mbr); public abstract void joinWithStateTransfer(Address local_addr); public abstract void leave(Address mbr); public abstract void handleJoinResponse(JoinRsp join_rsp); public abstract void handleLeaveResponse(); public abstract void suspect(Address mbr); public abstract void unsuspect(Address mbr); public void merge(Vector

other_coords) {} // only processed by coord public void handleMergeRequest(Address sender, ViewId merge_id) {} // only processed by coords public void handleMergeResponse(MergeData data, ViewId merge_id) {} // only processed by coords public void handleMergeView(MergeData data, ViewId merge_id) {} // only processed by coords public void handleMergeCancelled(ViewId merge_id) {} // only processed by coords public abstract void handleMembershipChange(Collection requests); public abstract void handleViewChange(View new_view, Digest digest); public void handleExit() {} public boolean handleUpEvent(Event evt) {return true;} public void init() throws Exception {leaving=false;} public void start() throws Exception {leaving=false;} public void stop() {leaving=true;} protected void sendMergeRejectedResponse(Address sender, ViewId merge_id) { Message msg=new Message(sender, null, null); msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.MERGE_RSP); hdr.merge_rejected=true; hdr.merge_id=merge_id; msg.putHeader(gms.getName(), hdr); if(log.isDebugEnabled()) log.debug("response=" + hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } protected void wrongMethod(String method_name) { if(log.isWarnEnabled()) log.warn(method_name + "() should not be invoked on an instance of " + getClass().getName()); } /** Returns potential coordinator based on lexicographic ordering of member addresses. Another approach would be to keep track of the primary partition and return the first member if we are the primary partition. */ protected boolean iWouldBeCoordinator(Vector new_mbrs) { Membership tmp_mbrs=gms.members.copy(); tmp_mbrs.merge(new_mbrs, null); tmp_mbrs.sort(); return !(tmp_mbrs.size() <= 0 || gms.local_addr == null) && gms.local_addr.equals(tmp_mbrs.elementAt(0)); } public static class Request { static final int JOIN = 1; static final int LEAVE = 2; static final int SUSPECT = 3; static final int MERGE = 4; static final int JOIN_WITH_STATE_TRANSFER = 6; int type=-1; Address mbr; boolean suspected; Vector
coordinators; Request(int type) { this.type=type; } Request(int type, Address mbr, boolean suspected, Vector
coordinators) { this.type=type; this.mbr=mbr; this.suspected=suspected; this.coordinators=coordinators; } public int getType() { return type; } public String toString() { switch(type) { case JOIN: return "JOIN(" + mbr + ")"; case JOIN_WITH_STATE_TRANSFER: return "JOIN_WITH_STATE_TRANSFER(" + mbr + ")"; case LEAVE: return "LEAVE(" + mbr + ", " + suspected + ")"; case SUSPECT: return "SUSPECT(" + mbr + ")"; case MERGE: return "MERGE(" + coordinators + ")"; } return " * Works as follows: periodically we mcast our highest seqnos (seen for each member) to the group. * A stability vector, which maintains the highest seqno for each member and initially contains no data, * is updated when such a message is received. The entry for a member P is computed set to * min(entry[P], digest[P]). When messages from all members have been received, a stability * message is mcast, which causes all members to send a STABLE event up the stack (triggering garbage collection * in the NAKACK layer).

* New: when max_bytes is exceeded (unless disabled by setting it to 0), * a STABLE task will be started (unless it is already running). Design in docs/design/STABLE.txt * @author Bela Ban * @version $Id: STABLE.java,v 1.85.2.4 2009/09/29 04:29:40 belaban Exp $ */ public class STABLE extends Protocol { private Address local_addr=null; private final Set

mbrs=new LinkedHashSet
(); // we don't need ordering here @GuardedBy("lock") private final MutableDigest digest=new MutableDigest(10); // keeps track of the highest seqnos from all members /** Keeps track of who we already heard from (STABLE_GOSSIP msgs). This is cleared initially, and we * add the sender when a STABLE message is received. When the list is full (responses from all members), * we send a STABILITY message */ @GuardedBy("lock") private final Set
votes=new HashSet
(); private final Lock lock=new ReentrantLock(); /** Sends a STABLE gossip every 20 seconds on average. 0 disables gossipping of STABLE messages */ private long desired_avg_gossip=20000; /** delay before we send STABILITY msg (give others a change to send first). This should be set to a very * small number (> 0 !) if max_bytes is used */ private long stability_delay=6000; @GuardedBy("stability_lock") private Future stability_task_future=null; private final Lock stability_lock=new ReentrantLock(); // to synchronize on stability_task @GuardedBy("stable_task_lock") private Future stable_task_future=null; // bcasts periodic STABLE message (added to timer below) private final Lock stable_task_lock=new ReentrantLock(); // to sync on stable_task private TimeScheduler timer=null; // to send periodic STABLE msgs (and STABILITY messages) private static final String name="STABLE"; /** Total amount of bytes from incoming messages (default = 0 = disabled). When exceeded, a STABLE * message will be broadcast and num_bytes_received reset to 0 . If this is > 0, then ideally * stability_delay should be set to a low number as well */ private long max_bytes=0; /** The total number of bytes received from unicast and multicast messages */ @GuardedBy("received") private long num_bytes_received=0; private final Lock received=new ReentrantLock(); /** When true, don't take part in garbage collection protocol: neither send STABLE messages nor * handle STABILITY messages */ private boolean suspended=false; private boolean initialized=false; private Future resume_task_future=null; private final Object resume_task_mutex=new Object(); private int num_stable_msgs_sent=0; private int num_stable_msgs_received=0; private int num_stability_msgs_sent=0; private int num_stability_msgs_received=0; private static final long MAX_SUSPEND_TIME=200000; public String getName() { return name; } public long getDesiredAverageGossip() { return desired_avg_gossip; } public void setDesiredAverageGossip(long gossip_interval) { desired_avg_gossip=gossip_interval; } public long getMaxBytes() { return max_bytes; } public void setMaxBytes(long max_bytes) { this.max_bytes=max_bytes; } public long getBytes() {return num_bytes_received;} public int getStableSent() {return num_stable_msgs_sent;} public int getStableReceived() {return num_stable_msgs_received;} public int getStabilitySent() {return num_stability_msgs_sent;} public int getStabilityReceived() {return num_stability_msgs_received;} public void resetStats() { super.resetStats(); num_stability_msgs_received=num_stability_msgs_sent=num_stable_msgs_sent=num_stable_msgs_received=0; } public Vector requiredDownServices() { Vector retval=new Vector(); retval.addElement(Event.GET_DIGEST); // NAKACK layer return retval; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("digest_timeout"); if(str != null) { props.remove("digest_timeout"); log.error("digest_timeout has been deprecated; it will be ignored"); } str=props.getProperty("desired_avg_gossip"); if(str != null) { desired_avg_gossip=Long.parseLong(str); props.remove("desired_avg_gossip"); } str=props.getProperty("stability_delay"); if(str != null) { stability_delay=Long.parseLong(str); props.remove("stability_delay"); } str=props.getProperty("max_gossip_runs"); if(str != null) { props.remove("max_gossip_runs"); log.error("max_gossip_runs has been deprecated and will be ignored"); } str=props.getProperty("max_bytes"); if(str != null) { max_bytes=Long.parseLong(str); props.remove("max_bytes"); } str=props.getProperty("max_suspend_time"); if(str != null) { log.error("max_suspend_time is not supported any longer; please remove it (ignoring it)"); props.remove("max_suspend_time"); } Util.checkBufferSize("STABLE.max_bytes", max_bytes); if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } private void suspend(long timeout) { if(!suspended) { suspended=true; if(log.isDebugEnabled()) log.debug("suspending message garbage collection"); } startResumeTask(timeout); // will not start task if already running } private void resume() { lock.lock(); try { resetDigest(); // start from scratch suspended=false; } finally { lock.unlock(); } if(log.isDebugEnabled()) log.debug("resuming message garbage collection"); stopResumeTask(); } public void start() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); if(desired_avg_gossip > 0) startStableTask(); } public void stop() { stopStableTask(); } public Object up(Event evt) { Message msg; StableHeader hdr; int type=evt.getType(); switch(type) { case Event.MSG: msg=(Message)evt.getArg(); hdr=(StableHeader)msg.getHeader(name); if(hdr == null) { handleRegularMessage(msg); return up_prot.up(evt); } switch(hdr.type) { case StableHeader.STABLE_GOSSIP: handleStableMessage(msg.getSrc(), hdr.stableDigest); break; case StableHeader.STABILITY: handleStabilityMessage(hdr.stableDigest, msg.getSrc()); break; default: if(log.isErrorEnabled()) log.error("StableHeader type " + hdr.type + " not known"); } return null; // don't pass STABLE or STABILITY messages up the stack case Event.VIEW_CHANGE: Object retval=up_prot.up(evt); View view=(View)evt.getArg(); handleViewChange(view); return retval; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return up_prot.up(evt); } private void handleRegularMessage(Message msg) { // only if message counting is enabled, and only for multicast messages // fixes http://jira.jboss.com/jira/browse/JGRP-233 if(max_bytes <= 0) return; Address dest=msg.getDest(); if(dest == null || dest.isMulticastAddress()) { received.lock(); boolean locked=true; try { num_bytes_received+=(long)msg.getLength(); if(num_bytes_received >= max_bytes) { if(log.isTraceEnabled()) { log.trace(new StringBuilder("max_bytes has been reached (").append(max_bytes). append(", bytes received=").append(num_bytes_received).append("): triggers stable msg")); } num_bytes_received=0; received.unlock(); locked=false; Digest my_digest=getDigest(); // asks the NAKACK protocol for the current digest, if(log.isTraceEnabled()) log.trace("setting latest_local_digest from NAKACK: " + my_digest.printHighestDeliveredSeqnos()); sendStableMessage(my_digest); } } finally { if(locked) received.unlock(); } } } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: Object retval=down_prot.down(evt); View v=(View)evt.getArg(); handleViewChange(v); return retval; case Event.SUSPEND_STABLE: long timeout=0; Object t=evt.getArg(); if(t != null && t instanceof Long) timeout=(Long)t; suspend(timeout); break; case Event.RESUME_STABLE: resume(); break; } return down_prot.down(evt); } public void runMessageGarbageCollection() { Digest copy=getDigest(); sendStableMessage(copy); } /* --------------------------------------- Private Methods ---------------------------------------- */ private void handleViewChange(View v) { Vector
tmp=v.getMembers(); synchronized(mbrs) { mbrs.clear(); mbrs.addAll(tmp); } lock.lock(); try { resetDigest(); if(!initialized) initialized=true; } finally { lock.unlock(); } } /** Update my own digest from a digest received by somebody else. Returns whether the update was successful. * Needs to be called with a lock on digest */ @GuardedBy("lock") private boolean updateLocalDigest(Digest d, Address sender) { if(d == null || d.size() == 0) return false; if(!initialized) { if(log.isTraceEnabled()) log.trace("STABLE message will not be handled as I'm not yet initialized"); return false; } if(!digest.sameSenders(d)) { // to avoid sending incorrect stability/stable msgs, we simply reset our votes list, see DESIGN resetDigest(); return false; } StringBuilder sb=null; if(log.isTraceEnabled()) { sb=new StringBuilder("[").append(local_addr).append("] handling digest from ").append(sender).append(" ("). append(votes.size()).append(" votes):\nmine: ").append(digest.printHighestDeliveredSeqnos()) .append("\nother: ").append(d.printHighestDeliveredSeqnos()); } Address mbr; long highest_seqno, my_highest_seqno, new_highest_seqno, my_low, low, new_low; long highest_seen_seqno, my_highest_seen_seqno, new_highest_seen_seqno; Digest.Entry val; for(Map.Entry entry: d.getSenders().entrySet()) { mbr=entry.getKey(); val=entry.getValue(); low=val.getLow(); highest_seqno=val.getHighestDeliveredSeqno(); // highest *delivered* seqno highest_seen_seqno=val.getHighestReceivedSeqno(); // highest *received* seqno my_low=digest.lowSeqnoAt(mbr); new_low=Math.min(my_low, low); // compute the minimum of the highest seqnos deliverable (for garbage collection) my_highest_seqno=digest.highestDeliveredSeqnoAt(mbr); // compute the maximum of the highest seqnos seen (for retransmission of last missing message) my_highest_seen_seqno=digest.highestReceivedSeqnoAt(mbr); new_highest_seqno=Math.min(my_highest_seqno, highest_seqno); new_highest_seen_seqno=Math.max(my_highest_seen_seqno, highest_seen_seqno); digest.setHighestDeliveredAndSeenSeqnos(mbr, new_low, new_highest_seqno, new_highest_seen_seqno); } if(log.isTraceEnabled()) { assert sb != null; sb.append("\nresult: ").append(digest.printHighestDeliveredSeqnos()).append("\n"); log.trace(sb); } return true; } @GuardedBy("lock") private void resetDigest() { Digest tmp=getDigest(); digest.replace(tmp); if(log.isTraceEnabled()) log.trace("resetting digest from NAKACK: " + digest.printHighestDeliveredSeqnos()); votes.clear(); } /** * Adds mbr to votes and returns true if we have all the votes, otherwise false. * @param mbr */ @GuardedBy("lock") private boolean addVote(Address mbr) { boolean added=votes.add(mbr); return added && allVotesReceived(votes); } /** Votes is already locked and guaranteed to be non-null */ private boolean allVotesReceived(Set
votes) { synchronized(mbrs) { return votes.equals(mbrs); // compares identity, size and element-wise (if needed) } } private void startStableTask() { stable_task_lock.lock(); try { if(stable_task_future == null || stable_task_future.isDone()) { StableTask stable_task=new StableTask(); stable_task_future=timer.scheduleWithDynamicInterval(stable_task); if(log.isTraceEnabled()) log.trace("stable task started"); } } finally { stable_task_lock.unlock(); } } private void stopStableTask() { stable_task_lock.lock(); try { if(stable_task_future != null) { stable_task_future.cancel(false); stable_task_future=null; } } finally { stable_task_lock.unlock(); } } private void startResumeTask(long max_suspend_time) { max_suspend_time=(long)(max_suspend_time * 1.1); // little slack if(max_suspend_time <= 0) max_suspend_time=MAX_SUSPEND_TIME; synchronized(resume_task_mutex) { if(resume_task_future == null || resume_task_future.isDone()) { ResumeTask resume_task=new ResumeTask(); resume_task_future=timer.schedule(resume_task, max_suspend_time, TimeUnit.MILLISECONDS); // fixed-rate scheduling if(log.isDebugEnabled()) log.debug("resume task started, max_suspend_time=" + max_suspend_time); } } } private void stopResumeTask() { synchronized(resume_task_mutex) { if(resume_task_future != null) { resume_task_future.cancel(false); resume_task_future=null; } } } private void startStabilityTask(Digest d, long delay) { stability_lock.lock(); try { if(stability_task_future == null || stability_task_future.isDone()) { StabilitySendTask stability_task=new StabilitySendTask(d); // runs only once stability_task_future=timer.schedule(stability_task, delay, TimeUnit.MILLISECONDS); } } finally { stability_lock.unlock(); } } private void stopStabilityTask() { stability_lock.lock(); try { if(stability_task_future != null) { stability_task_future.cancel(false); stability_task_future=null; } } finally { stability_lock.unlock(); } } /** Digest d contains (a) the highest seqnos deliverable for each sender and (b) the highest seqnos seen for each member. (Difference: with 1,2,4,5, the highest seqno seen is 5, whereas the highest seqno deliverable is 2). The minimum of all highest seqnos deliverable will be taken to send a stability message, which results in garbage collection of messages lower than the ones in the stability vector. The maximum of all seqnos will be taken to trigger possible retransmission of last missing seqno (see DESIGN for details). */ private void handleStableMessage(Address sender, Digest d) { if(d == null || sender == null) { if(log.isErrorEnabled()) log.error("digest or sender is null"); return; } if(!initialized) { if(log.isTraceEnabled()) log.trace("STABLE message will not be handled as I'm not yet initialized"); return; } if(suspended) { if(log.isTraceEnabled()) log.trace("STABLE message will not be handled as I'm suspended"); return; } Digest copy=null; boolean all_votes_received=false; lock.lock(); try { if(votes.contains(sender)) // already received gossip from sender; discard it return; num_stable_msgs_received++; boolean success=updateLocalDigest(d, sender); if(!success) // we can only add the sender to votes if *all* elements of my digest were updated return; all_votes_received=addVote(sender); if(all_votes_received) copy=digest.copy(); } finally { lock.unlock(); } // we don't yet reset digest: new STABLE messages will be discarded anyway as we have already // received votes from their senders if(copy != null) { sendStabilityMessage(copy); } } private void handleStabilityMessage(Digest stable_digest, Address sender) { if(stable_digest == null) { if(log.isErrorEnabled()) log.error("stability digest is null"); return; } if(!initialized) { if(log.isTraceEnabled()) log.trace("STABLE message will not be handled as I'm not yet initialized"); return; } if(suspended) { if(log.isDebugEnabled()) { log.debug("stability message will not be handled as I'm suspended"); } return; } if(log.isTraceEnabled()) log.trace(new StringBuilder("received stability msg from ").append(sender).append(": ").append(stable_digest.printHighestDeliveredSeqnos())); stopStabilityTask(); lock.lock(); try { // we won't handle the gossip d, if d's members don't match the membership in my own digest, // this is part of the fix for the NAKACK problem (bugs #943480 and #938584) if(!this.digest.sameSenders(stable_digest)) { if(log.isDebugEnabled()) { log.debug("received digest (digest=" + stable_digest + ") which does not match my own digest ("+ this.digest + "): ignoring digest and re-initializing own digest"); } resetDigest(); return; } num_stability_msgs_received++; resetDigest(); } finally { lock.unlock(); } // pass STABLE event down the stack, so NAKACK can garbage collect old messages down_prot.down(new Event(Event.STABLE, stable_digest)); } /** * Bcasts a STABLE message of the current digest to all members. Message contains highest seqnos of all members * seen by this member. Highest seqnos are retrieved from the NAKACK layer below. * @param d A copy of this.digest */ private void sendStableMessage(Digest d) { if(suspended) { if(log.isTraceEnabled()) log.trace("will not send STABLE message as I'm suspended"); return; } if(d != null && d.size() > 0) { if(log.isTraceEnabled()) log.trace("sending stable msg " + d.printHighestDeliveredSeqnos()); num_stable_msgs_sent++; final Message msg=new Message(); // mcast message msg.setFlag(Message.OOB); StableHeader hdr=new StableHeader(StableHeader.STABLE_GOSSIP, d); msg.putHeader(name, hdr); Runnable r=new Runnable() { public void run() { down_prot.down(new Event(Event.MSG, msg)); } }; // Run in a separate thread so we don't potentially block (http://jira.jboss.com/jira/browse/JGRP-532) timer.execute(r); // down_prot.down(new Event(Event.MSG, msg)); } } /** Schedules a stability message to be mcast after a random number of milliseconds (range 1-5 secs). The reason for waiting a random amount of time is that, in the worst case, all members receive a STABLE_GOSSIP message from the last outstanding member at the same time and would therefore mcast the STABILITY message at the same time too. To avoid this, each member waits random N msecs. If, before N elapses, some other member sent the STABILITY message, we just cancel our own message. If, during waiting for N msecs to send STABILITY message S1, another STABILITY message S2 is to be sent, we just discard S2. @param tmp A copy of te stability digest, so we don't need to copy it again */ private void sendStabilityMessage(Digest tmp) { long delay; if(suspended) { if(log.isTraceEnabled()) log.trace("STABILITY message will not be sent as I'm suspended"); return; } // give other members a chance to mcast STABILITY message. if we receive STABILITY by the end of // our random sleep, we will not send the STABILITY msg. this prevents that all mbrs mcast a // STABILITY msg at the same time delay=Util.random(stability_delay); if(log.isTraceEnabled()) log.trace("sending stability msg (in " + delay + " ms) " + tmp.printHighestDeliveredSeqnos() + " (copy=" + tmp.hashCode() + ")"); startStabilityTask(tmp, delay); } private Digest getDigest() { return (Digest)down_prot.down(Event.GET_DIGEST_EVT); } /* ------------------------------------End of Private Methods ------------------------------------- */ public static class StableHeader extends Header implements Streamable { public static final int STABLE_GOSSIP=1; public static final int STABILITY=2; int type=0; // Digest digest=new Digest(); // used for both STABLE_GOSSIP and STABILITY message Digest stableDigest=null; // changed by Bela April 4 2004 public StableHeader() { } // used for externalizable public StableHeader(int type, Digest digest) { this.type=type; this.stableDigest=digest; } static String type2String(int t) { switch(t) { case STABLE_GOSSIP: return "STABLE_GOSSIP"; case STABILITY: return "STABILITY"; default: return ""; } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append('['); sb.append(type2String(type)); sb.append("]: digest is "); sb.append(stableDigest); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(type); if(stableDigest == null) { out.writeBoolean(false); return; } out.writeBoolean(true); stableDigest.writeExternal(out); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readInt(); boolean digest_not_null=in.readBoolean(); if(digest_not_null) { stableDigest=new Digest(); stableDigest.readExternal(in); } } public int size() { int retval=Global.INT_SIZE + Global.BYTE_SIZE; // type + presence for digest if(stableDigest != null) retval+=stableDigest.serializedSize(); return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(type); Util.writeStreamable(stableDigest, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readInt(); stableDigest=(Digest)Util.readStreamable(Digest.class, in); } } /** Mcast periodic STABLE message. Interval between sends varies. */ private class StableTask implements TimeScheduler.Task { public long nextInterval() { long interval=computeSleepTime(); if(interval <= 0) return 10000; else return interval; } public void run() { if(suspended) { if(log.isTraceEnabled()) log.trace("stable task will not run as suspended=" + suspended); return; } // asks the NAKACK protocol for the current digest Digest my_digest=getDigest(); if(my_digest == null) { if(log.isWarnEnabled()) log.warn("received null digest, skipped sending of stable message"); return; } if(log.isTraceEnabled()) log.trace("setting latest_local_digest from NAKACK: " + my_digest.printHighestDeliveredSeqnos()); sendStableMessage(my_digest); } long computeSleepTime() { return getRandom((mbrs.size() * desired_avg_gossip * 2)); } long getRandom(long range) { return (long)((Math.random() * range) % range); } } /** * Multicasts a STABILITY message. */ private class StabilitySendTask implements Runnable { Digest stability_digest=null; StabilitySendTask(Digest d) { this.stability_digest=d; } public void run() { Message msg; StableHeader hdr; if(suspended) { if(log.isDebugEnabled()) { log.debug("STABILITY message will not be sent as suspended=" + suspended); } return; } if(stability_digest != null) { msg=new Message(); msg.setFlag(Message.OOB); hdr=new StableHeader(StableHeader.STABILITY, stability_digest); msg.putHeader(STABLE.name, hdr); if(log.isTraceEnabled()) log.trace("sending stability msg " + stability_digest.printHighestDeliveredSeqnos() + " (copy=" + stability_digest.hashCode() + ")"); num_stability_msgs_sent++; down_prot.down(new Event(Event.MSG, msg)); } } } private class ResumeTask implements Runnable { ResumeTask() { } public void run() { if(suspended) log.warn("ResumeTask resumed message garbage collection - this should be done by a RESUME_STABLE event; " + "check why this event was not received (or increase max_suspend_time for large state transfers)"); resume(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/JoinRsp.java0000644000175000017500000000504411366547366027137 0ustar twernertwerner// $Id: JoinRsp.java,v 1.12 2007/04/04 05:23:33 belaban Exp $ package org.jgroups.protocols.pbcast; import org.jgroups.View; import org.jgroups.Global; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import org.jgroups.util.Digest; import java.io.Serializable; import java.io.DataOutputStream; import java.io.IOException; import java.io.DataInputStream; /** * Result of a JOIN request (sent by the GMS client). Instances of this class are immutable. */ public class JoinRsp implements Serializable, Streamable { private View view=null; private Digest digest=null; /** only set if JOIN failed, e.g. in AUTH */ private String fail_reason=null; private static final long serialVersionUID = -212620440767943314L; public JoinRsp() { } public JoinRsp(View v, Digest d) { view=v; digest=d; } public JoinRsp(String fail_reason) { this.fail_reason=fail_reason; } public View getView() { return view; } public Digest getDigest() { return digest; } public String getFailReason() { return fail_reason; } public void setFailReason(String r) { fail_reason=r; } public void writeTo(DataOutputStream out) throws IOException { Util.writeStreamable(view, out); Util.writeStreamable(digest, out); Util.writeString(fail_reason, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { view=(View)Util.readStreamable(View.class, in); digest=(Digest)Util.readStreamable(Digest.class, in); fail_reason=Util.readString(in); } public int serializedSize() { int retval=Global.BYTE_SIZE * 2; // presence for view and digest if(view != null) retval+=view.serializedSize(); if(digest != null) retval+=digest.serializedSize(); retval+=Global.BYTE_SIZE; // presence byte for fail_reason if(fail_reason != null) retval+=fail_reason.length() +2; return retval; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("view: "); if(view == null) sb.append(""); else sb.append(view); sb.append(", digest: "); if(digest == null) sb.append(""); else sb.append(digest); if(fail_reason != null) sb.append(", fail reason: ").append(fail_reason); return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/ParticipantGmsImpl.java0000644000175000017500000001632211366547366031323 0ustar twernertwerner package org.jgroups.protocols.pbcast; import org.jgroups.*; import org.jgroups.util.Promise; import org.jgroups.util.Digest; import java.util.Vector; import java.util.Iterator; import java.util.Collection; import java.util.LinkedHashSet; /** * @author Bela Ban * @version $Id: ParticipantGmsImpl.java,v 1.29 2007/09/18 20:05:21 vlada Exp $ */ public class ParticipantGmsImpl extends GmsImpl { private final Vector
suspected_mbrs=new Vector
(11); private final Promise leave_promise=new Promise(); public ParticipantGmsImpl(GMS g) { super(g); } public void init() throws Exception { super.init(); suspected_mbrs.removeAllElements(); leave_promise.reset(); } public void join(Address mbr) { wrongMethod("join"); } public void joinWithStateTransfer(Address mbr) { wrongMethod("join"); } /** * Loop: determine coord. If coord is me --> handleLeave(). * Else send handleLeave() to coord until success */ public void leave(Address mbr) { Address coord; int max_tries=3; Boolean result; leave_promise.reset(); if(mbr.equals(gms.local_addr)) leaving=true; while((coord=gms.determineCoordinator()) != null && max_tries-- > 0) { if(gms.local_addr.equals(coord)) { // I'm the coordinator gms.becomeCoordinator(); // gms.getImpl().handleLeave(mbr, false); // regular leave gms.getImpl().leave(mbr); // regular leave return; } if(log.isDebugEnabled()) log.debug("sending LEAVE request to " + coord + " (local_addr=" + gms.local_addr + ")"); sendLeaveMessage(coord, mbr); result=leave_promise.getResult(gms.leave_timeout); if(result != null) break; } gms.becomeClient(); } /** In case we get a different JOIN_RSP from a previous JOIN_REQ sent by us (as a client), we simply apply the * new view if it is greater than ours * * @param join_rsp */ public void handleJoinResponse(JoinRsp join_rsp) { View v=join_rsp.getView(); ViewId tmp_vid=v != null? v.getVid() : null; if(tmp_vid != null && gms.view_id != null && tmp_vid.compareTo(gms.view_id) > 0) { gms.installView(v); } } public void handleLeaveResponse() { leave_promise.setResult(true); // unblocks thread waiting in leave() } public void suspect(Address mbr) { Collection suspected=new LinkedHashSet(1); suspected.add(new Request(Request.SUSPECT,mbr,true,null)); handleMembershipChange(suspected); } /** Removes previously suspected member from list of currently suspected members */ public void unsuspect(Address mbr) { if(mbr != null) suspected_mbrs.remove(mbr); } public void handleMembershipChange(Collection requests) { Collection
suspectedMembers=new LinkedHashSet
(requests.size()); for(Request req: requests) { if(req.type == Request.SUSPECT) suspectedMembers.add(req.mbr); } if(suspectedMembers.isEmpty()) return; for(Address mbr: suspectedMembers) { if(!suspected_mbrs.contains(mbr)) suspected_mbrs.addElement(mbr); } if(log.isDebugEnabled()) log.debug("suspected members=" + suspectedMembers + ", suspected_mbrs=" + suspected_mbrs); if(wouldIBeCoordinator()) { if(log.isDebugEnabled()) log.debug("members are " + gms.members + ", coord=" + gms.local_addr + ": I'm the new coord !"); suspected_mbrs.removeAllElements(); gms.becomeCoordinator(); for(Address mbr: suspectedMembers) { gms.getViewHandler().add(new Request(Request.SUSPECT, mbr, true, null)); gms.ack_collector.suspect(mbr); } } } /** * If we are leaving, we have to wait for the view change (last msg in the current view) that * excludes us before we can leave. * @param new_view The view to be installed * @param digest If view is a MergeView, digest contains the seqno digest of all members and has to * be set by GMS */ public void handleViewChange(View new_view, Digest digest) { Vector mbrs=new_view.getMembers(); if(log.isDebugEnabled()) log.debug("view=" + new_view); suspected_mbrs.removeAllElements(); if(leaving && !mbrs.contains(gms.local_addr)) { // received a view in which I'm not member: ignore return; } gms.installView(new_view, digest); } /* public void handleSuspect(Address mbr) { if(mbr == null) return; if(!suspected_mbrs.contains(mbr)) suspected_mbrs.addElement(mbr); if(log.isDebugEnabled()) log.debug("suspected mbr=" + mbr + ", suspected_mbrs=" + suspected_mbrs); if(wouldIBeCoordinator()) { if(log.isDebugEnabled()) log.debug("suspected mbr=" + mbr + "), members are " + gms.members + ", coord=" + gms.local_addr + ": I'm the new coord !"); suspected_mbrs.removeAllElements(); gms.becomeCoordinator(); // gms.getImpl().suspect(mbr); gms.getViewHandler().add(new Request(Request.SUSPECT, mbr, true, null)); gms.ack_collector.suspect(mbr); } }*/ public void handleMergeRequest(Address sender, ViewId merge_id) { // only coords handle this method; reject it if we're not coord sendMergeRejectedResponse(sender, merge_id); } /* ---------------------------------- Private Methods --------------------------------------- */ /** * Determines whether this member is the new coordinator given a list of suspected members. This is * computed as follows: the list of currently suspected members (suspected_mbrs) is removed from the current * membership. If the first member of the resulting list is equals to the local_addr, then it is true, * otherwise false. Example: own address is B, current membership is {A, B, C, D}, suspected members are {A, * D}. The resulting list is {B, C}. The first member of {B, C} is B, which is equal to the * local_addr. Therefore, true is returned. */ boolean wouldIBeCoordinator() { Address new_coord; Vector
mbrs=gms.members.getMembers(); // getMembers() returns a *copy* of the membership vector mbrs.removeAll(suspected_mbrs); if(mbrs.size() < 1) return false; new_coord=mbrs.firstElement(); return gms.local_addr.equals(new_coord); } void sendLeaveMessage(Address coord, Address mbr) { Message msg=new Message(coord, null, null); msg.setFlag(Message.OOB); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.LEAVE_REQ, mbr); msg.putHeader(gms.getName(), hdr); gms.getDownProtocol().down(new Event(Event.MSG, msg)); } /* ------------------------------ End of Private Methods ------------------------------------ */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/Gossip.java0000644000175000017500000000612211366547366027015 0ustar twernertwerner// $Id: Gossip.java,v 1.7.4.1 2008/01/22 10:01:27 belaban Exp $ package org.jgroups.protocols.pbcast; import org.jgroups.Address; import org.jgroups.util.Digest; import java.io.Serializable; import java.util.Vector; import java.net.UnknownHostException; public class Gossip implements Serializable { Address sender=null; long id=-1; Digest digest=null; Vector not_seen=null; // members who haven't seen this specific gossip yet Vector seen=new Vector(11); // members who have seen the gossip already private static final long serialVersionUID = 7954243278668401185L; public Gossip(Address obj, long id) { sender=obj; this.id=id; } public Gossip(Address obj, long id, Digest d, Vector not_seen) { sender=obj; this.id=id; digest=d; this.not_seen=not_seen; } /** * Removes obj from not_seen list */ public void removeFromNotSeenList(Address mbr) { if(not_seen != null && mbr != null) not_seen.removeElement(mbr); } public void addToSeenList(Address mbr) { if(mbr != null && !seen.contains(mbr)) seen.addElement(mbr); } public int sizeOfNotSeenList() { return not_seen == null ? 0 : not_seen.size(); } public Vector getNotSeenList() { return not_seen; } public Vector getSeenList() { return seen; } public boolean equals(Object o) { Gossip other=null; if(sender != null && o != null) { other=(Gossip)o; return sender.equals(other.sender) && id == other.id; } return false; } public int hashCode() { if(sender != null) return sender.hashCode() + (int)id; else return (int)id; } public Gossip copy() { Gossip ret=new Gossip(sender, id); if(digest != null) ret.digest=digest.copy(); if(not_seen != null) ret.not_seen=(Vector)not_seen.clone(); if(seen != null) ret.seen=(Vector)seen.clone(); return ret; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("sender="); if(sender != null) sb.append(sender); else sb.append(""); if(digest != null) sb.append(", digest=" + digest); if(not_seen != null) sb.append(", not_seen=" + not_seen); if(seen != null) sb.append(", seen=" + seen); sb.append(", id=" + id); return sb.toString(); } public String shortForm() { StringBuilder sb=new StringBuilder(); if(sender != null) sb.append(sender); else sb.append(""); sb.append("#" + id); return sb.toString(); } public static void main(String[] args) throws UnknownHostException { Gossip id1, id2; id1=new Gossip(new org.jgroups.stack.IpAddress("daddy", 4567), 23); id2=new Gossip(new org.jgroups.stack.IpAddress("133.164.130.19", 4567), 23); System.out.println(id1.equals(id2)); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/pbcast/NAKACK.java0000644000175000017500000021221711366547366026505 0ustar twernertwernerpackage org.jgroups.protocols.pbcast; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.*; import org.jgroups.util.*; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Negative AcKnowledgement layer (NAKs). Messages are assigned a monotonically increasing sequence number (seqno). * Receivers deliver messages ordered according to seqno and request retransmission of missing messages.
* Retransmit requests are usually sent to the original sender of a message, but this can be changed by * xmit_from_random_member (send to random member) or use_mcast_xmit_req (send to everyone). Responses can also be sent * to everyone instead of the requester by setting use_mcast_xmit to true. * * @author Bela Ban * @version $Id: NAKACK.java,v 1.170.2.18 2009/09/25 11:33:25 belaban Exp $ */ public class NAKACK extends Protocol implements Retransmitter.RetransmitCommand, NakReceiverWindow.Listener { private long[] retransmit_timeouts={600, 1200, 2400, 4800}; // time(s) to wait before requesting retransmission private boolean is_server=false; private Address local_addr=null; private final List
members=new CopyOnWriteArrayList
(); private View view; @GuardedBy("seqno_lock") private long seqno=0; // current message sequence number (starts with 1) private final Lock seqno_lock=new ReentrantLock(); private int gc_lag=20; // number of msgs garbage collection lags behind private static final long INITIAL_SEQNO=0; /** * Retransmit messages using multicast rather than unicast. This has the advantage that, if many receivers lost a * message, the sender only retransmits once. */ private boolean use_mcast_xmit=true; /** Use a multicast to request retransmission of missing messages. This may be costly as every member in the cluster * will send a response */ private boolean use_mcast_xmit_req=false; /** * Ask a random member for retransmission of a missing message. If set to true, discard_delivered_msgs will be * set to false */ private boolean xmit_from_random_member=false; /** The first value (in milliseconds) to use in the exponential backoff retransmission mechanism. Only enabled * if the value is > 0 */ private long exponential_backoff=0; /** If enabled, we use statistics gathered from actual retransmission times to compute the new retransmission times */ private boolean use_stats_for_retransmission=false; /** * Messages that have been received in order are sent up the stack (= delivered to the application). Delivered * messages are removed from NakReceiverWindow.xmit_table and moved to NakReceiverWindow.delivered_msgs, where * they are later garbage collected (by STABLE). Since we do retransmits only from sent messages, never * received or delivered messages, we can turn the moving to delivered_msgs off, so we don't keep the message * around, and don't need to wait for garbage collection to remove them. */ private boolean discard_delivered_msgs=false; /** Whether to emit a warning on reception of messages from members not in our view */ private boolean log_discard_msgs=true; /** If value is > 0, the retransmit buffer is bounded: only the max_xmit_buf_size latest messages are kept, * older ones are discarded when the buffer size is exceeded. A value <= 0 means unbounded buffers */ private int max_xmit_buf_size=0; /** Map to store sent and received messages (keyed by sender) */ private final ConcurrentMap xmit_table=new ConcurrentHashMap(11); /** Map which keeps track of threads removing messages from NakReceiverWindows, so we don't wait while a thread * is removing messages */ // private final ConcurrentMap in_progress=new ConcurrentHashMap(); private boolean leaving=false; private boolean started=false; private TimeScheduler timer=null; private static final String name="NAKACK"; private long xmit_reqs_received; private long xmit_reqs_sent; private long xmit_rsps_received; private long xmit_rsps_sent; private long missing_msgs_received; /** Captures stats on XMIT_REQS, XMIT_RSPS per sender */ private Map sent=new ConcurrentHashMap(); /** Captures stats on XMIT_REQS, XMIT_RSPS per receiver */ private Map received=new ConcurrentHashMap(); private int stats_list_size=20; /** BoundedList. Keeps track of the last stats_list_size XMIT requests */ private BoundedList receive_history; /** BoundedList. Keeps track of the last stats_list_size missing messages received */ private BoundedList send_history; /** Per-sender map of seqnos and timestamps, to keep track of avg times for retransmission of messages */ private final ConcurrentMap> xmit_stats=new ConcurrentHashMap>(); private int xmit_history_max_size=50; /** Maintains a list of the last N retransmission times (duration it took to retransmit a message) for all members */ private final ConcurrentMap> xmit_times_history=new ConcurrentHashMap>(); /** Maintains a smoothed average of the retransmission times per sender, these are the actual values that are used for * new retransmission requests */ private final Map smoothed_avg_xmit_times=new HashMap(); /** the weight with which we take the previous smoothed average into account, WEIGHT should be >0 and <= 1 */ private static final double WEIGHT=0.9; private static final double INITIAL_SMOOTHED_AVG=30.0; // private final ConcurrentMap loss_rates=new ConcurrentHashMap(); /** * Maintains retransmission related data across a time. Only used if enable_xmit_time_stats is set to true. * At program termination, accumulated data is dumped to a file named by the address of the member. Careful, * don't enable this in production as the data in this hashmap are never reaped ! Really only meant for * diagnostics ! */ private ConcurrentMap xmit_time_stats=null; private long xmit_time_stats_start; /** Keeps track of OOB messages sent by myself, needed by {@link #handleMessage(org.jgroups.Message, NakAckHeader)} */ private final Set oob_loopback_msgs=Collections.synchronizedSet(new HashSet()); private final Lock rebroadcast_lock=new ReentrantLock(); private final Condition rebroadcast_done=rebroadcast_lock.newCondition(); // set during processing of a rebroadcast event private volatile boolean rebroadcasting=false; private final Lock rebroadcast_digest_lock=new ReentrantLock(); @GuardedBy("rebroadcast_digest_lock") private Digest rebroadcast_digest=null; private long max_rebroadcast_timeout=2000; private static final int NUM_REBROADCAST_MSGS=3; /** BoundedList, keeps the last 10 stability messages */ protected final BoundedList stability_msgs=new BoundedList(10); protected final BoundedList merge_history=new BoundedList(10); /** When not finding a message on an XMIT request, include the last N stability messages in the error message */ protected boolean print_stability_history_on_failed_xmit=false; /** Regular messages which have been added, but not removed */ private final AtomicInteger undelivered_msgs=new AtomicInteger(0); public NAKACK() { } public String getName() { return name; } public long getXmitRequestsReceived() {return xmit_reqs_received;} public long getXmitRequestsSent() {return xmit_reqs_sent;} public long getXmitResponsesReceived() {return xmit_rsps_received;} public long getXmitResponsesSent() {return xmit_rsps_sent;} public long getMissingMessagesReceived() {return missing_msgs_received;} public long getUndeliveredMessages() {return undelivered_msgs.get();} public int getPendingRetransmissionRequests() { int num=0; for(NakReceiverWindow win: xmit_table.values()) { num+=win.getPendingXmits(); } return num; } public int getXmitTableSize() { int num=0; for(NakReceiverWindow win: xmit_table.values()) { num+=win.size(); } return num; } public int getReceivedTableSize() { return getPendingRetransmissionRequests(); } public void resetStats() { xmit_reqs_received=xmit_reqs_sent=xmit_rsps_received=xmit_rsps_sent=missing_msgs_received=0; sent.clear(); received.clear(); if(receive_history !=null) receive_history.clear(); if(send_history != null) send_history.clear(); stability_msgs.clear(); merge_history.clear(); } public void init() throws Exception { if(stats) { send_history=new BoundedList(stats_list_size); receive_history=new BoundedList(stats_list_size); } } public int getGcLag() { return gc_lag; } public void setGcLag(int gc_lag) { this.gc_lag=gc_lag; } public boolean isUseMcastXmit() { return use_mcast_xmit; } public void setUseMcastXmit(boolean use_mcast_xmit) { this.use_mcast_xmit=use_mcast_xmit; } public boolean isXmitFromRandomMember() { return xmit_from_random_member; } public void setXmitFromRandomMember(boolean xmit_from_random_member) { this.xmit_from_random_member=xmit_from_random_member; } public boolean isDiscardDeliveredMsgs() { return discard_delivered_msgs; } public void setDiscardDeliveredMsgs(boolean discard_delivered_msgs) { boolean old=this.discard_delivered_msgs; this.discard_delivered_msgs=discard_delivered_msgs; if(old != this.discard_delivered_msgs) { for(NakReceiverWindow win: xmit_table.values()) { win.setDiscardDeliveredMessages(this.discard_delivered_msgs); } } } public int getMaxXmitBufSize() { return max_xmit_buf_size; } public void setMaxXmitBufSize(int max_xmit_buf_size) { this.max_xmit_buf_size=max_xmit_buf_size; } /** * * @return * @deprecated removed in 2.6 */ public long getMaxXmitSize() { return -1; } /** * * @param max_xmit_size * @deprecated removed in 2.6 */ public void setMaxXmitSize(long max_xmit_size) { } public boolean isLogDiscardMsgs() { return log_discard_msgs; } public void setLogDiscardMsgs(boolean log_discard_msgs) { this.log_discard_msgs=log_discard_msgs; } public boolean setProperties(Properties props) { String str; long[] tmp; super.setProperties(props); str=props.getProperty("retransmit_timeout"); if(str != null) { tmp=Util.parseCommaDelimitedLongs(str); props.remove("retransmit_timeout"); if(tmp != null && tmp.length > 0) { retransmit_timeouts=tmp; } } str=props.getProperty("gc_lag"); if(str != null) { gc_lag=Integer.parseInt(str); if(gc_lag < 0) { log.error("gc_lag cannot be negative, setting it to 0"); } props.remove("gc_lag"); } str=props.getProperty("max_xmit_size"); if(str != null) { if(log.isWarnEnabled()) log.warn("max_xmit_size was deprecated in 2.6 and will be ignored"); props.remove("max_xmit_size"); } str=props.getProperty("use_mcast_xmit"); if(str != null) { use_mcast_xmit=Boolean.valueOf(str).booleanValue(); props.remove("use_mcast_xmit"); } str=props.getProperty("use_mcast_xmit_req"); if(str != null) { use_mcast_xmit_req=Boolean.valueOf(str).booleanValue(); props.remove("use_mcast_xmit_req"); } str=props.getProperty("exponential_backoff"); if(str != null) { exponential_backoff=Long.parseLong(str); props.remove("exponential_backoff"); } str=props.getProperty("use_stats_for_retransmission"); if(str != null) { use_stats_for_retransmission=Boolean.valueOf(str); props.remove("use_stats_for_retransmission"); } str=props.getProperty("discard_delivered_msgs"); if(str != null) { discard_delivered_msgs=Boolean.valueOf(str); props.remove("discard_delivered_msgs"); } str=props.getProperty("xmit_from_random_member"); if(str != null) { xmit_from_random_member=Boolean.valueOf(str); props.remove("xmit_from_random_member"); } str=props.getProperty("max_xmit_buf_size"); if(str != null) { max_xmit_buf_size=Integer.parseInt(str); props.remove("max_xmit_buf_size"); } str=props.getProperty("stats_list_size"); if(str != null) { stats_list_size=Integer.parseInt(str); props.remove("stats_list_size"); } str=props.getProperty("xmit_history_max_size"); if(str != null) { xmit_history_max_size=Integer.parseInt(str); props.remove("xmit_history_max_size"); } str=props.getProperty("enable_xmit_time_stats"); if(str != null) { boolean enable_xmit_time_stats=Boolean.valueOf(str); props.remove("enable_xmit_time_stats"); if(enable_xmit_time_stats) { if(log.isWarnEnabled()) log.warn("enable_xmit_time_stats is experimental, and may be removed in any release"); xmit_time_stats=new ConcurrentHashMap(); xmit_time_stats_start=System.currentTimeMillis(); } } str=props.getProperty("max_rebroadcast_timeout"); if(str != null) { max_rebroadcast_timeout=Long.parseLong(str); props.remove("max_rebroadcast_timeout"); } str=props.getProperty("eager_lock_release"); if(str != null) { log.warn("eager_lock_release has been deprecated and is ignored"); props.remove("eager_lock_release"); } if(xmit_from_random_member) { if(discard_delivered_msgs) { discard_delivered_msgs=false; log.warn("xmit_from_random_member set to true: changed discard_delivered_msgs to false"); } } str=props.getProperty("print_stability_history_on_failed_xmit"); if(str != null) { print_stability_history_on_failed_xmit=Boolean.valueOf(str).booleanValue(); props.remove("print_stability_history_on_failed_xmit"); } if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } public Map dumpStats() { Map retval=super.dumpStats(); if(retval == null) retval=new HashMap(); retval.put("xmit_reqs_received", new Long(xmit_reqs_received)); retval.put("xmit_reqs_sent", new Long(xmit_reqs_sent)); retval.put("xmit_rsps_received", new Long(xmit_rsps_received)); retval.put("xmit_rsps_sent", new Long(xmit_rsps_sent)); retval.put("missing_msgs_received", new Long(missing_msgs_received)); retval.put("msgs", printMessages()); return retval; } public String printStats() { Map.Entry entry; Object key, val; StringBuilder sb=new StringBuilder(); sb.append("sent:\n"); for(Iterator it=sent.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); if(key == null) key=""; val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } sb.append("\nreceived:\n"); for(Iterator it=received.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } sb.append("\nXMIT_REQS sent:\n"); for(XmitRequest tmp: send_history) { sb.append(tmp).append("\n"); } sb.append("\nMissing messages received\n"); for(MissingMessage missing: receive_history) { sb.append(missing).append("\n"); } sb.append("\nStability messages received\n"); sb.append(printStabilityMessages()).append("\n"); return sb.toString(); } public String printStabilityMessages() { StringBuilder sb=new StringBuilder(); sb.append(Util.printListWithDelimiter(stability_msgs, "\n")); return sb.toString(); } public String printStabilityHistory() { StringBuilder sb=new StringBuilder(); int i=1; for(Digest digest: stability_msgs) { sb.append(i++).append(": ").append(digest).append("\n"); } return sb.toString(); } public String printMergeHistory() { StringBuilder sb=new StringBuilder(); for(String tmp: merge_history) sb.append(tmp).append("\n"); return sb.toString(); } public String printLossRates() { StringBuilder sb=new StringBuilder(); NakReceiverWindow win; for(Map.Entry entry: xmit_table.entrySet()) { win=entry.getValue(); sb.append(entry.getKey()).append(": ").append(win.printLossRate()).append("\n"); } return sb.toString(); } public double getAverageLossRate() { double retval=0.0; int count=0; if(xmit_table.isEmpty()) return 0.0; for(NakReceiverWindow win: xmit_table.values()) { retval+=win.getLossRate(); count++; } return retval / (double)count; } public double getAverageSmoothedLossRate() { double retval=0.0; int count=0; if(xmit_table.isEmpty()) return 0.0; for(NakReceiverWindow win: xmit_table.values()) { retval+=win.getSmoothedLossRate(); count++; } return retval / (double)count; } public Vector providedUpServices() { Vector retval=new Vector(5); retval.addElement(new Integer(Event.GET_DIGEST)); retval.addElement(new Integer(Event.SET_DIGEST)); retval.addElement(new Integer(Event.MERGE_DIGEST)); return retval; } public void start() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer is null"); started=true; if(xmit_time_stats != null) { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { String filename="xmit-stats-" + local_addr + ".log"; System.out.println("-- dumping runtime xmit stats to " + filename); try { dumpXmitStats(filename); } catch(IOException e) { e.printStackTrace(); } } }); } } public void stop() { started=false; reset(); // clears sent_msgs and destroys all NakReceiverWindows oob_loopback_msgs.clear(); } /** * Callback. Called by superclass when event may be handled.

Do not use down_prot.down() in this * method as the event is passed down by default by the superclass after this method returns ! */ public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest != null && !dest.isMulticastAddress()) { break; // unicast address: not null and not mcast, pass down unchanged } send(evt, msg); return null; // don't pass down the stack case Event.STABLE: // generated by STABLE layer. Delete stable messages passed in arg stable((Digest)evt.getArg()); return null; // do not pass down further (Bela Aug 7 2001) case Event.GET_DIGEST: return getDigest(); case Event.SET_DIGEST: setDigest((Digest)evt.getArg()); return null; case Event.MERGE_DIGEST: mergeDigest((Digest)evt.getArg()); return null; case Event.TMP_VIEW: View tmp_view=(View)evt.getArg(); Vector

mbrs=tmp_view.getMembers(); members.clear(); members.addAll(mbrs); // adjustReceivers(false); break; case Event.VIEW_CHANGE: tmp_view=(View)evt.getArg(); mbrs=tmp_view.getMembers(); members.clear(); members.addAll(mbrs); adjustReceivers(members); is_server=true; // check vids from now on Set
tmp=new LinkedHashSet
(members); tmp.add(null); // for null destination (= mcast) sent.keySet().retainAll(tmp); received.keySet().retainAll(tmp); view=tmp_view; xmit_stats.keySet().retainAll(tmp); // in_progress.keySet().retainAll(mbrs); // remove elements which are not in the membership break; case Event.BECOME_SERVER: is_server=true; break; case Event.DISCONNECT: leaving=true; reset(); break; case Event.REBROADCAST: rebroadcasting=true; rebroadcast_digest=(Digest)evt.getArg(); try { rebroadcastMessages(); } finally { rebroadcasting=false; rebroadcast_digest_lock.lock(); try { rebroadcast_digest=null; } finally { rebroadcast_digest_lock.unlock(); } } return null; } return down_prot.down(evt); } /** * Callback. Called by superclass when event may be handled.

Do not use PassUp in this * method as the event is passed up by default by the superclass after this method returns ! */ public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); NakAckHeader hdr=(NakAckHeader)msg.getHeader(name); if(hdr == null) break; // pass up (e.g. unicast msg) // discard messages while not yet server (i.e., until JOIN has returned) if(!is_server) { if(log.isTraceEnabled()) log.trace("message was discarded (not yet server)"); return null; } // Changed by bela Jan 29 2003: we must not remove the header, otherwise // further xmit requests will fail ! //hdr=(NakAckHeader)msg.removeHeader(getName()); switch(hdr.type) { case NakAckHeader.MSG: handleMessage(msg, hdr); return null; // transmitter passes message up for us ! case NakAckHeader.XMIT_REQ: if(hdr.range == null) { if(log.isErrorEnabled()) { log.error("XMIT_REQ: range of xmit msg is null; discarding request from " + msg.getSrc()); } return null; } handleXmitReq(msg.getSrc(), hdr.range.low, hdr.range.high, hdr.sender); return null; case NakAckHeader.XMIT_RSP: if(log.isTraceEnabled()) log.trace("received missing message " + msg.getSrc() + ":" + hdr.seqno); handleXmitRsp(msg); return null; default: if(log.isErrorEnabled()) { log.error("NakAck header type " + hdr.type + " not known !"); } return null; } case Event.STABLE: // generated by STABLE layer. Delete stable messages passed in arg stable((Digest)evt.getArg()); return null; // do not pass up further (Bela Aug 7 2001) case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.SUSPECT: // release the promise if rebroadcasting is in progress... otherwise we wait forever. there will be a new // flush round anyway if(rebroadcasting) { cancelRebroadcasting(); } break; } return up_prot.up(evt); } /* --------------------------------- Private Methods --------------------------------------- */ /** * Adds the message to the sent_msgs table and then passes it down the stack. Change Bela Ban May 26 2002: we don't * store a copy of the message, but a reference ! This saves us a lot of memory. However, this also means that a * message should not be changed after storing it in the sent-table ! See protocols/DESIGN for details. * Made seqno increment and adding to sent_msgs atomic, e.g. seqno won't get incremented if adding to * sent_msgs fails e.g. due to an OOM (see http://jira.jboss.com/jira/browse/JGRP-179). bela Jan 13 2006 */ private void send(Event evt, Message msg) { if(msg == null) throw new NullPointerException("msg is null; event is " + evt); if(!started) { if(log.isTraceEnabled()) log.trace("[" + local_addr + "] discarded message as start() has not been called, message: " + msg); return; } long msg_id; NakReceiverWindow win=xmit_table.get(local_addr); msg.setSrc(local_addr); // this needs to be done so we can check whether the message sender is the local_addr seqno_lock.lock(); try { try { // incrementing seqno and adding the msg to sent_msgs needs to be atomic msg_id=seqno +1; msg.putHeader(name, new NakAckHeader(NakAckHeader.MSG, msg_id)); if(win.add(msg_id, msg) && !msg.isFlagSet(Message.OOB)) undelivered_msgs.incrementAndGet(); seqno=msg_id; } catch(Throwable t) { throw new RuntimeException("failure adding msg " + msg + " to the retransmit table for " + local_addr, t); } } finally { seqno_lock.unlock(); } try { // moved down_prot.down() out of synchronized clause (bela Sept 7 2006) http://jira.jboss.com/jira/browse/JGRP-300 if(msg.isFlagSet(Message.OOB)) oob_loopback_msgs.add(msg_id); if(log.isTraceEnabled()) log.trace("sending " + local_addr + "#" + msg_id); down_prot.down(evt); // if this fails, since msg is in sent_msgs, it can be retransmitted } catch(Throwable t) { // eat the exception, don't pass it up the stack if(log.isWarnEnabled()) { log.warn("failure passing message down", t); } } } /** * Finds the corresponding NakReceiverWindow and adds the message to it (according to seqno). Then removes as many * messages as possible from the NRW and passes them up the stack. Discards messages from non-members. */ private void handleMessage(Message msg, NakAckHeader hdr) { Address sender=msg.getSrc(); if(sender == null) { if(log.isErrorEnabled()) log.error("sender of message is null"); return; } if(log.isTraceEnabled()) log.trace(new StringBuilder().append('[').append(local_addr).append(": received ").append(sender).append('#').append(hdr.seqno)); NakReceiverWindow win=xmit_table.get(sender); if(win == null) { // discard message if there is no entry for sender if(leaving) return; if(log.isWarnEnabled() && log_discard_msgs) log.warn(local_addr + "] discarded message from non-member " + sender + ", my view is " + view); return; } boolean loopback=local_addr.equals(sender); boolean added_to_window=false; boolean added=loopback || (added_to_window=win.add(hdr.seqno, msg)); if(added_to_window) undelivered_msgs.incrementAndGet(); // message is passed up if OOB. Later, when remove() is called, we discard it. This affects ordering ! // http://jira.jboss.com/jira/browse/JGRP-379 if(added && msg.isFlagSet(Message.OOB)) { if(!loopback || oob_loopback_msgs.remove(hdr.seqno)) { up_prot.up(new Event(Event.MSG, msg)); win.removeOOBMessage(); if(!(win.hasMessagesToRemove() && undelivered_msgs.get() > 0)) return; } } // Efficient way of checking whether another thread is already processing messages from 'sender'. // If that's the case, we return immediately and let the existing thread process our message // (https://jira.jboss.org/jira/browse/JGRP-829). Benefit: fewer threads blocked on the same lock, these threads // can be returned to the thread pool final AtomicBoolean processing=win.getProcessing(); if(!processing.compareAndSet(false, true)) { return; } // Prevents concurrent passing up of messages by different threads (http://jira.jboss.com/jira/browse/JGRP-198); // this is all the more important once we have a threadless stack (http://jira.jboss.com/jira/browse/JGRP-181), // where lots of threads can come up to this point concurrently, but only 1 is allowed to pass at a time // We *can* deliver messages from *different* senders concurrently, e.g. reception of P1, Q1, P2, Q2 can result in // delivery of P1, Q1, Q2, P2: FIFO (implemented by NAKACK) says messages need to be delivered in the // order in which they were sent by the sender int num_regular_msgs_removed=0; // 2nd line of defense: in case of an exception, remove() might not be called, therefore processing would never // be set back to false. If we get an exception and released_processing is not true, then we set // processing to false in the finally clause boolean released_processing=false; try { while(true) { // we're removing a msg and set processing to false (if null) *atomically* (wrt to add()) Message msg_to_deliver=win.remove(processing); if(msg_to_deliver == null) { released_processing=true; return; // processing will be set to false now } // discard OOB msg as it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-379) if(msg_to_deliver.isFlagSet(Message.OOB)) { continue; } num_regular_msgs_removed++; // System.out.println("removed regular #" + ((NakAckHeader)msg_to_deliver.getHeader(name)).seqno); // Changed by bela Jan 29 2003: not needed (see above) //msg_to_deliver.removeHeader(getName()); up_prot.up(new Event(Event.MSG, msg_to_deliver)); } } finally { // We keep track of regular messages that we added, but couldn't remove (because of ordering). // When we have such messages pending, then even OOB threads will remove and process them // http://jira.jboss.com/jira/browse/JGRP-781 undelivered_msgs.addAndGet(-num_regular_msgs_removed); // processing is always set in win.remove(processing) above and never here ! This code is just a // 2nd line of defense should there be an exception before win.remove(processing) sets processing if(!released_processing) processing.set(false); } } /** * Retransmits messsages first_seqno to last_seqno from original_sender from xmit_table to xmit_requester, * called when XMIT_REQ is received. * @param xmit_requester The sender of the XMIT_REQ, we have to send the requested copy of the message to this address * @param first_seqno The first sequence number to be retransmitted (<= last_seqno) * @param last_seqno The last sequence number to be retransmitted (>= first_seqno) * @param original_sender The member who originally sent the messsage. Guaranteed to be non-null */ private void handleXmitReq(Address xmit_requester, long first_seqno, long last_seqno, Address original_sender) { Message msg; if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(": received xmit request from ").append(xmit_requester).append(" for "); sb.append(original_sender).append(" [").append(first_seqno).append(" - ").append(last_seqno).append("]"); log.trace(sb.toString()); } if(first_seqno > last_seqno) { if(log.isErrorEnabled()) log.error("first_seqno (" + first_seqno + ") > last_seqno (" + last_seqno + "): not able to retransmit"); return; } if(stats) { xmit_reqs_received+=last_seqno - first_seqno +1; updateStats(received, xmit_requester, 1, 0, 0); } if(xmit_time_stats != null) { long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; XmitTimeStat stat=xmit_time_stats.get(key); if(stat == null) { stat=new XmitTimeStat(); XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); if(stat2 != null) stat=stat2; } stat.xmit_reqs_received.addAndGet((int)(last_seqno - first_seqno +1)); stat.xmit_rsps_sent.addAndGet((int)(last_seqno - first_seqno +1)); } NakReceiverWindow win=xmit_table.get(original_sender); if(win == null) { if(log.isErrorEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("(requester=").append(xmit_requester).append(", local_addr=").append(this.local_addr); sb.append(") ").append(original_sender).append(" not found in retransmission table:\n").append(printMessages()); if(print_stability_history_on_failed_xmit) { sb.append(" (stability history:\n").append(printStabilityHistory()); } log.error(sb); } return; } for(long i=first_seqno; i <= last_seqno; i++) { msg=win.get(i); if(msg == null) { if(log.isWarnEnabled() && !local_addr.equals(xmit_requester)) { StringBuilder sb=new StringBuilder(); sb.append("(requester=").append(xmit_requester).append(", local_addr=").append(this.local_addr); sb.append(") message ").append(original_sender).append("::").append(i); sb.append(" not found in retransmission table of ").append(original_sender).append(":\n").append(win); if(print_stability_history_on_failed_xmit) { sb.append(" (stability history:\n").append(printStabilityHistory()); } log.warn(sb); } continue; } sendXmitRsp(xmit_requester, msg, i); } } private void cancelRebroadcasting() { rebroadcast_lock.lock(); try { rebroadcasting=false; rebroadcast_done.signalAll(); } finally { rebroadcast_lock.unlock(); } } private static void updateStats(Map map, Address key, int req, int rsp, int missing) { if(key != null) { StatsEntry entry=map.get(key); if(entry == null) { entry=new StatsEntry(); map.put(key, entry); } entry.xmit_reqs+=req; entry.xmit_rsps+=rsp; entry.missing_msgs_rcvd+=missing; } } /** * Sends a message msg to the requester. We have to wrap the original message into a retransmit message, as we need * to preserve the original message's properties, such as src, headers etc. * @param dest * @param msg * @param seqno */ private void sendXmitRsp(Address dest, Message msg, long seqno) { Buffer buf; if(msg == null) { if(log.isErrorEnabled()) log.error("message is null, cannot send retransmission"); return; } if(use_mcast_xmit) dest=null; if(stats) { xmit_rsps_sent++; updateStats(sent, dest, 0, 1, 0); } if(msg.getSrc() == null) msg.setSrc(local_addr); try { buf=Util.messageToByteBuffer(msg); Message xmit_msg=new Message(dest, null, buf.getBuf(), buf.getOffset(), buf.getLength()); // changed Bela Jan 4 2007: we should not use OOB for retransmitted messages, otherwise we tax the OOB thread pool // too much // msg.setFlag(Message.OOB); xmit_msg.putHeader(name, new NakAckHeader(NakAckHeader.XMIT_RSP, seqno)); down_prot.down(new Event(Event.MSG, xmit_msg)); } catch(IOException ex) { log.error("failed marshalling xmit list", ex); } } private void handleXmitRsp(Message msg) { if(msg == null) { if(log.isWarnEnabled()) log.warn("message is null"); return; } try { Message wrapped_msg=Util.byteBufferToMessage(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); if(xmit_time_stats != null) { long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; XmitTimeStat stat=xmit_time_stats.get(key); if(stat == null) { stat=new XmitTimeStat(); XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); if(stat2 != null) stat=stat2; } stat.xmit_rsps_received.incrementAndGet(); } if(stats) { xmit_rsps_received++; updateStats(received, msg.getSrc(), 0, 1, 0); } up(new Event(Event.MSG, wrapped_msg)); if(rebroadcasting) { Digest tmp=getDigest(); boolean cancel_rebroadcasting; rebroadcast_digest_lock.lock(); try { cancel_rebroadcasting=tmp.isGreaterThanOrEqual(rebroadcast_digest); } finally { rebroadcast_digest_lock.unlock(); } if(cancel_rebroadcasting) { cancelRebroadcasting(); } } } catch(Exception ex) { if(log.isErrorEnabled()) { log.error("failed reading retransmitted message", ex); } } } /** * Takes the argument highest_seqnos and compares it to the current digest. If the current digest has fewer messages, * then send retransmit messages for the missing messages. Return when all missing messages have been received. If * we're waiting for a missing message from P, and P crashes while waiting, we need to exclude P from the wait set. */ private void rebroadcastMessages() { Digest my_digest; Map their_digest; Address sender; Digest.Entry their_entry, my_entry; long their_high, my_high; long sleep=max_rebroadcast_timeout / NUM_REBROADCAST_MSGS; long wait_time=max_rebroadcast_timeout, start=System.currentTimeMillis(); while(wait_time > 0) { rebroadcast_digest_lock.lock(); try { if(rebroadcast_digest == null) break; their_digest=rebroadcast_digest.getSenders(); } finally { rebroadcast_digest_lock.unlock(); } my_digest=getDigest(); boolean xmitted=false; for(Map.Entry entry: their_digest.entrySet()) { sender=entry.getKey(); their_entry=entry.getValue(); my_entry=my_digest.get(sender); if(my_entry == null) continue; their_high=their_entry.getHighest(); my_high=my_entry.getHighest(); if(their_high > my_high) { if(log.isTraceEnabled()) log.trace("sending XMIT request to " + sender + " for messages " + my_high + " - " + their_high); retransmit(my_high, their_high, sender, true); // use multicast to send retransmit request xmitted=true; } } if(!xmitted) return; // we're done; no retransmissions are needed anymore. our digest is >= rebroadcast_digest rebroadcast_lock.lock(); try { try { my_digest=getDigest(); rebroadcast_digest_lock.lock(); try { if(!rebroadcasting || my_digest.isGreaterThanOrEqual(rebroadcast_digest)) return; } finally { rebroadcast_digest_lock.unlock(); } rebroadcast_done.await(sleep, TimeUnit.MILLISECONDS); wait_time-=(System.currentTimeMillis() - start); } catch(InterruptedException e) { } } finally { rebroadcast_lock.unlock(); } } } /** * Remove old members from NakReceiverWindows and add new members (starting seqno=0). Essentially removes all * entries from xmit_table that are not in members. This method is not called concurrently * multiple times */ private void adjustReceivers(List

new_members) { NakReceiverWindow win; // 1. Remove all senders in xmit_table that are not members anymore for(Iterator
it=xmit_table.keySet().iterator(); it.hasNext();) { Address sender=it.next(); if(!new_members.contains(sender)) { if(local_addr != null && local_addr.equals(sender)) { if(log.isErrorEnabled()) log.error("will not remove myself (" + sender + ") from xmit_table, received incorrect new membership of " + new_members); continue; } win=xmit_table.get(sender); win.reset(); if(log.isDebugEnabled()) { log.debug("removing " + sender + " from xmit_table (not member anymore)"); } it.remove(); } } // 2. Add newly joined members to xmit_table (starting seqno=0) for(Address sender: new_members) { if(!xmit_table.containsKey(sender)) { win=createNakReceiverWindow(sender, INITIAL_SEQNO, 0); xmit_table.put(sender, win); } } } /** * Returns a message digest: for each member P the lowest, highest delivered and highest received seqno is added */ private Digest getDigest() { Digest.Entry entry; Map map=new HashMap(members.size()); for(Address sender: members) { entry=getEntry(sender); if(entry == null) { if(log.isErrorEnabled()) { log.error("range is null"); } continue; } map.put(sender, entry); } return new Digest(map); } /** * Creates a NakReceiverWindow for each sender in the digest according to the sender's seqno. If NRW already exists, * reset it. */ private void setDigest(Digest digest) { if(digest == null) return; for(Map.Entry entry: digest.getSenders().entrySet()) { Address sender=entry.getKey(); Digest.Entry val=entry.getValue(); if(sender == null || val == null) continue; long initial_seqno=val.getHighestDeliveredSeqno(); NakReceiverWindow win=xmit_table.get(sender); if(win != null) { win.reset(); // stops retransmission xmit_table.remove(sender); } win=createNakReceiverWindow(sender, initial_seqno, val.getLow()); xmit_table.put(sender, win); } } /** * For all members of the digest, adjust the NakReceiverWindows in the xmit_table hashtable. If no entry * exists, create one with the initial seqno set to the seqno of the member in the digest. If the member already * exists, and is not the local address, replace it with the new entry (http://jira.jboss.com/jira/browse/JGRP-699) */ private void mergeDigest(Digest digest) { if(digest == null) { if(log.isErrorEnabled()) { log.error("digest or digest.senders is null"); } return; } StringBuilder sb=new StringBuilder(); sb.append("existing digest: " + getDigest()).append("\nnew digest: " + digest); for(Map.Entry entry: digest.getSenders().entrySet()) { Address sender=entry.getKey(); Digest.Entry val=entry.getValue(); if(sender == null || val == null) { if(log.isWarnEnabled()) { log.warn("sender or value is null"); } continue; } long highest_delivered_seqno=val.getHighestDeliveredSeqno(); long low_seqno=val.getLow(); // changed Feb 2008 (bela): http://jira.jboss.com/jira/browse/JGRP-699: we replace all existing entries // except for myself NakReceiverWindow win=xmit_table.get(sender); if(win != null) { if(local_addr != null && local_addr.equals(sender)) { continue; } else { win.reset(); // stops retransmission xmit_table.remove(sender); } } win=createNakReceiverWindow(sender, highest_delivered_seqno, low_seqno); xmit_table.put(sender, win); } sb.append("\n").append("resulting digest: " + getDigest()); merge_history.add(sb.toString()); if(log.isDebugEnabled()) log.debug(sb); if(!xmit_table.containsKey(local_addr)) { if(log.isWarnEnabled()) { log.warn("digest does not contain local address (local_addr=" + local_addr + ", digest=" + digest); } } } private NakReceiverWindow createNakReceiverWindow(Address sender, long initial_seqno, long lowest_seqno) { NakReceiverWindow win=new NakReceiverWindow(local_addr, sender, this, initial_seqno, lowest_seqno, timer); if(use_stats_for_retransmission) { win.setRetransmitTimeouts(new ActualInterval(sender)); } else if(exponential_backoff > 0) { win.setRetransmitTimeouts(new ExponentialInterval(exponential_backoff)); } else { win.setRetransmitTimeouts(new StaticInterval(retransmit_timeouts)); } win.setDiscardDeliveredMessages(discard_delivered_msgs); win.setMaxXmitBufSize(this.max_xmit_buf_size); if(stats) win.setListener(this); return win; } private void dumpXmitStats(String filename) throws IOException { Writer out=new FileWriter(filename); try { TreeMap map=new TreeMap(xmit_time_stats); StringBuilder sb; XmitTimeStat stat; out.write("time (secs) gaps-detected xmit-reqs-sent xmit-reqs-received xmit-rsps-sent xmit-rsps-received missing-msgs-received\n\n"); for(Map.Entry entry: map.entrySet()) { sb=new StringBuilder(); stat=entry.getValue(); sb.append(entry.getKey()).append(" "); sb.append(stat.gaps_detected).append(" "); sb.append(stat.xmit_reqs_sent).append(" "); sb.append(stat.xmit_reqs_received).append(" "); sb.append(stat.xmit_rsps_sent).append(" "); sb.append(stat.xmit_rsps_received).append(" "); sb.append(stat.missing_msgs_received).append("\n"); out.write(sb.toString()); } } finally { out.close(); } } private Digest.Entry getEntry(Address sender) { if(sender == null) { if(log.isErrorEnabled()) { log.error("sender is null"); } return null; } NakReceiverWindow win=xmit_table.get(sender); if(win == null) { if(log.isErrorEnabled()) { log.error("sender " + sender + " not found in xmit_table"); } return null; } long low=win.getLowestSeen(), highest_delivered=win.getHighestDelivered(), highest_received=win.getHighestReceived(); return new Digest.Entry(low, highest_delivered, highest_received); } /** * Garbage collect messages that have been seen by all members. Update sent_msgs: for the sender P in the digest * which is equal to the local address, garbage collect all messages <= seqno at digest[P]. Update xmit_table: * for each sender P in the digest and its highest seqno seen SEQ, garbage collect all delivered_msgs in the * NakReceiverWindow corresponding to P which are <= seqno at digest[P]. */ private void stable(Digest digest) { NakReceiverWindow recv_win; long my_highest_rcvd; // highest seqno received in my digest for a sender P long stability_highest_rcvd; // highest seqno received in the stability vector for a sender P if(members == null || local_addr == null || digest == null) { if(log.isWarnEnabled()) log.warn("members, local_addr or digest are null !"); return; } if(log.isTraceEnabled()) { log.trace("received stable digest " + digest); } stability_msgs.add(digest); Address sender; Digest.Entry val; long high_seqno_delivered, high_seqno_received; for(Map.Entry entry: digest.getSenders().entrySet()) { sender=entry.getKey(); if(sender == null) continue; val=entry.getValue(); high_seqno_delivered=val.getHighestDeliveredSeqno(); high_seqno_received=val.getHighestReceivedSeqno(); // check whether the last seqno received for a sender P in the stability vector is > last seqno // received for P in my digest. if yes, request retransmission (see "Last Message Dropped" topic // in DESIGN) recv_win=xmit_table.get(sender); if(recv_win != null) { my_highest_rcvd=recv_win.getHighestReceived(); stability_highest_rcvd=high_seqno_received; if(stability_highest_rcvd >= 0 && stability_highest_rcvd > my_highest_rcvd) { if(log.isTraceEnabled()) { log.trace("my_highest_rcvd (" + my_highest_rcvd + ") < stability_highest_rcvd (" + stability_highest_rcvd + "): requesting retransmission of " + sender + '#' + stability_highest_rcvd); } retransmit(stability_highest_rcvd, stability_highest_rcvd, sender); } } high_seqno_delivered-=gc_lag; if(high_seqno_delivered < 0) { continue; } if(log.isTraceEnabled()) log.trace("deleting msgs <= " + high_seqno_delivered + " from " + sender); // delete *delivered* msgs that are stable if(recv_win != null) { recv_win.stable(high_seqno_delivered); // delete all messages with seqnos <= seqno } } } /* ---------------------- Interface Retransmitter.RetransmitCommand ---------------------- */ /** * Implementation of Retransmitter.RetransmitCommand. Called by retransmission thread when gap is detected. */ public void retransmit(long first_seqno, long last_seqno, Address sender) { retransmit(first_seqno, last_seqno, sender, false); } protected void retransmit(long first_seqno, long last_seqno, Address sender, boolean multicast_xmit_request) { NakAckHeader hdr; Message retransmit_msg; Address dest=sender; // to whom do we send the XMIT request ? if(multicast_xmit_request || this.use_mcast_xmit_req) { dest=null; } else { if(xmit_from_random_member && !local_addr.equals(sender)) { Address random_member=(Address)Util.pickRandomElement(members); if(random_member != null && !local_addr.equals(random_member)) { dest=random_member; if(log.isTraceEnabled()) log.trace("picked random member " + dest + " to send XMIT request to"); } } } hdr=new NakAckHeader(NakAckHeader.XMIT_REQ, first_seqno, last_seqno, sender); retransmit_msg=new Message(dest, null, null); retransmit_msg.setFlag(Message.OOB); if(log.isTraceEnabled()) log.trace(local_addr + ": sending XMIT_REQ ([" + first_seqno + ", " + last_seqno + "]) to " + dest); retransmit_msg.putHeader(name, hdr); ConcurrentMap tmp=xmit_stats.get(sender); if(tmp == null) { tmp=new ConcurrentHashMap(); ConcurrentMap tmp2=xmit_stats.putIfAbsent(sender, tmp); if(tmp2 != null) tmp=tmp2; } for(long seq=first_seqno; seq < last_seqno; seq++) { tmp.putIfAbsent(seq, System.currentTimeMillis()); } if(xmit_time_stats != null) { long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; XmitTimeStat stat=xmit_time_stats.get(key); if(stat == null) { stat=new XmitTimeStat(); XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); if(stat2 != null) stat=stat2; } stat.xmit_reqs_sent.addAndGet((int)(last_seqno - first_seqno +1)); } down_prot.down(new Event(Event.MSG, retransmit_msg)); if(stats) { xmit_reqs_sent+=last_seqno - first_seqno +1; updateStats(sent, dest, 1, 0, 0); XmitRequest req=new XmitRequest(sender, first_seqno, last_seqno, dest); send_history.add(req); } } /* ------------------- End of Interface Retransmitter.RetransmitCommand -------------------- */ /* ----------------------- Interface NakReceiverWindow.Listener ---------------------- */ public void missingMessageReceived(long seqno, Address original_sender) { ConcurrentMap tmp=xmit_stats.get(original_sender); if(tmp != null) { Long timestamp=tmp.remove(seqno); if(timestamp != null) { long diff=System.currentTimeMillis() - timestamp; BoundedList list=xmit_times_history.get(original_sender); if(list == null) { list=new BoundedList(xmit_history_max_size); BoundedList list2=xmit_times_history.putIfAbsent(original_sender, list); if(list2 != null) list=list2; } list.add(diff); // compute the smoothed average for retransmission times for original_sender synchronized(smoothed_avg_xmit_times) { Double smoothed_avg=smoothed_avg_xmit_times.get(original_sender); if(smoothed_avg == null) smoothed_avg=INITIAL_SMOOTHED_AVG; // the smoothed avg takes 90% of the previous value, 100% of the new value and averages them // then, we add 10% to be on the safe side (an xmit value should rather err on the higher than lower side) smoothed_avg=((smoothed_avg * WEIGHT) + diff) / 2; smoothed_avg=smoothed_avg * (2 - WEIGHT); smoothed_avg_xmit_times.put(original_sender, smoothed_avg); } } } if(xmit_time_stats != null) { long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; XmitTimeStat stat=xmit_time_stats.get(key); if(stat == null) { stat=new XmitTimeStat(); XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); if(stat2 != null) stat=stat2; } stat.missing_msgs_received.incrementAndGet(); } if(stats) { missing_msgs_received++; updateStats(received, original_sender, 0, 0, 1); MissingMessage missing=new MissingMessage(original_sender, seqno); receive_history.add(missing); } } /** Called when a message gap is detected */ public void messageGapDetected(long from, long to, Address src) { if(xmit_time_stats != null) { long key=(System.currentTimeMillis() - xmit_time_stats_start) / 1000; XmitTimeStat stat=xmit_time_stats.get(key); if(stat == null) { stat=new XmitTimeStat(); XmitTimeStat stat2=xmit_time_stats.putIfAbsent(key, stat); if(stat2 != null) stat=stat2; } stat.gaps_detected.addAndGet((int)(to - from +1)); } } /* ------------------- End of Interface NakReceiverWindow.Listener ------------------- */ private void clear() { // changed April 21 2004 (bela): SourceForge bug# 938584. We cannot delete our own messages sent between // a join() and a getState(). Otherwise retransmission requests from members who missed those msgs might // fail. Not to worry though: those msgs will be cleared by STABLE (message garbage collection) // sent_msgs.clear(); for(NakReceiverWindow win: xmit_table.values()) { win.reset(); } xmit_table.clear(); undelivered_msgs.set(0); } private void reset() { seqno_lock.lock(); try { seqno=0; } finally { seqno_lock.unlock(); } for(NakReceiverWindow win: xmit_table.values()) { win.reset(); } xmit_table.clear(); undelivered_msgs.set(0); } public String printMessages() { StringBuilder ret=new StringBuilder(); Map.Entry entry; Address addr; Object w; for(Iterator> it=xmit_table.entrySet().iterator(); it.hasNext();) { entry=it.next(); addr=entry.getKey(); w=entry.getValue(); ret.append(addr).append(": ").append(w.toString()).append('\n'); } return ret.toString(); } public String printRetransmissionAvgs() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: xmit_times_history.entrySet()) { Address sender=entry.getKey(); BoundedList list=entry.getValue(); long tmp=0; int i=0; for(Long val: list) { tmp+=val; i++; } double avg=i > 0? tmp / i: -1; sb.append(sender).append(": ").append(avg).append("\n"); } return sb.toString(); } public String printSmoothedRetransmissionAvgs() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: smoothed_avg_xmit_times.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } public String printRetransmissionTimes() { StringBuilder sb=new StringBuilder(); for(Map.Entry> entry: xmit_times_history.entrySet()) { Address sender=entry.getKey(); BoundedList list=entry.getValue(); sb.append(sender).append(": ").append(list).append("\n"); } return sb.toString(); } public double getTotalAverageRetransmissionTime() { long total=0; int i=0; for(BoundedList list: xmit_times_history.values()) { for(Long val: list) { total+=val; i++; } } return i > 0? total / i: -1; } public double getTotalAverageSmoothedRetransmissionTime() { double total=0.0; int cnt=0; synchronized(smoothed_avg_xmit_times) { for(Double val: smoothed_avg_xmit_times.values()) { if(val != null) { total+=val; cnt++; } } } return cnt > 0? total / cnt : -1; } /** Returns the smoothed average retransmission time for a given sender */ public double getSmoothedAverageRetransmissionTime(Address sender) { synchronized(smoothed_avg_xmit_times) { Double retval=smoothed_avg_xmit_times.get(sender); if(retval == null) { retval=INITIAL_SMOOTHED_AVG; smoothed_avg_xmit_times.put(sender, retval); } return retval; } } // public static final class LossRate { // private final Set received=new HashSet(); // private final Set missing=new HashSet(); // private double smoothed_loss_rate=0.0; // // public synchronized void addReceived(long seqno) { // received.add(seqno); // missing.remove(seqno); // setSmoothedLossRate(); // } // // public synchronized void addReceived(Long ... seqnos) { // for(int i=0; i < seqnos.length; i++) { // Long seqno=seqnos[i]; // received.add(seqno); // missing.remove(seqno); // } // setSmoothedLossRate(); // } // // public synchronized void addMissing(long from, long to) { // for(long i=from; i <= to; i++) { // if(!received.contains(i)) // missing.add(i); // } // setSmoothedLossRate(); // } // // public synchronized double computeLossRate() { // int num_missing=missing.size(); // if(num_missing == 0) // return 0.0; // int num_received=received.size(); // int total=num_missing + num_received; // return num_missing / (double)total; // } // // public synchronized double getSmoothedLossRate() { // return smoothed_loss_rate; // } // // public synchronized String toString() { // StringBuilder sb=new StringBuilder(); // int num_missing=missing.size(); // int num_received=received.size(); // int total=num_missing + num_received; // sb.append("total=").append(total).append(" (received=").append(received.size()).append(", missing=") // .append(missing.size()).append(", loss rate=").append(computeLossRate()) // .append(", smoothed loss rate=").append(smoothed_loss_rate).append(")"); // return sb.toString(); // } // // /** Set the new smoothed_loss_rate value to 70% of the new value and 30% of the old value */ // private void setSmoothedLossRate() { // double new_loss_rate=computeLossRate(); // if(smoothed_loss_rate == 0) { // smoothed_loss_rate=new_loss_rate; // } // else { // smoothed_loss_rate=smoothed_loss_rate * .3 + new_loss_rate * .7; // } // } // } private static class XmitTimeStat { final AtomicInteger gaps_detected=new AtomicInteger(0); final AtomicInteger xmit_reqs_sent=new AtomicInteger(0); final AtomicInteger xmit_reqs_received=new AtomicInteger(0); final AtomicInteger xmit_rsps_sent=new AtomicInteger(0); final AtomicInteger xmit_rsps_received=new AtomicInteger(0); final AtomicInteger missing_msgs_received=new AtomicInteger(0); } private class ActualInterval implements Interval { private final Address sender; public ActualInterval(Address sender) { this.sender=sender; } public long next() { return (long)getSmoothedAverageRetransmissionTime(sender); } public Interval copy() { return this; } } static class StatsEntry { long xmit_reqs, xmit_rsps, missing_msgs_rcvd; public String toString() { StringBuilder sb=new StringBuilder(); sb.append(xmit_reqs).append(" xmit_reqs").append(", ").append(xmit_rsps).append(" xmit_rsps"); sb.append(", ").append(missing_msgs_rcvd).append(" missing msgs"); return sb.toString(); } } static class XmitRequest { Address original_sender; // original sender of message long low, high, timestamp=System.currentTimeMillis(); Address xmit_dest; // destination to which XMIT_REQ is sent, usually the original sender XmitRequest(Address original_sender, long low, long high, Address xmit_dest) { this.original_sender=original_sender; this.xmit_dest=xmit_dest; this.low=low; this.high=high; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(new Date(timestamp)).append(": ").append(original_sender).append(" #[").append(low); sb.append("-").append(high).append("]"); sb.append(" (XMIT_REQ sent to ").append(xmit_dest).append(")"); return sb.toString(); } } static class MissingMessage { Address original_sender; long seq, timestamp=System.currentTimeMillis(); MissingMessage(Address original_sender, long seqno) { this.original_sender=original_sender; this.seq=seqno; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(new Date(timestamp)).append(": ").append(original_sender).append(" #").append(seq); return sb.toString(); } } /* ----------------------------- End of Private Methods ------------------------------------ */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/MERGE3.java0000644000175000017500000002070411366547366025221 0ustar twernertwerner// $Id: MERGE3.java,v 1.17.4.1 2008/05/22 13:23:07 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.*; import java.util.concurrent.Future; /** * Protocol to discover subgroups; e.g., existing due to a network partition (that healed). Example: group * {p,q,r,s,t,u,v,w} is split into 3 subgroups {p,q}, {r,s,t,u} and {v,w}. This protocol will eventually send * a MERGE event with the coordinators of each subgroup up the stack: {p,r,v}. Note that - depending on the time * of subgroup discovery - there could also be 2 MERGE events, which first join 2 of the subgroups, and then the * resulting group to the last subgroup. The real work of merging the subgroups into one larger group is done * somewhere above this protocol (typically in the GMS protocol).

* This protocol works as follows: *

    *
  • If coordinator: periodically broadcast a "I'm the coordinator" message. If a coordinator receives such * a message, it immediately initiates a merge by sending up a MERGE event *

    * * Provides: sends MERGE event with list of coordinators up the stack
    * @author Bela Ban, Oct 16 2001 */ public class MERGE3 extends Protocol { Address local_addr=null; long min_interval=5000; // minimum time between executions of the FindSubgroups task long max_interval=20000; // maximum time between executions of the FindSubgroups task boolean is_coord=false; final Vector mbrs=new Vector(); TimeScheduler timer=null; Future announcer_task_future=null; CoordinatorAnnouncer announcer_task=null; final Set announcements=Collections.synchronizedSet(new HashSet()); /** Use a new thread to send the MERGE event up the stack */ boolean use_separate_thread=false; public String getName() { return "MERGE3"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("min_interval"); if(str != null) { min_interval=Long.parseLong(str); props.remove("min_interval"); } str=props.getProperty("max_interval"); if(str != null) { max_interval=Long.parseLong(str); props.remove("max_interval"); } if(min_interval <= 0 || max_interval <= 0) { if(log.isErrorEnabled()) log.error("min_interval and max_interval have to be > 0"); return false; } if(max_interval <= min_interval) { if(log.isErrorEnabled()) log.error("max_interval has to be greater than min_interval"); return false; } str=props.getProperty("use_separate_thread"); if(str != null) { use_separate_thread=Boolean.valueOf(str).booleanValue(); props.remove("use_separate_thread"); } if(!props.isEmpty()) { log.error("MERGE2.setProperties(): the following properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { timer=getTransport().getTimer(); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); CoordAnnouncement hdr=(CoordAnnouncement)msg.getHeader(getName()); if(hdr != null) { if(hdr.coord_addr != null && is_coord) { boolean contains; contains=announcements.contains(hdr.coord_addr); announcements.add(hdr.coord_addr); if(log.isDebugEnabled()) { if(contains) log.debug("discarded duplicate announcement: " + hdr.coord_addr + ", announcements=" + announcements); else log.debug("received announcement: " + hdr.coord_addr + ", announcements=" + announcements); } if(announcements.size() > 1 && is_coord) { processAnnouncements(); } } return null; } else return up_prot.up(evt); case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return up_prot.up(evt); } public Object down(Event evt) { Vector tmp; Address coord; switch(evt.getType()) { case Event.VIEW_CHANGE: down_prot.down(evt); tmp=((View)evt.getArg()).getMembers(); mbrs.clear(); mbrs.addAll(tmp); coord=(Address)mbrs.elementAt(0); if(coord.equals(local_addr)) { if(is_coord == false) { is_coord=true; startCoordAnnouncerTask(); } } else { if(is_coord == true) { is_coord=false; stopCoordAnnouncerTask(); } } break; } return down_prot.down(evt); } void startCoordAnnouncerTask() { if(announcer_task_future == null || announcer_task_future.isDone()) { announcements.add(local_addr); announcer_task=new CoordinatorAnnouncer(); announcer_task_future=timer.scheduleWithDynamicInterval(announcer_task); if(log.isDebugEnabled()) log.debug("coordinator announcement task started, announcements=" + announcements); } } void stopCoordAnnouncerTask() { if(announcer_task_future != null) { announcer_task_future.cancel(false); announcer_task_future=null; } announcer_task=null; announcements.clear(); if(log.isDebugEnabled()) log.debug("coordinator announcement task stopped"); } /** * Returns a random value within [min_interval - max_interval] */ long computeInterval() { return min_interval + Util.random(max_interval - min_interval); } void sendCoordinatorAnnouncement(Address coord) { Message coord_announcement=new Message(); // multicast to all CoordAnnouncement hdr=new CoordAnnouncement(coord); coord_announcement.putHeader(getName(), hdr); down_prot.down(new Event(Event.MSG, coord_announcement)); } void processAnnouncements() { if(announcements.size() > 1) { Vector coords=new Vector(announcements); // create a clone if(coords.size() > 1) { if(log.isDebugEnabled()) log.debug("passing up MERGE event, coords=" + coords); final Event evt=new Event(Event.MERGE, coords); if(use_separate_thread) { Thread merge_notifier=new Thread(Util.getGlobalThreadGroup(), "merge notifier thread") { public void run() { up_prot.up(evt); } }; merge_notifier.setDaemon(true); merge_notifier.start(); } else { up_prot.up(evt); } } announcements.clear(); announcements.add(local_addr); } } class CoordinatorAnnouncer implements TimeScheduler.Task { public long nextInterval() { return computeInterval(); } public void run() { if(is_coord) sendCoordinatorAnnouncement(local_addr); } } public static class CoordAnnouncement extends Header { Address coord_addr=null; public CoordAnnouncement() { } public CoordAnnouncement(Address coord) { this.coord_addr=coord; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { coord_addr=(Address)in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(coord_addr); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/DUMMY_TP.java0000644000175000017500000000330311366547366025571 0ustar twernertwerner// $Id: DUMMY_TP.java,v 1.7 2007/01/15 15:59:26 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.stack.Protocol; /** * Dummy transport, returns a fake local address and responds to CONNECT. * Compared to LOOPBACK, this discards everything * @author Bela Ban * @version $Id: DUMMY_TP.java,v 1.7 2007/01/15 15:59:26 belaban Exp $ */ public class DUMMY_TP extends Protocol { private Address local_addr=null; public DUMMY_TP() { } public String toString() { return "Protocol DUMMY_TP (local address: " + local_addr + ')'; } /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return "DUMMY_TP"; } public void init() throws Exception { local_addr=new org.jgroups.stack.IpAddress("localhost", 10000); // fake address } public void start() throws Exception { up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling Down). */ public Object down(Event evt) { switch(evt.getType()) { case Event.CONNECT: return null; case Event.DISCONNECT: return null; } return down_prot.down(evt); } /*--------------------------- End of Protocol interface -------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/MERGE2.java0000644000175000017500000002245011366547366025220 0ustar twernertwerner// $Id: MERGE2.java,v 1.42.2.7 2009/08/17 06:49:11 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.View; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.util.List; import java.util.Properties; import java.util.Vector; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * Protocol to discover subgroups; e.g., existing due to a network partition (that healed). Example: group * {p,q,r,s,t,u,v,w} is split into 3 subgroups {p,q}, {r,s,t,u} and {v,w}. This protocol will eventually send * a MERGE event with the coordinators of each subgroup up the stack: {p,r,v}. Note that - depending on the time * of subgroup discovery - there could also be 2 MERGE events, which first join 2 of the subgroups, and then the * resulting group to the last subgroup. The real work of merging the subgroups into one larger group is done * somewhere above this protocol (typically in the GMS protocol).

    * This protocol works as follows: *

      *
    • If coordinator: periodically retrieve the initial membership (using the FIND_INITIAL_MBRS event provided e.g. * by PING or TCPPING protocols. This list contains {coord,addr} pairs. *
    • If there is more than 1 coordinator: *
        *
      1. Get all coordinators *
      2. Create a MERGE event with the list of coordinators as argument *
      3. Send the event up the stack *
      *
    * *

    * * Requires: FIND_INITIAL_MBRS event from below
    * Provides: sends MERGE event with list of coordinators up the stack
    * @author Bela Ban, Oct 16 2001 */ public class MERGE2 extends Protocol { private Address local_addr=null; private final FindSubgroupsTask task=new FindSubgroupsTask(); // task periodically executing as long as we are coordinator private long min_interval=5000; // minimum time between executions of the FindSubgroups task private long max_interval=20000; // maximum time between executions of the FindSubgroups task private volatile boolean is_coord=false; private volatile boolean use_separate_thread=false; // Use a new thread to send the MERGE event up the stack private TimeScheduler timer; public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); } public String getName() { return "MERGE2"; } public long getMinInterval() { return min_interval; } public void setMinInterval(long i) { min_interval=i; } public long getMaxInterval() { return max_interval; } public void setMaxInterval(long l) { max_interval=l; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("min_interval"); if(str != null) { min_interval=Long.parseLong(str); props.remove("min_interval"); } str=props.getProperty("max_interval"); if(str != null) { max_interval=Long.parseLong(str); props.remove("max_interval"); } if(min_interval <= 0 || max_interval <= 0) { if(log.isErrorEnabled()) log.error("min_interval and max_interval have to be > 0"); return false; } if(max_interval <= min_interval) { if(log.isErrorEnabled()) log.error("max_interval has to be greater than min_interval"); return false; } str=props.getProperty("use_separate_thread"); if(str != null) { use_separate_thread=Boolean.valueOf(str).booleanValue(); props.remove("use_separate_thread"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public Vector requiredDownServices() { Vector retval=new Vector(1); retval.addElement(new Integer(Event.FIND_INITIAL_MBRS)); return retval; } public void stop() { is_coord=false; task.stop(); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); return up_prot.up(evt); default: return up_prot.up(evt); // Pass up to the layer above us } } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: Object ret=down_prot.down(evt); Vector

    mbrs=((View)evt.getArg()).getMembers(); if(mbrs == null || mbrs.isEmpty() || local_addr == null) { task.stop(); return ret; } Address coord=mbrs.elementAt(0); if(coord.equals(local_addr)) { is_coord=true; task.start(); // start task if we became coordinator (doesn't start if already running) } else { // if we were coordinator, but are no longer, stop task. this happens e.g. when we merge and someone // else becomes the new coordinator of the merged group if(is_coord) { is_coord=false; } task.stop(); } return ret; default: return down_prot.down(evt); // Pass on to the layer below us } } /** * Task periodically executing (if role is coordinator). Gets the initial membership and determines * whether there are subgroups (multiple coordinators for the same group). If yes, it sends a MERGE event * with the list of the coordinators up the stack */ private class FindSubgroupsTask { @GuardedBy("this") private Future future; public synchronized void start() { if(future == null || future.isDone()) { future=timer.scheduleWithFixedDelay(new Runnable() { public void run() { findAndNotify(); } }, Math.max(computeInterval(), 5000L), computeInterval(), TimeUnit.MILLISECONDS); } } public synchronized void stop() { if(future != null) { future.cancel(true); future=null; } } public void findAndNotify() { List initial_mbrs=findInitialMembers(); Vector
    coords=detectMultipleCoordinators(initial_mbrs); if(coords.size() > 1) { if(log.isDebugEnabled()) log.debug(local_addr + " found multiple coordinators: " + coords + "; sending up MERGE event"); final Event evt=new Event(Event.MERGE, coords); if(use_separate_thread) { Thread merge_notifier=new Thread() { public void run() { up_prot.up(evt); } }; merge_notifier.setDaemon(true); merge_notifier.setName("merge notifier thread"); merge_notifier.start(); } else { up_prot.up(evt); } } if(log.isTraceEnabled()) log.trace("MERGE2.FindSubgroups thread terminated (local_addr=" + local_addr + ")"); } /** * Returns a random value within [min_interval - max_interval] */ long computeInterval() { return min_interval + Util.random(max_interval - min_interval); } /** * Returns a list of PingRsp pairs. */ List findInitialMembers() { PingRsp tmp=new PingRsp(local_addr, local_addr, true); List retval=(List)down_prot.down(new Event(Event.FIND_INITIAL_MBRS)); if(retval != null && is_coord && local_addr != null && !retval.contains(tmp)) retval.add(tmp); return retval; } /** * Finds out if there is more than 1 coordinator in the initial_mbrs vector (contains PingRsp elements). * @param initial_mbrs A list of PingRsp pairs * @return Vector A list of the coordinators (Addresses) found. Will contain just 1 element for a correct * membership, and more than 1 for multiple coordinators */ Vector
    detectMultipleCoordinators(List initial_mbrs) { Vector
    ret=new Vector
    (11); if(initial_mbrs != null) { for(PingRsp response:initial_mbrs) { if(response.isServer()) { Address coord=response.getCoordAddress(); if(!ret.contains(coord)) ret.add(coord); } } } return ret; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/0000755000175000017500000000000011621261110025213 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/UDP.java.txt0000644000175000017500000021245511366547366027367 0ustar twernertwerner// $Id: UDP.java.txt,v 1.1.14.1 2008/01/22 10:01:13 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.List; import org.jgroups.util.*; import org.jgroups.util.Queue; import java.io.*; import java.net.*; import java.util.*; /** * IP multicast transport based on UDP. Messages to the group (msg.dest == null) will * be multicast (to all group members), whereas point-to-point messages * (msg.dest != null) will be unicast to a single member. Uses a multicast and * a unicast socket.

    * The following properties are being read by the UDP protocol

    * param mcast_addr - the multicast address to use default is 228.8.8.8
    * param mcast_port - (int) the port that the multicast is sent on default is 7600
    * param ip_mcast - (boolean) flag whether to use IP multicast - default is true
    * param ip_ttl - Set the default time-to-live for multicast packets sent out on this * socket. default is 32
    * param use_packet_handler - If set, the mcast and ucast receiver threads just put * the datagram's payload (a byte buffer) into a queue, from where a separate thread * will dequeue and handle them (unmarshal and pass up). This frees the receiver * threads from having to do message unmarshalling; this time can now be spent * receiving packets. If you have lots of retransmissions because of network * input buffer overflow, consider setting this property to true (default is false). * @author Bela Ban */ public class UDP extends Protocol implements Runnable { /** Socket used for *

      *
    1. sending unicast packets and *
    2. receiving unicast packets *
    * The address of this socket will be our local address (local_addr) */ DatagramSocket sock=null; /** * BoundedList of the last 100 ports used. This is to avoid reusing a port for DatagramSocket */ private static BoundedList last_ports_used=null; /** Maintain a list of local ports opened by DatagramSocket. If this is 0, this option is turned off. * If bind_port is null, then this options will be ignored */ int num_last_ports=100; /** IP multicast socket for receiving multicast packets */ MulticastSocket mcast_recv_sock=null; /** IP multicast socket for sending multicast packets */ MulticastSocket mcast_send_sock=null; /** * Traffic class for sending unicast and multicast datagrams. * Valid values are (check {@link #DatagramSocket.setTrafficClass(int)} for details): *
      *
    • IPTOS_LOWCOST (0x02), decimal 2
    • *
    • IPTOS_RELIABILITY (0x04)<, decimal 4/LI> *
    • IPTOS_THROUGHPUT (0x08), decimal 8
    • *
    • IPTOS_LOWDELAY (0x10), decimal 16
    • *
    */ int tos=0; // valid values: 2, 4, 8, 16 /** The address (host and port) of this member */ IpAddress local_addr=null; /** The name of the group to which this member is connected */ String channel_name=null; UdpHeader udp_hdr=null; /** The multicast address (mcast address and port) this member uses */ IpAddress mcast_addr=null; /** The interface (NIC) to which the unicast and multicast sockets bind */ InetAddress bind_addr=null; /** Bind the receiver multicast socket to all available interfaces (requires JDK 1.4) */ boolean bind_to_all_interfaces=false; /** The port to which the unicast receiver socket binds. * 0 means to bind to any (ephemeral) port */ int bind_port=0; int port_range=1; // 27-6-2003 bgooren, Only try one port by default /** The multicast address used for sending and receiving packets */ String mcast_addr_name="228.8.8.8"; /** The multicast port used for sending and receiving packets */ int mcast_port=7600; /** The multicast receiver thread */ Thread mcast_receiver=null; /** The unicast receiver thread */ UcastReceiver ucast_receiver=null; /** Whether to enable IP multicasting. If false, multiple unicast datagram * packets are sent rather than one multicast packet */ boolean ip_mcast=true; /** The time-to-live (TTL) for multicast datagram packets */ int ip_ttl=64; /** The members of this group (updated when a member joins or leaves) */ final Vector members=new Vector(11); /** Pre-allocated byte stream. Used for serializing datagram packets. Will grow as needed */ final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(1024); /** Send buffer size of the multicast datagram socket */ int mcast_send_buf_size=32000; /** Receive buffer size of the multicast datagram socket */ int mcast_recv_buf_size=64000; /** Send buffer size of the unicast datagram socket */ int ucast_send_buf_size=32000; /** Receive buffer size of the unicast datagram socket */ int ucast_recv_buf_size=64000; /** If true, messages sent to self are treated specially: unicast messages are * looped back immediately, multicast messages get a local copy first and - * when the real copy arrives - it will be discarded. Useful for Window * media (non)sense */ boolean loopback=true; /** Discard packets with a different version. Usually minor version differences are okay. Setting this property * to true means that we expect the exact same version on all incoming packets */ boolean discard_incompatible_packets=false; /** Sometimes receivers are overloaded (they have to handle de-serialization etc). * Packet handler is a separate thread taking care of de-serialization, receiver * thread(s) simply put packet in queue and return immediately. Setting this to * true adds one more thread */ boolean use_incoming_packet_handler=false; /** Used by packet handler to store incoming DatagramPackets */ Queue incoming_queue=null; /** Dequeues DatagramPackets from packet_queue, unmarshalls them and * calls handleIncomingUdpPacket() */ IncomingPacketHandler incoming_packet_handler=null; /** Packets to be sent are stored in outgoing_queue and sent by a separate thread. Enabling this * value uses an additional thread */ boolean use_outgoing_packet_handler=false; /** Used by packet handler to store outgoing DatagramPackets */ Queue outgoing_queue=null; OutgoingPacketHandler outgoing_packet_handler=null; /** If set it will be added to local_addr. Used to implement * for example transport independent addresses */ byte[] additional_data=null; /** Maximum number of bytes for messages to be queued until they are sent. This value needs to be smaller than the largest UDP datagram packet size */ int max_bundle_size=AUTOCONF.senseMaxFragSizeStatic(); /** Max number of milliseconds until queued messages are sent. Messages are sent when max_bundle_size or * max_bundle_timeout has been exceeded (whichever occurs faster) */ long max_bundle_timeout=20; /** Enabled bundling of smaller messages into bigger ones */ boolean enable_bundling=false; /** Used by BundlingOutgoingPacketHandler */ TimeScheduler timer=null; /** HashMap. Keys=senders, values=destinations. For each incoming message M with sender S, adds * an entry with key=S and value= sender's IP address and port. */ HashMap addr_translation_table=new HashMap(); boolean use_addr_translation=false; /** The name of this protocol */ static final String name="UDP"; static final String IGNORE_BIND_ADDRESS_PROPERTY="ignore.bind.address"; /** Usually, src addresses are nulled, and the receiver simply sets them to the address of the sender. However, * for multiple addresses on a Windows loopback device, this doesn't work * (see http://jira.jboss.com/jira/browse/JGRP-79 and the JGroups wiki for details). This must be the same * value for all members of the same group. Default is true, for performance reasons */ boolean null_src_addresses=true; long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; /** * public constructor. creates the UDP protocol, and initializes the * state variables, does however not start any sockets or threads */ public UDP() { ; } /** * debug only */ public String toString() { return "UDP(local address: " + local_addr + ')'; } public void resetStats() { num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=0; } private BoundedList getLastPortsUsed() { if(last_ports_used == null) last_ports_used=new BoundedList(num_last_ports); return last_ports_used; } public long getNumMessagesSent() {return num_msgs_sent;} public long getNumMessagesReceived() {return num_msgs_received;} public long getNumBytesSent() {return num_bytes_sent;} public long getNumBytesReceived() {return num_bytes_received;} public String getBindAddress() {return bind_addr != null? bind_addr.toString() : "null";} public void setBindAddress(String bind_addr) throws UnknownHostException { this.bind_addr=InetAddress.getByName(bind_addr); } public boolean getBindToAllInterfaces() {return bind_to_all_interfaces;} public void setBindToAllInterfaces(boolean flag) {this.bind_to_all_interfaces=flag;} public boolean isDiscardIncompatiblePackets() {return discard_incompatible_packets;} public void setDiscardIncompatiblePackets(boolean flag) {discard_incompatible_packets=flag;} public boolean isEnableBundling() {return enable_bundling;} public void setEnableBundling(boolean flag) {enable_bundling=flag;} public int getMaxBundleSize() {return max_bundle_size;} public void setMaxBundleSize(int size) {max_bundle_size=size;} public long getMaxBundleTimeout() {return max_bundle_timeout;} public void setMaxBundleTimeout(long timeout) {max_bundle_timeout=timeout;} /* ----------------------- Receiving of MCAST UDP packets ------------------------ */ public void run() { DatagramPacket packet; byte receive_buf[]=new byte[65535]; int len, sender_port; byte[] tmp, data; InetAddress sender_addr; // moved out of loop to avoid excessive object creations (bela March 8 2001) packet=new DatagramPacket(receive_buf, receive_buf.length); while(mcast_receiver != null && mcast_recv_sock != null) { try { packet.setData(receive_buf, 0, receive_buf.length); mcast_recv_sock.receive(packet); sender_addr=packet.getAddress(); sender_port=packet.getPort(); len=packet.getLength(); data=packet.getData(); if(len == 4) { // received a diagnostics probe if(data[0] == 'd' && data[1] == 'i' && data[2] == 'a' && data[3] == 'g') { handleDiagnosticProbe(sender_addr, sender_port); continue; } } if(log.isTraceEnabled()){ StringBuilder sb=new StringBuilder("received (mcast) "); sb.append(len).append(" bytes from ").append(sender_addr).append(':'); sb.append(sender_port).append(" (size=").append(len).append(" bytes)"); log.trace(sb.toString()); } if(len > receive_buf.length) { if(log.isErrorEnabled()) log.error("size of the received packet (" + len + ") is bigger than " + "allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); } if(use_incoming_packet_handler) { tmp=new byte[len]; System.arraycopy(data, 0, tmp, 0, len); incoming_queue.add(new IncomingQueueEntry(mcast_addr, sender_addr, sender_port, tmp)); } else handleIncomingUdpPacket(mcast_addr, sender_addr, sender_port, data); } catch(SocketException sock_ex) { if(log.isTraceEnabled()) log.trace("multicast socket is closed, exception=" + sock_ex); break; } catch(InterruptedIOException io_ex) { // thread was interrupted ; // go back to top of loop, where we will terminate loop } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failure in multicast receive()", ex); Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) } } if(log.isDebugEnabled()) log.debug("multicast thread terminated"); } private void handleDiagnosticProbe(InetAddress sender, int port) { try { byte[] diag_rsp=getDiagResponse().getBytes(); DatagramPacket rsp=new DatagramPacket(diag_rsp, 0, diag_rsp.length, sender, port); if(log.isDebugEnabled()) log.debug("sending diag response to " + sender + ':' + port); sock.send(rsp); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender + ':' + port + ", exception=" + t); } } private String getDiagResponse() { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" (").append(channel_name).append(')'); sb.append(" [").append(mcast_addr_name).append(':').append(mcast_port).append("]\n"); sb.append("Version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); sb.append("bound to ").append(bind_addr).append(':').append(bind_port).append('\n'); sb.append("members: ").append(members).append('\n'); return sb.toString(); } /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return name; } public void init() throws Exception { if(use_incoming_packet_handler) { incoming_queue=new Queue(); incoming_packet_handler=new IncomingPacketHandler(); } if(use_outgoing_packet_handler) { outgoing_queue=new Queue(); if(enable_bundling) { timer=stack != null? stack.timer : null; if(timer == null) throw new Exception("timer could not be retrieved"); outgoing_packet_handler=new BundlingOutgoingPacketHandler(); } else outgoing_packet_handler=new OutgoingPacketHandler(); } } /** * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */ public void start() throws Exception { if(log.isDebugEnabled()) log.debug("creating sockets and starting threads"); createSockets(); passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); startThreads(); } public void stop() { if(log.isDebugEnabled()) log.debug("closing sockets and stopping threads"); stopThreads(); // will close sockets, closeSockets() is not really needed anymore, but... closeSockets(); // ... we'll leave it in there for now (doesn't do anything if already closed) } /** * Setup the Protocol instance acording to the configuration string * The following properties are being read by the UDP protocol * param mcast_addr - the multicast address to use default is 228.8.8.8 * param mcast_port - (int) the port that the multicast is sent on default is 7600 * param ip_mcast - (boolean) flag whether to use IP multicast - default is true * param ip_ttl - Set the default time-to-live for multicast packets sent out on this socket. default is 32 * @return true if no other properties are left. * false if the properties still have data in them, ie , * properties are left over and not handled by the protocol stack */ public boolean setProperties(Properties props) { String str; String tmp = null; super.setProperties(props); // PropertyPermission not granted if running in an untrusted environment with JNLP. try { tmp=System.getProperty("bind.address"); if(Boolean.getBoolean(IGNORE_BIND_ADDRESS_PROPERTY)) { tmp=null; } } catch (SecurityException ex){ } if(tmp != null) str=tmp; else str=props.getProperty("bind_addr"); if(str != null) { try { bind_addr=InetAddress.getByName(str); } catch(UnknownHostException unknown) { if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); return false; } props.remove("bind_addr"); } str=props.getProperty("bind_to_all_interfaces"); if(str != null) { bind_to_all_interfaces=new Boolean(str).booleanValue(); props.remove("bind_to_all_interfaces"); } str=props.getProperty("bind_port"); if(str != null) { bind_port=Integer.parseInt(str); props.remove("bind_port"); } str=props.getProperty("num_last_ports"); if(str != null) { num_last_ports=Integer.parseInt(str); props.remove("num_last_ports"); } str=props.getProperty("start_port"); if(str != null) { bind_port=Integer.parseInt(str); props.remove("start_port"); } str=props.getProperty("port_range"); if(str != null) { port_range=Integer.parseInt(str); props.remove("port_range"); } str=props.getProperty("mcast_addr"); if(str != null) { mcast_addr_name=str; props.remove("mcast_addr"); } str=props.getProperty("mcast_port"); if(str != null) { mcast_port=Integer.parseInt(str); props.remove("mcast_port"); } str=props.getProperty("ip_mcast"); if(str != null) { ip_mcast=Boolean.valueOf(str).booleanValue(); props.remove("ip_mcast"); } str=props.getProperty("ip_ttl"); if(str != null) { ip_ttl=Integer.parseInt(str); props.remove("ip_ttl"); } str=props.getProperty("tos"); if(str != null) { tos=Integer.parseInt(str); props.remove("tos"); } str=props.getProperty("mcast_send_buf_size"); if(str != null) { mcast_send_buf_size=Integer.parseInt(str); props.remove("mcast_send_buf_size"); } str=props.getProperty("mcast_recv_buf_size"); if(str != null) { mcast_recv_buf_size=Integer.parseInt(str); props.remove("mcast_recv_buf_size"); } str=props.getProperty("ucast_send_buf_size"); if(str != null) { ucast_send_buf_size=Integer.parseInt(str); props.remove("ucast_send_buf_size"); } str=props.getProperty("ucast_recv_buf_size"); if(str != null) { ucast_recv_buf_size=Integer.parseInt(str); props.remove("ucast_recv_buf_size"); } str=props.getProperty("loopback"); if(str != null) { loopback=Boolean.valueOf(str).booleanValue(); props.remove("loopback"); } str=props.getProperty("discard_incompatible_packets"); if(str != null) { discard_incompatible_packets=Boolean.valueOf(str).booleanValue(); props.remove("discard_incompatible_packets"); } // this is deprecated, just left for compatibility (use use_incoming_packet_handler) str=props.getProperty("use_packet_handler"); if(str != null) { use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_packet_handler"); if(log.isWarnEnabled()) log.warn("'use_packet_handler' is deprecated; use 'use_incoming_packet_handler' instead"); } str=props.getProperty("use_incoming_packet_handler"); if(str != null) { use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_incoming_packet_handler"); } str=props.getProperty("use_outgoing_packet_handler"); if(str != null) { use_outgoing_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_outgoing_packet_handler"); } str=props.getProperty("max_bundle_size"); if(str != null) { int bundle_size=Integer.parseInt(str); if(bundle_size > max_bundle_size) { if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is greater than largest UDP fragmentation size (" + max_bundle_size + ')'); return false; } if(bundle_size <= 0) { if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is <= 0"); return false; } max_bundle_size=bundle_size; props.remove("max_bundle_size"); } str=props.getProperty("max_bundle_timeout"); if(str != null) { max_bundle_timeout=Long.parseLong(str); if(max_bundle_timeout <= 0) { if(log.isErrorEnabled()) log.error("max_bundle_timeout of " + max_bundle_timeout + " is invalid"); return false; } props.remove("max_bundle_timeout"); } str=props.getProperty("enable_bundling"); if(str != null) { enable_bundling=Boolean.valueOf(str).booleanValue(); props.remove("enable_bundling"); } str=props.getProperty("use_addr_translation"); if(str != null) { use_addr_translation=Boolean.valueOf(str).booleanValue(); props.remove("use_addr_translation"); } str=props.getProperty("null_src_addresses"); if(str != null) { null_src_addresses=Boolean.valueOf(str).booleanValue(); props.remove("null_src_addresses"); } if(props.size() > 0) { log.error("UDP.setProperties(): the following properties are not recognized: " + props); return false; } if(enable_bundling) { if(use_outgoing_packet_handler == false) if(log.isWarnEnabled()) log.warn("enable_bundling is true; setting use_outgoing_packet_handler=true"); use_outgoing_packet_handler=true; } return true; } /** * DON'T REMOVE ! This prevents the up-handler thread to be created, which essentially is superfluous: * messages are received from the network rather than from a layer below. */ public void startUpHandler() { ; } /** * handle the UP event. * @param evt - the event being send from the stack */ public void up(Event evt) { switch(evt.getType()) { case Event.CONFIG: passUp(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); return; } passUp(evt); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling Down). */ public void down(Event evt) { Message msg; Object dest_addr; if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond handleDownEvent(evt); return; } msg=(Message)evt.getArg(); if(channel_name != null) { // added patch by Roland Kurmann (March 20 2003) // msg.putHeader(name, new UdpHeader(channel_name)); msg.putHeader(name, udp_hdr); } dest_addr=msg.getDest(); // Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). // This way, we still have performance numbers for UDP if(observer != null) observer.passDown(evt); if(dest_addr == null) { // 'null' means send to all group members if(ip_mcast) { if(mcast_addr == null) { if(log.isErrorEnabled()) log.error("dest address of message is null, and " + "sending to default address fails as mcast_addr is null, too !" + " Discarding message " + Util.printEvent(evt)); return; } // if we want to use IP multicast, then set the destination of the message msg.setDest(mcast_addr); } else { //sends a separate UDP message to each address sendMultipleUdpMessages(msg, members); return; } } try { sendUdpMessage(msg); } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e + ", msg=" + msg + ", mcast_addr=" + mcast_addr); } } /*--------------------------- End of Protocol interface -------------------------- */ /* ------------------------------ Private Methods -------------------------------- */ /** * If the sender is null, set our own address. We cannot just go ahead and set the address * anyway, as we might be sending a message on behalf of someone else ! E.gin case of * retransmission, when the original sender has crashed, or in a FLUSH protocol when we * have to return all unstable messages with the FLUSH_OK response. */ private void setSourceAddress(Message msg) { if(msg.getSrc() == null) msg.setSrc(local_addr); } /** * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because * mcast or unicast socket reads can be concurrent. * Correction (bela April 19 2005): we acces no instance variables, all vars are allocated on the stack, so * this method should be reentrant: removed 'synchronized' keyword */ void handleIncomingUdpPacket(IpAddress dest, InetAddress sender, int port, byte[] data) { ByteArrayInputStream inp_stream=null; DataInputStream inp=null; Message msg=null; List l; // used if bundling is enabled short version; boolean is_message_list; try { // skip the first n bytes (default: 4), this is the version info inp_stream=new ByteArrayInputStream(data); inp=new DataInputStream(inp_stream); version=inp.readShort(); if(Version.compareTo(version) == false) { if(log.isWarnEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("packet from ").append(sender).append(':').append(port); sb.append(" has different version (").append(version); sb.append(") from ours (").append(Version.printVersion()).append("). "); if(discard_incompatible_packets) sb.append("Packet is discarded"); else sb.append("This may cause problems"); log.warn(sb.toString()); } if(discard_incompatible_packets) return; } is_message_list=inp.readBoolean(); if(is_message_list) { l=bufferToList(inp, dest); for(Enumeration en=l.elements(); en.hasMoreElements();) { msg=(Message)en.nextElement(); try { handleMessage(msg); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed unmarshalling message list", t); } } } else { msg=bufferToMessage(inp, dest, sender, port); handleMessage(msg); } } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception in processing incoming packet", e); } finally { Util.closeInputStream(inp); Util.closeInputStream(inp_stream); } } void handleMessage(Message msg) { Event evt; UdpHeader hdr; Address dst=msg.getDest(); if(dst == null) dst=mcast_addr; if(stats) { num_msgs_received++; num_bytes_received+=msg.getLength(); } // discard my own multicast loopback copy if(loopback) { Address src=msg.getSrc(); if((dst == null || (dst != null && dst.isMulticastAddress())) && src != null && local_addr.equals(src)) { if(log.isTraceEnabled()) log.trace("discarded own loopback multicast packet"); return; } } evt=new Event(Event.MSG, msg); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("message is "); sb.append(msg).append(", headers are ").append(msg.getHeaders()); log.trace(sb.toString()); } /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. * This allows e.g. PerfObserver to get the time of reception of a message */ if(observer != null) observer.up(evt, up_queue.size()); hdr=(UdpHeader)msg.getHeader(name); // replaced removeHeader() with getHeader() if(hdr != null) { /* Discard all messages destined for a channel with a different name */ String ch_name=hdr.channel_name; // Discard if message's group name is not the same as our group name unless the // message is a diagnosis message (special group name DIAG_GROUP) if(ch_name != null && channel_name != null && !channel_name.equals(ch_name) && !ch_name.equals(Util.DIAG_GROUP)) { if(log.isWarnEnabled()) log.warn("discarded message from different group (" + ch_name + "). Sender was " + msg.getSrc()); return; } } else { if(log.isErrorEnabled()) log.error("message does not have a UDP header"); } passUp(evt); } void sendUdpMessage(Message msg) throws Exception { sendUdpMessage(msg, false); } /** Send a message to the address specified in dest */ void sendUdpMessage(Message msg, boolean copyForOutgoingQueue) throws Exception { IpAddress dest; Message copy; Event evt; dest=(IpAddress)msg.getDest(); // guaranteed to be non-null setSourceAddress(msg); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("sending msg to "); sb.append(msg.getDest()).append(" (src=").append(msg.getSrc()).append("), headers are ").append(msg.getHeaders()); log.trace(sb.toString()); } // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, // we will discard our own multicast message if(loopback && (dest.equals(local_addr) || dest.isMulticastAddress())) { copy=msg.copy(); // copy.removeHeader(name); // we don't remove the header copy.setSrc(local_addr); // copy.setDest(dest); evt=new Event(Event.MSG, copy); /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. This allows e.g. PerfObserver to get the time of reception of a message */ if(observer != null) observer.up(evt, up_queue.size()); if(log.isTraceEnabled()) log.trace("looped back local message " + copy); passUp(evt); if(dest != null && !dest.isMulticastAddress()) return; } if(use_outgoing_packet_handler) { if(copyForOutgoingQueue) outgoing_queue.add(msg.copy()); else outgoing_queue.add(msg); return; } send(msg); } /** Internal method to serialize and send a message. This method is not reentrant */ void send(Message msg) throws Exception { Buffer buf; IpAddress dest=(IpAddress)msg.getDest(); // guaranteed to be non-null IpAddress src=(IpAddress)msg.getSrc(); synchronized(out_stream) { buf=messageToBuffer(msg, dest, src); doSend(buf, dest.getIpAddress(), dest.getPort()); } } void doSend(Buffer buf, InetAddress dest, int port) throws IOException { DatagramPacket packet; // packet=new DatagramPacket(data, data.length, dest, port); packet=new DatagramPacket(buf.getBuf(), buf.getOffset(), buf.getLength(), dest, port); if(stats) { num_msgs_sent++; num_bytes_sent+=buf.getLength(); } if(dest.isMulticastAddress() && mcast_send_sock != null) { // mcast_recv_sock might be null if ip_mcast is false mcast_send_sock.send(packet); } else { if(sock != null) sock.send(packet); } } void sendMultipleUdpMessages(Message msg, Vector dests) { Address dest; for(int i=0; i < dests.size(); i++) { dest=(Address)dests.elementAt(i); msg.setDest(dest); try { sendUdpMessage(msg, true); // copy for outgoing queue if outgoing queue handler is enabled } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending multiple messages", e); } } } /** * This method needs to be synchronized on out_stream when it is called * @param msg * @param dest * @param src * @return * @throws IOException */ private Buffer messageToBuffer(Message msg, IpAddress dest, IpAddress src) throws Exception { Buffer retval; DataOutputStream out=null; try { out_stream.reset(); out=new DataOutputStream(out_stream); out.writeShort(Version.version); // write the version out.writeBoolean(false); // single message, *not* a list of messages nullAddresses(msg, dest, src); msg.writeTo(out); revertAddresses(msg, dest, src); out.flush(); retval=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); return retval; } finally { Util.closeOutputStream(out); } } private void nullAddresses(Message msg, IpAddress dest, IpAddress src) { msg.setDest(null); if(!dest.isMulticastAddress()) { // unicast if(src != null) { if(null_src_addresses) msg.setSrc(new IpAddress(src.getPort(), false)); // null the host part, leave the port if(src.getAdditionalData() != null) ((IpAddress)msg.getSrc()).setAdditionalData(src.getAdditionalData()); } else { msg.setSrc(null); } } else { // multicast if(src != null) { if(null_src_addresses) msg.setSrc(new IpAddress(src.getPort(), false)); // null the host part, leave the port if(src.getAdditionalData() != null) ((IpAddress)msg.getSrc()).setAdditionalData(src.getAdditionalData()); } } } private void revertAddresses(Message msg, IpAddress dest, IpAddress src) { msg.setDest(dest); msg.setSrc(src); } private Message bufferToMessage(DataInputStream instream, IpAddress dest, InetAddress sender, int port) throws Exception { Message msg=new Message(); msg.readFrom(instream); setAddresses(msg, dest, sender, port); return msg; } private void setAddresses(Message msg, IpAddress dest, InetAddress sender, int port) { // set the destination address if(msg.getDest() == null && dest != null) msg.setDest(dest); // set the source address if not set IpAddress src_addr=(IpAddress)msg.getSrc(); if(src_addr == null) { try {msg.setSrc(new IpAddress(sender, port));} catch(Throwable t) {} } else { byte[] tmp_additional_data=src_addr.getAdditionalData(); if(src_addr.getIpAddress() == null) { try {msg.setSrc(new IpAddress(sender, src_addr.getPort()));} catch(Throwable t) {} } if(tmp_additional_data != null) ((IpAddress)msg.getSrc()).setAdditionalData(tmp_additional_data); } } private Buffer listToBuffer(List l, IpAddress dest) throws Exception { Buffer retval=null; IpAddress src; Message msg; int len=l != null? l.size() : 0; boolean src_written=false; DataOutputStream out=null; out_stream.reset(); try { out=new DataOutputStream(out_stream); out.writeShort(Version.version); out.writeBoolean(true); out.writeInt(len); for(Enumeration en=l.elements(); en.hasMoreElements();) { msg=(Message)en.nextElement(); src=(IpAddress)msg.getSrc(); if(!src_written) { Util.writeAddress(src, out); src_written=true; } msg.setDest(null); msg.setSrc(null); msg.writeTo(out); revertAddresses(msg, dest, src); } out.flush(); retval=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); return retval; } finally { Util.closeOutputStream(out); } } private List bufferToList(DataInputStream instream, IpAddress dest) throws Exception { List l=new List(); DataInputStream in=null; int len; Message msg; Address src; try { len=instream.readInt(); src=Util.readAddress(instream); for(int i=0; i < len; i++) { msg=new Message(); msg.readFrom(instream); msg.setDest(dest); msg.setSrc(src); l.add(msg); } return l; } finally { Util.closeInputStream(in); } } /** * Create UDP sender and receiver sockets. Currently there are 2 sockets * (sending and receiving). This is due to Linux's non-BSD compatibility * in the JDK port (see DESIGN). */ void createSockets() throws Exception { InetAddress tmp_addr=null; // bind_addr not set, try to assign one by default. This is needed on Windows // changed by bela Feb 12 2003: by default multicast sockets will be bound to all network interfaces // CHANGED *BACK* by bela March 13 2003: binding to all interfaces did not result in a correct // local_addr. As a matter of fact, comparison between e.g. 0.0.0.0:1234 (on hostA) and // 0.0.0.0:1.2.3.4 (on hostB) would fail ! if(bind_addr == null) { InetAddress[] interfaces=InetAddress.getAllByName(InetAddress.getLocalHost().getHostAddress()); if(interfaces != null && interfaces.length > 0) bind_addr=interfaces[0]; } if(bind_addr == null) bind_addr=InetAddress.getLocalHost(); if(bind_addr != null) if(log.isInfoEnabled()) log.info("sockets will use interface " + bind_addr.getHostAddress()); // 2. Create socket for receiving unicast UDP packets. The address and port // of this socket will be our local address (local_addr) if(bind_port > 0) { sock=createDatagramSocketWithBindPort(); } else { sock=createEphemeralDatagramSocket(); } if(tos > 0) { try { sock.setTrafficClass(tos); } catch(SocketException e) { log.warn("traffic class of " + tos + " could not be set, will be ignored", e); } } if(sock == null) throw new Exception("UDP.createSocket(): sock is null"); local_addr=new IpAddress(sock.getLocalAddress(), sock.getLocalPort()); if(additional_data != null) local_addr.setAdditionalData(additional_data); // 3. Create socket for receiving IP multicast packets if(ip_mcast) { // 3a. Create mcast receiver socket mcast_recv_sock=new MulticastSocket(mcast_port); mcast_recv_sock.setTimeToLive(ip_ttl); tmp_addr=InetAddress.getByName(mcast_addr_name); mcast_addr=new IpAddress(tmp_addr, mcast_port); if(bind_to_all_interfaces) { bindToAllInterfaces(mcast_recv_sock, mcast_addr.getIpAddress()); } else { if(bind_addr != null) mcast_recv_sock.setInterface(bind_addr); mcast_recv_sock.joinGroup(tmp_addr); } // 3b. Create mcast sender socket mcast_send_sock=new MulticastSocket(); mcast_send_sock.setTimeToLive(ip_ttl); if(bind_addr != null) mcast_send_sock.setInterface(bind_addr); if(tos > 0) { try { mcast_send_sock.setTrafficClass(tos); // high throughput } catch(SocketException e) { log.warn("traffic class of " + tos + " could not be set, will be ignored", e); } } } setBufferSizes(); if(log.isInfoEnabled()) log.info("socket information:\n" + dumpSocketInfo()); } private void bindToAllInterfaces(MulticastSocket s, InetAddress mcastAddr) throws IOException { SocketAddress tmp_mcast_addr=new InetSocketAddress(mcastAddr, mcast_port); Enumeration en=NetworkInterface.getNetworkInterfaces(); while(en.hasMoreElements()) { NetworkInterface i=(NetworkInterface)en.nextElement(); for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr=(InetAddress)en2.nextElement(); // if(addr.isLoopbackAddress()) // continue; s.joinGroup(tmp_mcast_addr, i); if(log.isTraceEnabled()) log.trace("joined " + tmp_mcast_addr + " on interface " + i.getName() + " (" + addr + ")"); break; } } } /** Creates a DatagramSocket with a random port. Because in certain operating systems, ports are reused, * we keep a list of the n last used ports, and avoid port reuse */ private DatagramSocket createEphemeralDatagramSocket() throws SocketException { DatagramSocket tmp=null; int localPort=0; while(true) { tmp=new DatagramSocket(localPort, bind_addr); // first time localPort is 0 if(num_last_ports <= 0) break; localPort=tmp.getLocalPort(); if(getLastPortsUsed().contains(new Integer(localPort))) { if(log.isDebugEnabled()) log.debug("local port " + localPort + " already seen in this session; will try to get other port"); try {tmp.close();} catch(Throwable e) {} localPort++; continue; } else { getLastPortsUsed().add(new Integer(localPort)); break; } } return tmp; } /** * Creates a DatagramSocket when bind_port > 0. Attempts to allocate the socket with port == bind_port, and * increments until it finds a valid port, or until port_range has been exceeded * @return DatagramSocket The newly created socket * @throws Exception */ private DatagramSocket createDatagramSocketWithBindPort() throws Exception { DatagramSocket tmp=null; // 27-6-2003 bgooren, find available port in range (start_port, start_port+port_range) int rcv_port=bind_port, max_port=bind_port + port_range; while(rcv_port <= max_port) { try { tmp=new DatagramSocket(rcv_port, bind_addr); break; } catch(SocketException bind_ex) { // Cannot listen on this port rcv_port++; } catch(SecurityException sec_ex) { // Not allowed to listen on this port rcv_port++; } // Cannot listen at all, throw an Exception if(rcv_port >= max_port + 1) { // +1 due to the increment above throw new Exception("UDP.createSockets(): cannot list on any port in range " + bind_port + '-' + (bind_port + port_range)); } } return tmp; } private String dumpSocketInfo() throws Exception { StringBuilder sb=new StringBuilder(128); sb.append("local_addr=").append(local_addr); sb.append(", mcast_addr=").append(mcast_addr); sb.append(", bind_addr=").append(bind_addr); sb.append(", ttl=").append(ip_ttl); if(sock != null) { sb.append("\nsock: bound to "); sb.append(sock.getLocalAddress().getHostAddress()).append(':').append(sock.getLocalPort()); sb.append(", receive buffer size=").append(sock.getReceiveBufferSize()); sb.append(", send buffer size=").append(sock.getSendBufferSize()); } if(mcast_recv_sock != null) { sb.append("\nmcast_recv_sock: bound to "); sb.append(mcast_recv_sock.getInterface().getHostAddress()).append(':').append(mcast_recv_sock.getLocalPort()); sb.append(", send buffer size=").append(mcast_recv_sock.getSendBufferSize()); sb.append(", receive buffer size=").append(mcast_recv_sock.getReceiveBufferSize()); } if(mcast_send_sock != null) { sb.append("\nmcast_send_sock: bound to "); sb.append(mcast_send_sock.getInterface().getHostAddress()).append(':').append(mcast_send_sock.getLocalPort()); sb.append(", send buffer size=").append(mcast_send_sock.getSendBufferSize()); sb.append(", receive buffer size=").append(mcast_send_sock.getReceiveBufferSize()); } return sb.toString(); } void setBufferSizes() { if(sock != null) { try { sock.setSendBufferSize(ucast_send_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting ucast_send_buf_size in sock: " + ex); } try { sock.setReceiveBufferSize(ucast_recv_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting ucast_recv_buf_size in sock: " + ex); } } if(mcast_recv_sock != null) { try { mcast_recv_sock.setSendBufferSize(mcast_send_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting mcast_send_buf_size in mcast_recv_sock: " + ex); } try { mcast_recv_sock.setReceiveBufferSize(mcast_recv_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting mcast_recv_buf_size in mcast_recv_sock: " + ex); } } if(mcast_send_sock != null) { try { mcast_send_sock.setSendBufferSize(mcast_send_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting mcast_send_buf_size in mcast_send_sock: " + ex); } try { mcast_send_sock.setReceiveBufferSize(mcast_recv_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting mcast_recv_buf_size in mcast_send_sock: " + ex); } } } /** * Closed UDP unicast and multicast sockets */ void closeSockets() { // 1. Close multicast socket closeMulticastSocket(); // 2. Close socket closeSocket(); } void closeMulticastSocket() { if(mcast_recv_sock != null) { try { if(mcast_addr != null) { mcast_recv_sock.leaveGroup(mcast_addr.getIpAddress()); } mcast_recv_sock.close(); // this will cause the mcast receiver thread to break out of its loop mcast_recv_sock=null; if(log.isDebugEnabled()) log.debug("multicast receive socket closed"); } catch(IOException ex) { } mcast_addr=null; } if(mcast_send_sock != null) { mcast_send_sock.close(); // this will cause the mcast receiver thread to break out of its loop mcast_send_sock=null; if(log.isDebugEnabled()) log.debug("multicast send socket closed"); } } void closeSocket() { if(sock != null) { sock.close(); sock=null; if(log.isDebugEnabled()) log.debug("socket closed"); } } /** * Starts the unicast and multicast receiver threads */ void startThreads() throws Exception { if(ucast_receiver == null) { //start the listener thread of the ucast_recv_sock ucast_receiver=new UcastReceiver(); ucast_receiver.start(); if(log.isDebugEnabled()) log.debug("created unicast receiver thread"); } if(ip_mcast) { if(mcast_receiver != null) { if(mcast_receiver.isAlive()) { if(log.isDebugEnabled()) log.debug("did not create new multicastreceiver thread as existing " + "multicast receiver thread is still running"); } else mcast_receiver=null; // will be created just below... } if(mcast_receiver == null) { mcast_receiver=new Thread(this, "UDP mcast receiver"); mcast_receiver.setPriority(Thread.MAX_PRIORITY); // needed ???? mcast_receiver.setDaemon(true); mcast_receiver.start(); } } if(use_outgoing_packet_handler) outgoing_packet_handler.start(); if(use_incoming_packet_handler) incoming_packet_handler.start(); } /** * Stops unicast and multicast receiver threads */ void stopThreads() { Thread tmp; // 1. Stop the multicast receiver thread if(mcast_receiver != null) { if(mcast_receiver.isAlive()) { tmp=mcast_receiver; mcast_receiver=null; closeMulticastSocket(); // will cause the multicast thread to terminate tmp.interrupt(); try { tmp.join(100); } catch(Exception e) { } tmp=null; } mcast_receiver=null; } // 2. Stop the unicast receiver thread if(ucast_receiver != null) { ucast_receiver.stop(); ucast_receiver=null; } // 3. Stop the in_packet_handler thread if(incoming_packet_handler != null) incoming_packet_handler.stop(); // 4. Stop the outgoing packet handler thread if(outgoing_packet_handler != null) outgoing_packet_handler.stop(); } void handleDownEvent(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: synchronized(members) { members.removeAllElements(); Vector tmpvec=((View)evt.getArg()).getMembers(); for(int i=0; i < tmpvec.size(); i++) members.addElement(tmpvec.elementAt(i)); } break; case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local) passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); break; case Event.CONNECT: channel_name=(String)evt.getArg(); udp_hdr=new UdpHeader(channel_name); // removed March 18 2003 (bela), not needed (handled by GMS) // changed July 2 2003 (bela): we discard CONNECT_OK at the GMS level anyway, this might // be needed if we run without GMS though passUp(new Event(Event.CONNECT_OK)); break; case Event.DISCONNECT: passUp(new Event(Event.DISCONNECT_OK)); break; case Event.CONFIG: if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); break; } } void handleConfigEvent(HashMap map) { if(map == null) return; if(map.containsKey("additional_data")) additional_data=(byte[])map.get("additional_data"); if(map.containsKey("send_buf_size")) { mcast_send_buf_size=((Integer)map.get("send_buf_size")).intValue(); ucast_send_buf_size=mcast_send_buf_size; } if(map.containsKey("recv_buf_size")) { mcast_recv_buf_size=((Integer)map.get("recv_buf_size")).intValue(); ucast_recv_buf_size=mcast_recv_buf_size; } setBufferSizes(); } /* ----------------------------- End of Private Methods ---------------------------------------- */ /* ----------------------------- Inner Classes ---------------------------------------- */ class IncomingQueueEntry { IpAddress dest=null; InetAddress sender=null; int port=-1; byte[] buf; public IncomingQueueEntry(IpAddress dest, InetAddress sender, int port, byte[] buf) { this.dest=dest; this.sender=sender; this.port=port; this.buf=buf; } public IncomingQueueEntry(byte[] buf) { this.buf=buf; } } public class UcastReceiver implements Runnable { boolean running=true; Thread thread=null; public void start() { if(thread == null) { thread=new Thread(this, "UDP.UcastReceiverThread"); thread.setDaemon(true); running=true; thread.start(); } } public void stop() { Thread tmp; if(thread != null && thread.isAlive()) { running=false; tmp=thread; thread=null; closeSocket(); // this will cause the thread to break out of its loop tmp.interrupt(); tmp=null; } thread=null; } public void run() { DatagramPacket packet; byte receive_buf[]=new byte[65535]; int len; byte[] data, tmp; InetAddress sender_addr; int sender_port; // moved out of loop to avoid excessive object creations (bela March 8 2001) packet=new DatagramPacket(receive_buf, receive_buf.length); while(running && thread != null && sock != null) { try { packet.setData(receive_buf, 0, receive_buf.length); sock.receive(packet); sender_addr=packet.getAddress(); sender_port=packet.getPort(); len=packet.getLength(); data=packet.getData(); if(log.isTraceEnabled()) log.trace(new StringBuilder("received (ucast) ").append(len).append(" bytes from "). append(sender_addr).append(':').append(sender_port)); if(len > receive_buf.length) { if(log.isErrorEnabled()) log.error("size of the received packet (" + len + ") is bigger than allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); } if(use_incoming_packet_handler) { tmp=new byte[len]; System.arraycopy(data, 0, tmp, 0, len); incoming_queue.add(new IncomingQueueEntry(local_addr, sender_addr, sender_port, tmp)); } else handleIncomingUdpPacket(local_addr, sender_addr, sender_port, data); } catch(SocketException sock_ex) { if(log.isDebugEnabled()) log.debug("unicast receiver socket is closed, exception=" + sock_ex); break; } catch(InterruptedIOException io_ex) { // thread was interrupted ; // go back to top of loop, where we will terminate loop } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("[" + local_addr + "] failed receiving unicast packet", ex); Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) } } if(log.isDebugEnabled()) log.debug("unicast receiver thread terminated"); } } /** * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up * to the higher layer (done in handleIncomingUdpPacket()). */ class IncomingPacketHandler implements Runnable { Thread t=null; public void run() { byte[] data; IncomingQueueEntry entry; while(incoming_queue != null && incoming_packet_handler != null) { try { entry=(IncomingQueueEntry)incoming_queue.remove(); data=entry.buf; } catch(QueueClosedException closed_ex) { if(log.isDebugEnabled()) log.debug("packet_handler thread terminating"); break; } handleIncomingUdpPacket(entry.dest, entry.sender, entry.port, data); } } void start() { if(t == null || !t.isAlive()) { t=new Thread(this, "UDP.IncomingPacketHandler thread"); t.setDaemon(true); t.start(); } } void stop() { if(incoming_queue != null) incoming_queue.close(false); // should terminate the packet_handler thread too t=null; incoming_queue=null; } } /** * This thread fetches byte buffers from the outgoing_packet_queue, converts them into messages and sends them * using the unicast or multicast socket */ class OutgoingPacketHandler implements Runnable { Thread t=null; byte[] buf; DatagramPacket packet; IpAddress dest; public void run() { Message msg; while(outgoing_queue != null && outgoing_packet_handler != null) { try { msg=(Message)outgoing_queue.remove(); handleMessage(msg); } catch(QueueClosedException closed_ex) { break; } catch(Throwable th) { if(log.isErrorEnabled()) log.error("exception sending packet", th); } msg=null; // let's give the poor garbage collector a hand... } if(log.isTraceEnabled()) log.trace("packet_handler thread terminating"); } protected void handleMessage(Message msg) throws Exception { send(msg); } void start() { if(t == null || !t.isAlive()) { t=new Thread(this, "UDP.OutgoingPacketHandler thread"); t.setDaemon(true); t.start(); } } void stop() { if(outgoing_queue != null) outgoing_queue.close(false); // should terminate the packet_handler thread too t=null; // outgoing_queue=null; } } /** * Bundles smaller messages into bigger ones. Collects messages in a list until * messages of a total of max_bundle_size bytes have accumulated, or until * max_bundle_timeout milliseconds have elapsed, whichever is first. Messages * are unbundled at the receiver. */ class BundlingOutgoingPacketHandler extends OutgoingPacketHandler { long total_bytes=0; /** HashMap>. Keys are destinations, values are lists of Messages */ final HashMap msgs=new HashMap(11); void start() { super.start(); t.setName("UDP.BundlingOutgoingPacketHandler thread"); } public void run() { Message msg=null, leftover=null; long start=0; while(outgoing_queue != null) { try { total_bytes=0; msg=leftover != null? leftover : (Message)outgoing_queue.remove(); // blocks until message is available start=System.currentTimeMillis(); leftover=waitForMessagesToAccumulate(msg, outgoing_queue, max_bundle_size, start, max_bundle_timeout); bundleAndSend(start); } catch(QueueClosedException closed_ex) { break; } catch(Throwable th) { if(log.isErrorEnabled()) log.error("exception sending packet", th); } } bundleAndSend(start); if(log.isTraceEnabled()) log.trace("packet_handler thread terminating"); } /** * Waits until max_size bytes have accumulated in the queue, or max_time milliseconds have elapsed. * When a message cannot be added to the ready-to-send bundle, it is returned, so the caller can * re-submit it again next time. * @param m * @param q * @param max_size * @param max_time * @return */ Message waitForMessagesToAccumulate(Message m, Queue q, long max_size, long start_time, long max_time) { Message msg, leftover=null; boolean running=true, size_exceeded=false, time_reached=false; long len, time_to_wait=max_time, waited_time=0; while(running) { try { msg=m != null? m : (Message)q.remove(time_to_wait); m=null; // necessary, otherwise we get 'm' again in subsequent iterations of the same loop ! len=msg.size(); checkLength(len); waited_time=System.currentTimeMillis() - start_time; time_to_wait=max_time - waited_time; size_exceeded=total_bytes + len > max_size; time_reached=time_to_wait <= 0; if(size_exceeded) { running=false; leftover=msg; } else { addMessage(msg); total_bytes+=len; if(time_reached) running=false; } } catch(TimeoutException timeout) { waited_time=System.currentTimeMillis() - start_time; time_reached=true; break; } catch(QueueClosedException closed) { break; } catch(Exception ex) { log.error("failure in bundling", ex); } } return leftover; } void checkLength(long len) throws Exception { if(len > max_bundle_size) throw new Exception("UDP.BundlingOutgoingPacketHandler.handleMessage(): message size (" + len + ") is greater than max bundling size (" + max_bundle_size + "). " + "Set the fragmentation/bundle size in FRAG and UDP correctly"); } void addMessage(Message msg) { List tmp; Address dst=msg.getDest(); synchronized(msgs) { tmp=(List)msgs.get(dst); if(tmp == null) { tmp=new List(); msgs.put(dst, tmp); } tmp.add(msg); } } private void bundleAndSend(long start_time) { Map.Entry entry; IpAddress dst; Buffer buffer; InetAddress addr; int port; List l; long stop_time=System.currentTimeMillis(); synchronized(msgs) { if(msgs.size() == 0) return; if(start_time == 0) start_time=System.currentTimeMillis(); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("sending ").append(numMsgs(msgs)).append(" msgs ("); sb.append(total_bytes).append(" bytes, ").append(stop_time-start_time).append("ms)"); sb.append(" to ").append(msgs.size()).append(" destination(s)"); if(msgs.size() > 1) sb.append(" (dests=").append(msgs.keySet()).append(")"); log.trace(sb.toString()); } for(Iterator it=msgs.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); dst=(IpAddress)entry.getKey(); addr=dst.getIpAddress(); port=dst.getPort(); l=(List)entry.getValue(); try { if(l.size() > 0) { synchronized(out_stream) { buffer=listToBuffer(l, dst); doSend(buffer, addr, port); } } } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception sending msg (to dest=" + dst + ")", e); } } msgs.clear(); } } private int numMsgs(HashMap map) { Collection values=map.values(); List l; int size=0; for(Iterator it=values.iterator(); it.hasNext();) { l=(List)it.next(); size+=l.size(); } return size; } } String dumpMessages(HashMap map) { StringBuilder sb=new StringBuilder(); Map.Entry entry; List l; Object key; if(map != null) { synchronized(map) { for(Iterator it=map.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); if(key == null) key="null"; l=(List)entry.getValue(); sb.append(key).append(": "); sb.append(l.size()).append(" msgs\n"); } } } return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/FC.java.txt0000644000175000017500000005143611366547366027227 0ustar twernertwerner// $Id: FC.java.txt,v 1.1.14.1 2008/01/22 10:01:12 belaban Exp $ package org.jgroups.protocols; import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.CondVar; import org.jgroups.util.Streamable; import java.io.*; import java.util.*; /** * Simple flow control protocol based on a credit system. Each sender has a number of credits (bytes * to send). When the credits have been exhausted, the sender blocks. Each receiver also keeps track of * how many credits it has received from a sender. When credits for a sender fall below a threshold, * the receiver sends more credits to the sender. Works for both unicast and multicast messages. *

    * Note that this protocol must be located towards the top of the stack, or all down_threads from JChannel to this * protocol must be set to false ! This is in order to block JChannel.send()/JChannel.down(). * @author Bela Ban * @version $Revision: 1.1.14.1 $ */ public class FC extends Protocol { /** My own address */ Address local_addr=null; /** HashMap: keys are members, values are credits left. For each send, the * number of credits is decremented by the message size */ final Map sent=new HashMap(11); // final Map sent=new ConcurrentHashMap(11); /** HashMap: keys are members, values are credits left (in bytes). * For each receive, the credits for the sender are decremented by the size of the received message. * When the credits are 0, we refill and send a CREDIT message to the sender. Sender blocks until CREDIT * is received after reaching min_credits credits. */ final Map received=new ConcurrentReaderHashMap(11); // final Map received=new ConcurrentHashMap(11); /** We cache the membership */ final Vector members=new Vector(11); /** List of members from whom we expect credits */ final Vector creditors=new Vector(11); /** Max number of bytes to send per receiver until an ack must * be received before continuing sending */ private long max_credits=50000; /** Max time (in milliseconds) to block. If credit hasn't been received after max_block_time, we send * a REPLENISHMENT request to the members from which we expect credits. A value <= 0 means to * wait forever. */ private long max_block_time=5000; /** If credits fall below this limit, we send more credits to the sender. (We also send when * credits are exhausted (0 credits left)) */ double min_threshold=0.25; /** Computed as max_credits times min_theshold. If explicitly set, this will * override the above computation */ private long min_credits=0; /** Current blocking. True if blocking, else false */ private final CondVar blocking=new CondVar("blocking", Boolean.FALSE); static final String name="FC"; private long start_blocking=0, stop_blocking=0; private int num_blockings=0, num_replenishments=0, num_credit_requests=0; private long total_time_blocking=0; final BoundedList last_blockings=new BoundedList(50); final static FcHeader REPLENISH_HDR=new FcHeader(FcHeader.REPLENISH); final static FcHeader CREDIT_REQUEST_HDR=new FcHeader(FcHeader.CREDIT_REQUEST); public String getName() { return name; } public void resetStats() { super.resetStats(); num_blockings=num_replenishments=num_credit_requests=0; total_time_blocking=0; last_blockings.removeAll(); } public long getMaxCredits() { return max_credits; } public void setMaxCredits(long max_credits) { this.max_credits=max_credits; } public double getMinThreshold() { return min_threshold; } public void setMinThreshold(double min_threshold) { this.min_threshold=min_threshold; } public long getMinCredits() { return min_credits; } public void setMinCredits(long min_credits) { this.min_credits=min_credits; } public boolean isBlocked() { Object obj=blocking.get(); return obj != null && obj instanceof Boolean && ((Boolean)obj).booleanValue(); } public int getNumberOfBlockings() { return num_blockings; } public long getTotalTimeBlocked() { return total_time_blocking; } public double getAverageTimeBlocked() { return num_blockings == 0? num_blockings : total_time_blocking / num_blockings; } public int getNumberOfReplenishmentsReceived() { return num_replenishments; } public int getNumberOfCreditRequests() { return num_credit_requests; } public String printSenderCredits() { return printMap(sent); } public String printReceiverCredits() { return printMap(received); } public String printCredits() { StringBuilder sb=new StringBuilder(); sb.append("senders:\n").append(printMap(sent)).append("\n\nreceivers:\n").append(printMap(received)); return sb.toString(); } public Map dumpStats() { Map retval=super.dumpStats(); if(retval == null) retval=new HashMap(); retval.put("senders", printMap(sent)); retval.put("receivers", printMap(received)); retval.put("num_blockings", new Integer(this.num_blockings)); retval.put("avg_time_blocked", new Double(getAverageTimeBlocked())); retval.put("num_replenishments", new Integer(this.num_replenishments)); return retval; } public String showLastBlockingTimes() { return last_blockings.toString(); } public void unblock() { unblockSender(); } public boolean setProperties(Properties props) { String str; boolean min_credits_set=false; super.setProperties(props); str=props.getProperty("max_credits"); if(str != null) { max_credits=Long.parseLong(str); props.remove("max_credits"); } str=props.getProperty("min_threshold"); if(str != null) { min_threshold=Double.parseDouble(str); props.remove("min_threshold"); } str=props.getProperty("min_credits"); if(str != null) { min_credits=Long.parseLong(str); props.remove("min_credits"); min_credits_set=true; } if(!min_credits_set) min_credits=(long)((double)max_credits * min_threshold); str=props.getProperty("max_block_time"); if(str != null) { max_block_time=Long.parseLong(str); props.remove("max_block_time"); } if(props.size() > 0) { log.error("FC.setProperties(): the following properties are not recognized: " + props); return false; } return true; } public void stop() { super.stop(); unblock(); } /** * We need to receive view changes concurrent to messages on the down events: a message might blocks, e.g. * because we don't have enough credits to send to member P. However, if member P crashed, we need to unblock ! * @param evt */ protected void receiveDownEvent(Event evt) { if(evt.getType() == Event.VIEW_CHANGE) { View v=(View)evt.getArg(); Vector mbrs=v.getMembers(); handleViewChange(mbrs); } super.receiveDownEvent(evt); } public void down(Event evt) { switch(evt.getType()) { case Event.MSG: handleDownMessage(evt); return; } passDown(evt); // this could potentially use the lower protocol's thread which may block } private synchronized void handleDownMessage(Event evt) { if(Boolean.TRUE.equals(blocking.get())) { // blocked waitUntilEnoughCreditsAvailable(); } else { // not blocked boolean rc; synchronized(sent) { // 'sent' is the same lock as blocking.getLock()... rc=decrMessage((Message)evt.getArg()); if(rc == false) { if(trace) log.trace("blocking due to insufficient credits"); blocking.set(Boolean.TRUE); start_blocking=System.currentTimeMillis(); num_blockings++; } } if(rc == false) { waitUntilEnoughCreditsAvailable(); } } passDown(evt); } public void up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.VIEW_CHANGE: handleViewChange(((View)evt.getArg()).getMembers()); break; case Event.MSG: Message msg=(Message)evt.getArg(); FcHeader hdr=(FcHeader)msg.removeHeader(name); if(hdr != null) { switch(hdr.type) { case FcHeader.REPLENISH: num_replenishments++; handleCredit(msg.getSrc()); break; case FcHeader.CREDIT_REQUEST: num_credit_requests++; Address sender=msg.getSrc(); if(trace) log.trace("received credit request from " + sender + ": sending credits"); received.put(sender, new Long(max_credits)); sendCredit(sender); break; default: log.error("header type " + hdr.type + " not known"); break; } return; // don't pass message up } else { adjustCredit(msg); } break; } passUp(evt); } private void handleCredit(Address sender) { if(sender == null) return; StringBuilder sb=null; boolean unblock=false; if(trace) { Long old_credit=(Long)sent.get(sender); sb=new StringBuilder(); sb.append("received credit from ").append(sender).append(", old credit was "). append(old_credit).append(", new credits are ").append(max_credits). append(".\nCreditors before are: ").append(creditors); } synchronized(sent) { sent.put(sender, new Long(max_credits)); if(creditors.size() > 0) { // we are blocked because we expect credit from one or more members removeCreditor(sender); if(trace) { sb.append("\nCreditors after removal of ").append(sender).append(" are: ").append(creditors); log.trace(sb.toString()); } if(creditors.size() == 0) { unblock=true; } } else { // no creditors, but still blocking: we need to unblock if(Boolean.TRUE.equals(blocking.get())) unblock=true; } } if(unblock) // moved this outside of the 'sent' synchronized block unblockSender(); } /** * Check whether sender has enough credits left. If not, send him some more * @param msg */ private void adjustCredit(Message msg) { Address src=msg.getSrc(); long size=Math.max(24, msg.getLength()); if(src == null) { if(log.isErrorEnabled()) log.error("src is null"); return; } if(decrementCredit(received, src, size, min_credits) == false) { received.put(src, new Long(max_credits)); if(trace) log.trace("sending replenishment message to " + src); sendCredit(src); } } private void sendCredit(Address dest) { Message msg=new Message(dest, null, null); msg.putHeader(name, REPLENISH_HDR); passDown(new Event(Event.MSG, msg)); } private void sendCreditRequest(final Address dest) { Message msg=new Message(dest, null, null); msg.putHeader(name, CREDIT_REQUEST_HDR); passDown(new Event(Event.MSG, msg)); } /** * Checks whether enough credits are available to send message. If not, blocks until enough credits * are available * @param evt Guaranteed to be a Message * @return */ private void waitUntilEnoughCreditsAvailable() { while(true) { try { blocking.waitUntilWithTimeout(Boolean.FALSE, max_block_time); // waits on 'sent' break; } catch(TimeoutException e) { List tmp=new ArrayList(creditors); if(trace) log.trace("timeout occurred waiting for credits; sending credit request to " + tmp + ", creditors are " + creditors); Address mbr; for(Iterator it=tmp.iterator(); it.hasNext();) { mbr=(Address)it.next(); sendCreditRequest(mbr); } } } } /** * Try to decrement the credits needed for this message and return true if successful, or false otherwise. * For unicast destinations, the credits required are subtracted from the unicast destination member, for * multicast messages the credits are subtracted from all current members in the group. * @param msg * @return false: will block, true: will not block */ private boolean decrMessage(Message msg) { Address dest; long size; boolean success=true; // ****************************************************************************************************** // this method is called by waitUntilEnoughCredits() which syncs on 'sent', so we don't need to sync here // ****************************************************************************************************** if(msg == null) { if(log.isErrorEnabled()) log.error("msg is null"); return true; // don't block ! } dest=msg.getDest(); size=Math.max(24, msg.getLength()); if(dest != null && !dest.isMulticastAddress()) { // unicast destination if(decrementCredit(sent, dest, size, 0)) { return true; } else { addCreditor(dest); return false; } } else { // multicast destination for(Iterator it=members.iterator(); it.hasNext();) { dest=(Address)it.next(); if(decrementCredit(sent, dest, size, 0) == false) { addCreditor(dest); success=false; } } } return success; } /** If message queueing is enabled, sends queued messages and unlocks sender (if successful) */ private void unblockSender() { if(start_blocking > 0) { stop_blocking=System.currentTimeMillis(); long diff=stop_blocking - start_blocking; total_time_blocking+=diff; last_blockings.add(new Long(diff)); stop_blocking=start_blocking=0; if(trace) log.trace("setting blocking=false, blocking time was " + diff + "ms"); } if(trace) log.trace("setting blocking=false"); blocking.set(Boolean.FALSE); } private void addCreditor(Address mbr) { if(mbr != null && !creditors.contains(mbr)) creditors.add(mbr); } private void removeCreditor(Address mbr) { creditors.remove(mbr); } /** * Find the credits associated with dest and decrement its credits by credits_required. If the remaining * value is less than or equal to 0, return false, else return true. Note that we will always subtract the credits. * @param map * @param dest * @param credits_required Number of bytes required * @param minimal_credits For the receiver: add minimal credits to check whether credits need to be sent * @return Whether the required credits could successfully be subtracted from the credits left */ private boolean decrementCredit(Map map, Address dest, long credits_required, long minimal_credits) { long credits_left, new_credits_left; Long tmp=(Long)map.get(dest); boolean success; if(tmp == null) return true; credits_left=tmp.longValue(); success=credits_left > (credits_required + minimal_credits); new_credits_left=Math.max(0, credits_left - credits_required); map.put(dest, new Long(new_credits_left)); if(success) { return true; } else { if(trace) { StringBuilder sb=new StringBuilder(); sb.append("not enough credits left for ").append(dest).append(": left=").append(new_credits_left); sb.append(", required+min_credits=").append((credits_required +min_credits)).append(", required="); sb.append(credits_required).append(", min_credits=").append(min_credits); log.trace(sb.toString()); } return false; } } void handleViewChange(Vector mbrs) { Address addr; if(mbrs == null) return; if(trace) log.trace("new membership: " + mbrs); members.clear(); members.addAll(mbrs); synchronized(received) { // add members not in membership to received hashmap (with full credits) for(int i=0; i < mbrs.size(); i++) { addr=(Address) mbrs.elementAt(i); if(!received.containsKey(addr)) received.put(addr, new Long(max_credits)); } // remove members that left for(Iterator it=received.keySet().iterator(); it.hasNext();) { addr=(Address) it.next(); if(!mbrs.contains(addr)) it.remove(); } } boolean unblock=false; synchronized(sent) { // add members not in membership to sent hashmap (with full credits) for(int i=0; i < mbrs.size(); i++) { addr=(Address) mbrs.elementAt(i); if(!sent.containsKey(addr)) sent.put(addr, new Long(max_credits)); } // remove members that left for(Iterator it=sent.keySet().iterator(); it.hasNext();) { addr=(Address)it.next(); if(!mbrs.contains(addr)) it.remove(); // modified the underlying map } // remove all creditors which are not in the new view for(int i=0; i < creditors.size(); i++) { Address creditor=(Address)creditors.elementAt(i); if(!mbrs.contains(creditor)) creditors.remove(creditor); } if(trace) log.trace("creditors are " + creditors); if(creditors.size() == 0) unblock=true; } if(unblock) unblockSender(); } private static String printMap(Map m) { Map.Entry entry; StringBuilder sb=new StringBuilder(); for(Iterator it=m.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } public static class FcHeader extends Header implements Streamable { public static final byte REPLENISH = 1; public static final byte CREDIT_REQUEST = 2; // the sender of the message is the requester byte type = REPLENISH; public FcHeader() { } public FcHeader(byte type) { this.type=type; } public long size() { return Global.BYTE_SIZE; } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); } public String toString() { switch(type) { case REPLENISH: return "REPLENISH"; case CREDIT_REQUEST: return "CREDIT_REQUEST"; default: return ""; } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/FD_RAND.java.txt0000644000175000017500000001553511366547366030034 0ustar twernertwerner// $Id: FD_RAND.java.txt,v 1.3 2005/05/30 14:31:05 belaban Exp $ package org.jgroups.protocols; import java.util.Properties; import java.util.Vector; class FdRandHeader extends Header { static final int HEARTBEAT = 0; static final int HEARTBEAT_ACK = 1; static final int SUSPECT = 2; static final int REGULAR = 3; int type=HEARTBEAT; Object suspected_mbr=null; FdRandHeader(int type) {this.type=type;} public String toString() { switch(type) { case HEARTBEAT: return "[FD_RAND: heartbeat]"; case HEARTBEAT_ACK: return "[FD_RAND: heartbeat ack]"; case SUSPECT: return "[FD_RAND: suspect]"; case REGULAR: return "[FD_RAND: regular message]"; default: return "[FD_RAND: unknown type (" + type + ")]"; } } } /** Failure detection based on simple heartbeat protocol. Regularly polls randomly selected members for liveness. Passes SUSPECT message up the stack when a member is not reachable. The simple algorithms works as follows: the membership is known. Each HB protocol periodically sends a 'are-you-alive' message to a randomly selected member, except itself. When a response hasn't been received for n milliseconds and m tries, the corresponding member is suspected (and eventually excluded if faulty).

    FD_RAND starts when it detects (in a view change notification) that there are at least 2 members in the group. It stops running when the membership drops below 2. */ public class FD_RAND extends Protocol implements Runnable { boolean trace=false; Address ping_dest=null; Address local_addr=null; Thread pinger=null; long timeout=1000; // 1 second between heartbeats boolean ack_received=false; Vector members=null; int num_tries=0; final int max_tries=2; // 3 tries before suspecting Object ack_mutex=new Object(); public String getName() {return "FD_RAND";} public boolean setProperties(Properties props) {super.setProperties(props); String str; this.props=props; str=props.getProperty("trace"); if(str != null) { trace=new Boolean(str).booleanValue(); props.remove("trace"); } str=props.getProperty("timeout"); if(str != null) { timeout=new Long(str).longValue(); props.remove("timeout"); } str=props.getProperty("num_tries"); if(str != null) { num_tries=new Integer(str).intValue(); props.remove("num_tries"); if(num_tries < 1) { log.error("FD_RAND.setProperties(): propertiy 'num_tries' must be at least 1 ! " + "Setting it to 1"); num_tries=1; } } if(props.size() > 0) { log.error("FD_RAND.setProperties(): the following properties are not recognized: " + props); return false; } return true; } Address getPingDest() { Address retval=null; int r, size; if(members == null || members.size() < 2 || local_addr == null) return null; size=members.size(); while(members.size() > 1) { r=((int)(Math.random() * (size+1))) % size; retval=(Address)members.elementAt(r); if(local_addr.equals(retval)) continue; else break; } return retval; } public void up(Event evt) { Message msg; FdRandHeader hdr=null; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: msg=(Message)evt.getArg(); try { hdr=(FdRandHeader)msg.removeHeader(); } catch(Exception e) { log.error("FD_RAND.up(): " + e); } switch(hdr.type) { case FdRandHeader.HEARTBEAT: // heartbeat request; send heartbeat ack Message hb_ack=new Message(msg.getSrc(), null, null); FdRandHeader tmp_hdr=new FdRandHeader(FdRandHeader.HEARTBEAT_ACK); tmp_hdr.suspected_mbr=local_addr; hb_ack.addHeader(tmp_hdr); passDown(new Event(Event.MSG, hb_ack)); return; // don't pass up ! case FdRandHeader.HEARTBEAT_ACK: // heartbeat ack Object suspect=hdr.suspected_mbr; if(ping_dest != null && ping_dest.equals(suspect)) { synchronized(ack_mutex) { ack_received=true; ack_mutex.notify(); } } return; case FdRandHeader.SUSPECT: if(hdr.suspected_mbr != null) { System.out.println("FD_RAND: SUSPECT(" + hdr.suspected_mbr + ")"); passUp(new Event(Event.SUSPECT, hdr.suspected_mbr)); } return; default: break; } } passUp(evt); // pass up to the layer above us } public void down(Event evt) { Message msg; switch(evt.getType()) { case Event.STOP: stop(); passDown(evt); break; case Event.VIEW_CHANGE: synchronized(this) { stop(); View v=(View)evt.getArg(); members=v != null ? v.getMembers() : null; passDown(evt); start(); } break; case Event.MSG: msg=(Message)evt.getArg(); msg.addHeader(new FdRandHeader(FdRandHeader.REGULAR)); // regular message passDown(evt); break; default: passDown(evt); break; } } /** Loop while more than 1 member available. Choose a member randomly (not myself !) and send a heartbeat. Wait for ack. If ack not received withing timeout, mcast SUSPECT message. */ public void run() { Message suspect_msg, hb_req; FdRandHeader hdr; while(members.size() > 1 && pinger != null) { ack_received=false; num_tries=0; ping_dest=getPingDest(); while(!ack_received && num_tries <= max_tries && pinger != null) { hb_req=new Message(ping_dest, null, null); hb_req.addHeader(new FdRandHeader(FdRandHeader.HEARTBEAT)); // send heartbeat request passDown(new Event(Event.MSG, hb_req)); synchronized(ack_mutex) { // wait for heartbeat ack try {ack_mutex.wait(timeout);} catch(Exception e) {} } if(pinger == null) return; if(ack_received) { Util.sleep(timeout); break; } else { if(num_tries >= max_tries) { System.out.println("FD_RAND(" + local_addr + "): received no heartbeat ack from " + ping_dest + ", suspecting it"); hdr=new FdRandHeader(FdRandHeader.SUSPECT); hdr.suspected_mbr=ping_dest; suspect_msg=new Message(null, null, null); // mcast SUSPECT to all members suspect_msg.addHeader(hdr); passDown(new Event(Event.MSG, suspect_msg)); break; } else { num_tries++; Util.sleep(timeout); } } } } } void start() { if(pinger == null) { pinger=new Thread(this, "FD_RAND.PingerThread"); pinger.start(); } } void stop() { Thread tmp=null; num_tries=0; ack_received=false; if(pinger != null && pinger.isAlive()) { tmp=pinger; pinger=null; tmp.interrupt(); try {tmp.join(timeout);} catch(Exception ex) {} } pinger=null; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/UNIFORM.java.txt0000644000175000017500000002264011366547366030051 0ustar twernertwerner// $Id: UNIFORM.java.txt,v 1.3 2005/05/30 14:31:05 belaban Exp $ package org.jgroups.protocols; import java.io.Serializable; import java.util.Hashtable; import java.util.Properties; import java.util.Vector; class UniformHeader implements Serializable { public static final int SAVE = 0; public static final int SAVE_OK = 1; public static final int DELIVER = 2; public static final int DELIVER_OK = 3; public static final int SEEN = 4; public static final int SEEN_OK = 5; public static final int SEEN_NOTOK = 6; public static final int GC = 7; public static final int GC_OK = 8; public int type=-1; public long id=-1; public boolean handle=true; String type2Str(int t) { switch(t) { case SAVE: return "SAVE"; case SAVE_OK: return "SAVE_OK"; case DELIVER: return "DELIVER"; case DELIVER_OK: return "DELIVER_OK"; case SEEN: return "SEEN"; case SEEN_OK: return "SEEN_OK"; case GC: return "GC"; case GC_OK: return "GC_OK"; default: return ""; } } public UniformHeader() {handle=false;} public UniformHeader(int type) { this.type=type; id=System.currentTimeMillis(); handle=true; } public UniformHeader(int type, long id) { this.type=type; this.id=id; handle=true; } public String toString() {return "[UNIFORM: type=" + type2Str(type) + ", id=" + id + "]";} } /** The algorithms implements dynamically-uniform failure-atomic group multicast, that is, a message is delivered by all members if it is delivered by at least 1 non-faulty member even if the sender crashes after sending. If the sender crashes, it will eventually be removed from the group membership: the FLUSH protocol preceding the view change causes all pending multicasts to be flushed out of the system, thereby re-sending pending multicasts to members that haven't received them yet.

    The protocol makes use of GroupRequest (which itself uses RequestCorrelator) to send a request to all members and receive responses from all non-faulty members. */ public class UNIFORM extends Protocol implements RequestHandler, Transport { Vector members=null; boolean trace=false; RequestCorrelator corr=new RequestCorrelator(getName(), this, this); Hashtable pending=new Hashtable(); // key = sender, val = Hashtable (msg-id, msg) Hashtable delivered=new Hashtable(); // key = sender, val = Hashtable (msg-id, msg) public String getName() {return "UNIFORM";} public boolean setProperties(Properties props) {super.setProperties(props); String str; this.props=props; str=props.getProperty("trace"); if(str != null) { trace=new Boolean(str).booleanValue(); props.remove("trace"); } if(props.size() > 0) { log.error("UNIFORM.setProperties(): the following properties are not recognized: " + props); return false; } return true; } /** Just remove if you don't need to reset any state */ public void reset() {} public void up(Event evt) { Message msg; boolean rc; Object obj; if(evt.getType() == Event.START) { corr.start(); passUp(evt); return; } corr.receive(evt); } public void down(Event evt) { Message msg; GroupRequest save_req, deliver_req, seen_req, gc_req; AndCommand and_comm; Message save_msg, deliver_msg, seen_msg, gc_msg; Vector mbrs=null; long id=0; switch(evt.getType()) { case Event.STOP: corr.Stop(); passDown(evt); break; case Event.TMP_VIEW: case Event.VIEW_CHANGE: Vector tmp; if((tmp=(Vector)((View)evt.getArg()).getMembers()) != null) members=tmp; passDown(evt); break; case Event.MSG: msg=(Message)evt.getArg(); if(msg.getDest() != null) { // unicast msg passDown(evt); return; } id=System.currentTimeMillis(); mbrs=(Vector)members.clone(); /* 1. Create 4 commands (SaveCommand, OkCommand, SeenCommand and GcCommand). Each has the same unique ID, and each is tagged with its type (e.g. SAVE_REQ, OK_REQ etc). ID and type are contained in a UniformHeader attached to the message (with each command). 2. Create an AndCommand and add the 4 commands. 3. Add the AndCommand to a list of currently running commands and execute it. 4. When a FLUSH request is received, wait until all commands are done. */ save_msg=msg; save_msg.addHeader(new UniformHeader(UniformHeader.SAVE, id)); save_req=new GroupRequest(save_msg, corr, mbrs, GroupRequest.GET_ALL); deliver_msg=new Message(null, null, null); deliver_msg.addHeader(new UniformHeader(UniformHeader.DELIVER, id)); deliver_req=new GroupRequest(deliver_msg, corr, mbrs, GroupRequest.GET_ALL); seen_msg=new Message(null, null, null); seen_msg.addHeader(new UniformHeader(UniformHeader.SEEN, id)); seen_req=new GroupRequest(seen_msg, corr, mbrs, GroupRequest.GET_ALL); gc_msg=new Message(null, null, null); gc_msg.addHeader(new UniformHeader(UniformHeader.GC, id)); gc_req=new GroupRequest(gc_msg, corr, mbrs, GroupRequest.GET_ALL); and_comm=new AndCommand(); and_comm.add(save_req); and_comm.add(deliver_req); and_comm.add(seen_req); and_comm.add(gc_req); boolean rc=and_comm.execute(); System.out.println("UNIFORM: rc from Execute is " + rc); break; default: passDown(evt); // Pass on to the layer below us } } /**

           1. Remove UniformHeader from message and get ID and type
           2. If type == SAVE: add message to save-table (key=sender + ID) and send response (SAVE_OK)
              If type == OK:   add message to ok-table, remove from save-table. Deliver message (pass up
    	  the stack) and send response (OK_OK).
    	  If type == SEEN: find message in ok-table. If found, send SEEN_OK response, else NOT_SEEN.
    	  If type == GC: delete message from ok-table.
           
    */ public Object handle(Message msg) { UniformHeader hdr; Object obj=msg.peekHeader(); Message m=null; if(obj != null && obj instanceof UniformHeader) { hdr=(UniformHeader)msg.removeHeader(); switch(hdr.type) { case UniformHeader.SAVE: System.out.println("==> save in pending: " + msg.getSrc() + ":" + hdr.id); saveInPending(hdr.id, msg); return new Integer(UniformHeader.SAVE_OK); case UniformHeader.DELIVER: System.out.println("==> move to delivered: " + msg.getSrc() + ":" + hdr.id); m=moveFromPendingToDelivered(msg.getSrc(), hdr.id); if(m != null) passUp(new Event(Event.MSG, m)); return new Integer(UniformHeader.DELIVER_OK); case UniformHeader.SEEN: System.out.print("==> find in delivered: " + msg.getSrc() + ":" + hdr.id); if(findInDelivered(msg.getSrc(), hdr.id)) { System.out.println(" SEEN_OK"); return new Integer(UniformHeader.SEEN_OK); } System.out.println(" SEEN_NOTOK"); return new Integer(UniformHeader.SEEN_NOTOK); case UniformHeader.GC: System.out.println("==> remove from delivered: " + msg.getSrc() + ":" + hdr.id); removeFromDelivered(msg.getSrc(), hdr.id); return new Integer(UniformHeader.GC_OK); default: log.error("UNIFORM.handle(): UniformHeader.type " + hdr.type + " not known"); break; } } return null; } /* --------------------------- Transport interface ---------------------------------- */ public void send(Message msg) throws Exception {passDown(new Event(Event.MSG, msg));} public Object receive(long timeout) throws Exception {return null;} /* ------------------------ End of Transport interface ------------------------------ */ void saveInPending(long msg_id, Message msg) { Object sender=msg.getSrc(); Long key=new Long(msg_id); Hashtable val=(Hashtable)pending.get(sender); // look for sender as key if(val == null) { val=new Hashtable(); pending.put(sender, val); } if(!val.containsKey(key)) val.put(key, msg); } Message moveFromPendingToDelivered(Object sender, long msg_id) { Message msg=null; Hashtable val_pending, val_delivered; Long key=new Long(msg_id); val_pending=(Hashtable)pending.get(sender); if(val_pending == null) { log.error("UNIFORM.moveFromPendingToDelivered(): value for " + sender + " not found !"); return null; } msg=(Message)val_pending.get(key); if(msg == null) { log.error("UNIFORM.moveFromPendingToDelivered(): value for " + sender + ":" + key + " not found !"); return null; } val_delivered=(Hashtable)delivered.get(sender); if(val_delivered == null) { val_delivered=new Hashtable(); delivered.put(sender, val_delivered); } if(!val_delivered.containsKey(key)) val_delivered.put(key, msg); val_pending.remove(key); // remove from pending table if(val_pending.size() == 0) pending.remove(sender); return msg; } boolean findInDelivered(Object sender, long msg_id) { Hashtable val=(Hashtable)delivered.get(sender); if(val == null) return false; return val.containsKey(new Long(msg_id)); } void removeFromDelivered(Object sender, long msg_id) { Hashtable val=(Hashtable)delivered.get(sender); if(val == null) return; val.remove(new Long(msg_id)); if(val.size() == 0) delivered.remove(sender); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/ENCRYPT.java.txt0000644000175000017500000002663411366547366030065 0ustar twernertwerner// $Id: ENCRYPT.java.txt,v 1.3 2005/05/30 14:31:05 belaban Exp $ package org.jgroups.protocols; import java.io.Serializable; import java.security.*; import java.security.spec.KeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Properties; import java.util.Vector; import org.jgroups.*; import org.jgroups.stack.*; import org.jgroups.log.Trace; class EncryptHeader implements Serializable { int type; static final int ENCRYPT = 0; static final int KEY_REQUEST = 1; static final int SERVER_PUBKEY = 2; static final int SECRETKEY = 3; static final int SECRETKEY_READY = 4; public EncryptHeader(int type) { this.type = type; } public String toString() {return "[ENCTYPT: ]";} } /** * ENCRYPT layer. Encrypt and decrypt the group communication in JGroups */ public class ENCRYPT extends Protocol { Address local_addr=null; Address keyServerAddr = null; boolean keyServer=false; String asymAlgorithm="RSA"; String symAlgorithm="DES/ECB/PKCS5Padding"; int asymInit=512; // initial public/private key length int symInit=56; // initial shared key length // for public/private Key KeyPair Kpair; // to store own's public/private Key SecretKey desKey=null; PublicKey pubKey = null; // for server to store the temporary client public key PublicKey serverPubKey = null; // for client to store server's public Key Cipher cipher; Cipher rsa; Vector members=new Vector(); Vector notReady = new Vector(); public ENCRYPT(){ Security.addProvider(new ABAProvider()); } public String getName() {return "ENCRYPT";} /* * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding" */ private String getAlgorithm(String s) { int index = s.indexOf("/"); if (index==-1) return s; return s.substring(0, index); } public boolean setProperties(Properties props) {super.setProperties(props); String str; this.props=props; // asymmetric key length str=props.getProperty("asymInit"); if (str != null) { asymInit = new Integer(str).intValue(); props.remove("asymInit"); System.out.println("asymInit = "+asymInit); } // symmetric key length str=props.getProperty("symInit"); if (str != null) { symInit = new Integer(str).intValue(); props.remove("symInit"); System.out.println("symInit = "+symInit); } // asymmetric algorithm name str=props.getProperty("asymAlgorithm"); if (str != null) { asymAlgorithm = new String(str).toString(); props.remove("asymAlgorithm"); } // symmetric algorithm name str=props.getProperty("symAlgorithm"); if (str != null) { symAlgorithm = new String(str).toString(); props.remove("symAlgorithm"); } if (props.size() > 0) { log.error("ENCRYPT.setProperties(): these properties are not recognized: " + props); return false; } // generate keys according to the specified algorithms try{ // generate publicKey and Private Key using RSA KeyPairGenerator KpairGen = KeyPairGenerator.getInstance(getAlgorithm(asymAlgorithm)); KpairGen.initialize(asymInit, new SecureRandom()); Kpair = KpairGen.generateKeyPair(); // generate secret key KeyGenerator keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); keyGen.init(symInit); desKey = keyGen.generateKey(); // initialize for rsa, cipher encryption/decryption rsa = Cipher.getInstance(asymAlgorithm); cipher = Cipher.getInstance(symAlgorithm); } catch(Exception e){ System.out.println(e+"at setProperties"); } return true; } /** Just remove if you don't need to reset any state */ public void reset() {} public void up(Event evt) { Message msg; Message newMsg; EncryptHeader hdr; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.FIND_INITIAL_MBRS_OK: Vector member=(Vector)evt.getArg(); keyServer = member.size() > 0 ? false : true; if (member != null && member.size() > 0) keyServerAddr = (Address)((PingRsp)member.firstElement()).coord_addr; else keyServerAddr = local_addr; System.out.println("keyServer = " + keyServer + " keyServerAddr : "+keyServerAddr.toString()); if (!keyServer) { desKey = null; // client send clien's public key to server and request server's public key newMsg = new Message(keyServerAddr,local_addr,Kpair.getPublic().getEncoded()); newMsg.addHeader(new EncryptHeader(EncryptHeader.KEY_REQUEST)); passDown(new Event(Event.MSG,newMsg)); } passUp(evt); return; case Event.MSG: msg=(Message)evt.getArg(); Object obj=msg.peekHeader(); // if not encrypted message, pass up if (obj == null || !(obj instanceof EncryptHeader)) { passUp(evt); return; } hdr = (EncryptHeader)msg.removeHeader(); switch(hdr.type){ // key request from client and send server's public key to client case EncryptHeader.KEY_REQUEST: try{ // store the this client to notReady list using client's address notReady.addElement(msg.getSrc()); // store the client's public key for temporary PublicKey pubKey = generatePubKey(msg.getBuffer()); // send server's publicKey newMsg = new Message(msg.getSrc(), local_addr, Kpair.getPublic().getEncoded()); newMsg.addHeader(new EncryptHeader(EncryptHeader.SERVER_PUBKEY)); passDown(new Event(Event.MSG, newMsg)); // send shared DesKey to client // 1. Decrypt desKey with server's own private Key // 2. Encrypt decrypted desKey with client's own public Key // encrypt encoded desKey using server's private key rsa.init(Cipher.ENCRYPT_MODE, Kpair.getPrivate()); byte [] decryptedKey = rsa.doFinal(desKey.getEncoded()); // encrypt decrypted key using client's public key rsa.init(Cipher.ENCRYPT_MODE, pubKey); byte [] encryptedKey = rsa.doFinal(decryptedKey); //send encrypted deskey to client newMsg = new Message(msg.getSrc(), local_addr, encryptedKey); newMsg.addHeader(new EncryptHeader(EncryptHeader.SECRETKEY)); passDown(new Event(Event.MSG, newMsg)); } catch(Exception e){ System.out.println(e+"0"); } return; case EncryptHeader.SECRETKEY_READY: //server get client's public key and generate the secret key notReady.removeElement(msg.getSrc()); return; case EncryptHeader.SERVER_PUBKEY: serverPubKey = generatePubKey(msg.getBuffer()); return; case EncryptHeader.SECRETKEY: try{ // decrypt using client's private Key rsa.init(Cipher.DECRYPT_MODE,Kpair.getPrivate()); byte[] decryptedKey = rsa.doFinal(msg.getBuffer()); // decrypt using server's public Key rsa.init(Cipher.DECRYPT_MODE,serverPubKey); byte[] encodedKey = rsa.doFinal(decryptedKey); // decode secretKey desKey = decodedKey(encodedKey); System.out.println("Client generate shared secret key"); // send ready message newMsg = new Message(msg.getSrc(), local_addr, null); newMsg.addHeader(new EncryptHeader(EncryptHeader.SECRETKEY_READY)); passDown(new Event(Event.MSG, newMsg)); } catch(Exception e){ System.out.println(e+"5"); } return; default: break; } if (hdr.type != 0) System.out.println("This is ERROR"); // not have shared key yet // this encrypted message is of no use, drop it if (desKey == null) return; // if both the shared key and incoming message are not null // decrypt the message if (msg.getBuffer()!=null) { try{ cipher.init(Cipher.DECRYPT_MODE, desKey); msg.setBuffer(cipher.doFinal(msg.getBuffer())); } catch(Exception e){ System.out.println(e+"6"); } } break; } passUp(evt); // Pass up to the layer above us } public void down(Event evt) { Message msg; Message newMsg; SecretKey key; boolean leave = false; switch(evt.getType()) { case Event.VIEW_CHANGE: Vector new_members=(Vector)((View)evt.getArg()).getMembers(); // member size decreases: member leaves, need a new key if (members.size() > new_members.size()) leave = true; // copy member list synchronized(members) { members.removeAllElements(); if (new_members != null && new_members.size() > 0) for (int i=0; i < new_members.size(); i++) members.addElement(new_members.elementAt(i)); } // redistribute/regain the new key because old member leaves if (leave){ // get coordinator address Object obj = members.firstElement(); // if I'm the coordinator/key-server if (obj.equals(local_addr)){ //create the new shared key and distribute keyServer = true; keyServerAddr = local_addr; // reset shared key desKey=null; try { //generate new shared key KeyGenerator keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); keyGen.init(symInit); desKey = keyGen.generateKey(); } catch (Exception e) { System.out.println(e+"7"); } }//end of local_addr == obj // if I'm not the coordinator/key-server else { keyServer = false; keyServerAddr = (Address)obj; // reset shared key desKey = null; // client send clien's public key to server and request server's public key newMsg = new Message(keyServerAddr, local_addr, Kpair.getPublic().getEncoded()); newMsg.addHeader(new EncryptHeader(EncryptHeader.KEY_REQUEST)); passDown(new Event(Event.MSG, newMsg)); System.out.println("Request new key"); } } break; case Event.MSG: msg=(Message)evt.getArg(); int i; // For Server: // if some members don't have the shared key yet if (!notReady.isEmpty()){ System.out.println("not Ready list :"+ notReady.toString()); if (msg.getDest() == null){ for (i = 0; i < notReady.size();i++){ newMsg = new Message(notReady.elementAt(i), local_addr, msg.getBuffer()); passDown(new Event(Event.MSG, newMsg)); } break; } else{ for (i = 0; i < notReady.size();i++){ if (msg.getDest() == notReady.elementAt(i)){ passDown(evt); return; } } } } // I already know the shared key if (desKey != null) { try { // if the message is not empty, encrypt it if (msg.getBuffer() != null) { cipher.init(Cipher.ENCRYPT_MODE, desKey); msg.setBuffer(cipher.doFinal(msg.getBuffer())); msg.addHeader(new EncryptHeader(0)); } } catch (Exception e) { System.out.println(e+"8"); } } break; } //System.out.println("Pass Down: "+evt.toString()); passDown(evt); // Pass on to the layer below us } private SecretKey decodedKey(byte[] encodedKey){ SecretKey key = null; try{ SecretKeyFactory KeyFac = SecretKeyFactory.getInstance(getAlgorithm(symAlgorithm)); SecretKeySpec desKeySpec = new SecretKeySpec(encodedKey, getAlgorithm(symAlgorithm)); key = KeyFac.generateSecret((KeySpec)desKeySpec); } catch(Exception e){ log.error(e); } return key; } private PublicKey generatePubKey(byte [] encodedKey){ PublicKey pubKey = null; try{ KeyFactory KeyFac = KeyFactory.getInstance(getAlgorithm(asymAlgorithm)); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey); pubKey = KeyFac.generatePublic((KeySpec)x509KeySpec); } catch(Exception e){ log.error(e); } return pubKey; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/PerfHeader.java.txt0000644000175000017500000002725411366547366030745 0ustar twernertwerner// $Id: PerfHeader.java.txt,v 1.1.4.1 2008/01/22 10:01:13 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.*; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; /** * Inserted by PERF into each message. Records the time taken by each protocol to process the message to * which this header is attached. Travels down through the stack and up the other stack with the message. * * @author Bela Ban */ public class PerfHeader extends Header { Object sender=null; Object receiver=null; long start_time=0; // time when header was created long end_time=0; // time when header was received long network_send=0; // time the packet was put on the network long network_recv=0; // time the packet was received from the network long network_time=0; // time spent on the network (between bottom layers) HashMap down=new HashMap(); // key=protocol name, val=PerfEntry HashMap up=new HashMap(); // key=protocol name, val=PerfEntry final static int UP=1; final static int DOWN=2; final static String classname="org.jgroups.protocols.PerfHeader"; static long size=0; private static Message msg2; static Log log=LogFactory.getLog(PerfHeader.class); static { size=Util.sizeOf(classname); if(size <= 0) size=400; } // Needed for externalization public PerfHeader() { } public PerfHeader(Object sender, Object receiver) { this.sender=sender; this.receiver=receiver; start_time=System.currentTimeMillis(); } public String toString() { return "[PerfHeader]"; } public String printContents(boolean detailed) { return printContents(detailed, null); } public String printContents(boolean detailed, Vector prots) { StringBuilder sb=new StringBuilder(); String key; PerfEntry val; Protocol p; if(sender != null) sb.append("sender=").append(sender).append('\n'); if(receiver != null) sb.append("receiver=").append(receiver).append('\n'); if(detailed) sb.append("start_time=").append(start_time).append("\nend_time=").append(end_time).append('\n'); if(end_time >= start_time) sb.append("total time=").append((end_time - start_time)).append('\n'); else sb.append("total time=n/a\n"); if(detailed) { if(network_send > 0) sb.append("network_send=").append(network_send).append('\n'); if(network_recv > 0) sb.append("network_recv=").append(network_recv).append('\n'); } if(network_time > 0) sb.append("network=").append(network_time).append('\n'); sb.append("\nDOWN\n-----\n"); if(prots != null) { for(int i=0; i < prots.size(); i++) { p=(Protocol)prots.elementAt(i); key=p.getName(); val=(PerfEntry)down.get(key); sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); } } else for(Iterator it=down.keySet().iterator(); it.hasNext();) { key=(String)it.next(); val=(PerfEntry)down.get(key); sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); } sb.append("\nUP\n-----\n"); if(prots != null) { for(int i=prots.size() - 1; i >= 0; i--) { p=(Protocol)prots.elementAt(i); key=p.getName(); val=(PerfEntry)up.get(key); sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); } } else for(Iterator it=up.keySet().iterator(); it.hasNext();) { key=(String)it.next(); val=(PerfEntry)up.get(key); sb.append(key).append(':').append('\t').append(val.printContents(detailed)).append('\n'); } return sb.toString(); } public void setEndTime() { end_time=System.currentTimeMillis(); } public void setReceived(String prot_name, int type) { PerfEntry entry=getEntry(prot_name, type); long t=System.currentTimeMillis(); if(entry != null) entry.setReceived(t); } public void setDone(String prot_name, int type) { PerfEntry entry=getEntry(prot_name, type); long t=System.currentTimeMillis(); if(entry != null) entry.setDone(t); } public void setNetworkSent() { network_send=System.currentTimeMillis(); } public void setNetworkReceived() { network_recv=System.currentTimeMillis(); if(network_send > 0 && network_recv > network_send) network_time=network_recv - network_send; } /** * Adds a new entry to both hashtables */ public void addEntry(String prot_name) { up.put(prot_name, new PerfEntry()); down.put(prot_name, new PerfEntry()); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(sender); out.writeObject(receiver); out.writeLong(start_time); out.writeLong(end_time); out.writeLong(network_send); out.writeLong(network_recv); out.writeLong(network_time); writeHashtable(down, out); writeHashtable(up, out); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { sender=in.readObject(); receiver=in.readObject(); start_time=in.readLong(); end_time=in.readLong(); network_send=in.readLong(); network_recv=in.readLong(); network_time=in.readLong(); down=readHashtable(in); up=readHashtable(in); } public long size() { return size; } void writeHashtable(HashMap h, ObjectOutput out) { String key; PerfEntry val; try { if(h == null) { out.writeInt(0); return; } out.writeInt(h.size()); for(Iterator it=h.keySet().iterator(); it.hasNext();) { key=(String)it.next(); val=(PerfEntry)h.get(key); if(key == null || val == null) { System.err.println("PerfHeader.writeHashtable(): key or val is null"); continue; } out.writeObject(key); out.writeObject(val); } } catch(Exception ex) { System.err.println("PerfHeader.writeHashtable(): " + ex); } } HashMap readHashtable(ObjectInput in) { HashMap h=new HashMap(); int num=0; String key; PerfEntry val; try { num=in.readInt(); if(num == 0) return h; for(int i=0; i < num; i++) { key=(String)in.readObject(); val=(PerfEntry)in.readObject(); h.put(key, val); } } catch(Exception ex) { System.err.println("PerfHeader.readHashtable(): " + ex); } return h; } PerfEntry getEntry(String prot_name, int type) { HashMap tmp=null; PerfEntry entry=null; if(prot_name == null) return null; if(type == UP) tmp=up; else if(type == DOWN) tmp=down; if(tmp == null) return null; entry=(PerfEntry)tmp.get(prot_name); if(entry == null) log.error("PerfHeader.getEntry(): protocol \"" + prot_name + "\" not found"); return entry; } public static void main(String[] args) { PerfHeader hdr=new PerfHeader(), hdr2; Message msg; ByteArrayOutputStream out_stream; ByteArrayInputStream in_stream; ObjectOutputStream out; ObjectInputStream in; byte[] out_buf, in_buf; hdr.addEntry("GMS"); hdr.addEntry("GMS"); hdr.addEntry("FRAG"); hdr.addEntry("FRAG"); hdr.addEntry("UDP"); hdr.addEntry("UDP"); msg=new Message(); msg.putHeader("PERF", hdr); hdr.setReceived("GMS", PerfHeader.DOWN); Util.sleep(2); hdr.setDone("GMS", PerfHeader.DOWN); hdr.setReceived("FRAG", PerfHeader.DOWN); Util.sleep(20); hdr.setDone("FRAG", PerfHeader.DOWN); long len=msg.size(); System.out.println("Size is " + len); hdr.setReceived("UDP", PerfHeader.DOWN); Util.sleep(12); hdr.setDone("UDP", PerfHeader.DOWN); Util.sleep(30); hdr.setReceived("UDP", PerfHeader.UP); hdr.setDone("UDP", PerfHeader.UP); hdr.setReceived("FRAG", PerfHeader.UP); Util.sleep(23); hdr.setDone("FRAG", PerfHeader.UP); hdr.setReceived("GMS", PerfHeader.UP); Util.sleep(3); hdr.setDone("GMS", PerfHeader.UP); hdr.setEndTime(); System.out.println(hdr.printContents(true)); try { System.out.println("Saving hdr to byte buffer"); out_stream=new ByteArrayOutputStream(256); out=new ObjectOutputStream(out_stream); out.writeObject(msg); out_buf=out_stream.toByteArray(); System.out.println("Constructing hdr2 from byte buffer"); in_buf=out_buf; // ref in_stream=new ByteArrayInputStream(in_buf); in=new ObjectInputStream(in_stream); msg2=(Message)in.readObject(); hdr2=(PerfHeader)msg2.removeHeader("PERF"); System.out.println(hdr2.printContents(true)); } catch(Exception ex) { log.error(ex); } } } /** * Entry specific for 1 protocol layer. Records time message was received by that layer and when message was passed on */ class PerfEntry implements Externalizable { long received=0; long done=0; long total=-1; // Needed for externalization public PerfEntry() { } public long getReceived() { return received; } public long getDone() { return done; } public long getTotal() { return total; } public void setReceived(long r) { received=r; } public void setDone(long d) { done=d; if(received > 0 && done > 0 && done >= received) total=done - received; } public String toString() { if(total >= 0) return "time: " + total; else return "time: n/a"; } public String printContents(boolean detailed) { StringBuilder sb=new StringBuilder(); if(detailed) { if(received > 0) sb.append("received=").append(received); if(done > 0) { if(received > 0) sb.append(", "); sb.append("done=").append(done); } } if(detailed && (received > 0 || done > 0)) sb.append(", "); sb.append(toString()); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(received); out.writeLong(done); out.writeLong(total); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { received=in.readLong(); done=in.readLong(); total=in.readLong(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/PERF.java.txt0000644000175000017500000000432111366547366027462 0ustar twernertwerner// $Id: PERF.java.txt,v 1.1 2006/12/27 16:44:40 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolObserver; import java.util.Properties; import java.util.Vector; /** * Observes a protocol and adds its timings to the PerfHeader attached to each protocol. */ class PerfObserver implements ProtocolObserver { final String prot_name; boolean bottom=false; PerfObserver(String prot_name) { this.prot_name=prot_name; } public void setProtocol(Protocol prot) { if(prot != null && prot.getDownProtocol() == null) bottom=true; } public boolean up(Event evt) { PerfHeader hdr; if(evt.getType() == Event.MSG) { hdr=getPerfHeader((Message)evt.getArg()); if(hdr != null) { hdr.setReceived(prot_name, PerfHeader.UP); if(bottom) hdr.setNetworkReceived(); } } return true; } public boolean passUp(Event evt) { PerfHeader hdr; if(evt.getType() == Event.MSG) { hdr=getPerfHeader((Message)evt.getArg()); if(hdr != null) { hdr.setDone(prot_name, PerfHeader.UP); } } return true; } public boolean down(Event evt) { PerfHeader hdr; if(evt.getType() == Event.MSG) { hdr=getPerfHeader((Message)evt.getArg()); if(hdr != null) { hdr.setReceived(prot_name, PerfHeader.DOWN); } } return true; } public boolean passDown(Event evt) { PerfHeader hdr; if(evt.getType() == Event.MSG) { hdr=getPerfHeader((Message)evt.getArg()); if(hdr != null) { hdr.setDone(prot_name, PerfHeader.DOWN); if(bottom) hdr.setNetworkSent(); } } return true; } PerfHeader getPerfHeader(Message m) { Object hdr=null; if(m == null || (hdr=m.getHeader(PERF.name)) == null) return null; return (PerfHeader)hdr; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/FD_SHUN.java.txt0000644000175000017500000002176111366547366030063 0ustar twernertwerner// $Id: FD_SHUN.java.txt,v 1.3 2005/05/30 14:31:05 belaban Exp $ package org.jgroups.protocols; import java.util.Hashtable; import java.util.Properties; import java.util.Vector; class FdHeaderShun extends Header { static final int HEARTBEAT = 0; static final int HEARTBEAT_ACK = 1; static final int SUSPECT = 2; static final int NOT_MEMBER = 3; // received as response by pinged mbr when we are not a member int type=HEARTBEAT; Address suspected_mbr=null; Address from=null; // member who detected that suspected_mbr has failed FdHeaderShun(int type) {this.type=type;} public String toString() { switch(type) { case HEARTBEAT: return "[FD_SHUN: heartbeat]"; case HEARTBEAT_ACK: return "[FD_SHUN: heartbeat ack]"; case SUSPECT: return "[FD_SHUN: SUSPECT (suspected_mbr=" + suspected_mbr + ", from=" + from + ")]"; case NOT_MEMBER: return "[FD_SHUN: NOT_MEMBER]"; default: return "[FD_SHUN: unknown type (" + type + ")]"; } } } /** Failure detection based on simple heartbeat protocol. Regularly polls members for liveness. Passes SUSPECT message up the stack when a member is not reachable. The simple algorithms works as follows: the membership is known and ordered. Each HB protocol periodically sends a 'are-you-alive' message to its *neighbor*. A neighbor is the next in rank in the membership list. It is recomputed upon a view change. When a response hasn't been received for n milliseconds and m tries, the corresponding member is suspected (and eventually excluded if faulty).

    FD_SHUN starts when it detects (in a view change notification) that there are at least 2 members in the group. It stops running when the membership drops below 2.

    When a message is received from the monitored neighbor member, it causes the pinger thread to 'skip' sending the next are-you-alive message. Thus, traffic is reduced.

    When we receive a ping from a member that's not in the membership list, we shun it by sending it a NOT_MEMBER message. That member will then leave the group (and possibly rejoin). */ public class FD_SHUN extends Protocol implements Runnable { boolean trace=false; Address ping_dest=null; Address local_addr=null; Thread pinger=null; // pinger thread long timeout=3000; // number of millisecs to wait for an are-you-alive msg boolean ack_received=false, skip_heartbeat=false; int num_tries=0; int max_tries=2; // number of times to send a are-you-alive msg (tot time= max_tries*timeout) Vector members=null; Hashtable invalid_pingers=new Hashtable(); // keys=Address, val=Integer (number of pings from suspected mbrs) public String getName() {return "FD_SHUN";} public boolean setProperties(Properties props) {super.setProperties(props); String str; this.props=props; str=props.getProperty("trace"); if(str != null) { trace=new Boolean(str).booleanValue(); props.remove("trace"); } str=props.getProperty("timeout"); if(str != null) { timeout=new Long(str).longValue(); props.remove("timeout"); } str=props.getProperty("max_tries"); // before suspecting a member if(str != null) { max_tries=new Integer(str).intValue(); props.remove("max_tries"); } if(props.size() > 0) { log.error("FD_SHUN.setProperties(): the following properties are not recognized: " + props); return false; } return true; } Address getPingDest(Vector members) { Address tmp, retval=null; if(members == null || members.size() < 2 || local_addr == null) return null; for(int i=0; i < members.size(); i++) { tmp=(Address)members.elementAt(i); if(local_addr.equals(tmp)) { if(i + 1 >= members.size()) retval=(Address)members.elementAt(0); else retval=(Address)members.elementAt(i+1); break; } } return retval; } public void up(Event evt) { Message msg; FdHeaderShun hdr=null; Address sender; Object tmphdr; int num_pings=0; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: msg=(Message)evt.getArg(); tmphdr=msg.peekHeader(); if(tmphdr == null || !(tmphdr instanceof FdHeaderShun)) { if(ping_dest != null && (sender=msg.getSrc()) != null) { if(ping_dest.equals(sender)) { ack_received=true; num_tries=0; skip_heartbeat=true; } } break; // message did not originate from FD_SHUN layer, just pass up } hdr=(FdHeaderShun)msg.removeHeader(); switch(hdr.type) { case FdHeaderShun.HEARTBEAT: // heartbeat request; send heartbeat ack Address hb_sender=msg.getSrc(); Message hb_ack=new Message(msg.getSrc(), null, null); FdHeaderShun tmp_hdr=new FdHeaderShun(FdHeaderShun.HEARTBEAT_ACK); // 1. Send an ack tmp_hdr.suspected_mbr=local_addr; hb_ack.addHeader(tmp_hdr); passDown(new Event(Event.MSG, hb_ack)); // 2. If sender is not a member, send a SUSPECT to sender (after n pings received) if(hb_sender != null && members != null && !members.contains(hb_sender)) { if(invalid_pingers.containsKey(hb_sender)) { num_pings=((Integer)invalid_pingers.get(hb_sender)).intValue(); if(num_pings >= max_tries) { log.error("** FD_SHUN.up(HEARTBEAT): sender " + hb_sender + " is not member in " + members + " ! Telling it to leave group"); hb_ack=new Message(msg.getSrc(), null, null); tmp_hdr=new FdHeaderShun(FdHeaderShun.NOT_MEMBER); tmp_hdr.from=local_addr; tmp_hdr.suspected_mbr=hb_sender; hb_ack.addHeader(tmp_hdr); passDown(new Event(Event.MSG, hb_ack)); invalid_pingers.remove(hb_sender); } else { num_pings++; invalid_pingers.put(hb_sender, new Integer(num_pings)); } } else { num_pings++; invalid_pingers.put(hb_sender, new Integer(num_pings)); } } break; // don't pass up ! case FdHeaderShun.HEARTBEAT_ACK: // heartbeat ack Object suspect=hdr.suspected_mbr; if(ping_dest != null && ping_dest.equals(suspect)) { ack_received=true; num_tries=0; } else { stop(); ping_dest=(Address)getPingDest(members); if(ping_dest != null) start(); } break; case FdHeaderShun.SUSPECT: if(hdr.suspected_mbr != null) { if(trace) System.out.println("FD_SHUN.up(SUSPECT): " + hdr); passUp(new Event(Event.SUSPECT, hdr.suspected_mbr)); passDown(new Event(Event.SUSPECT, hdr.suspected_mbr)); } break; case FdHeaderShun.NOT_MEMBER: System.out.println("** FD_SHUN.up(NOT_MEMBER): I'm being shunned; exiting"); passUp(new Event(Event.EXIT)); break; } return; } passUp(evt); // pass up to the layer above us } public void down(Event evt) { Message msg; switch(evt.getType()) { case Event.STOP: stop(); passDown(evt); break; case Event.VIEW_CHANGE: synchronized(this) { stop(); View v=(View)evt.getArg(); members=(v != null) ? v.getMembers() : null; passDown(evt); ping_dest=(Address)getPingDest(members); if(ping_dest != null) start(); } break; default: passDown(evt); break; } } public void run() { Message suspect_msg, hb_req; FdHeaderShun hdr; while(ping_dest != null && pinger != null) { ack_received=false; if(!skip_heartbeat) { hb_req=new Message(ping_dest, null, null); hb_req.addHeader(new FdHeaderShun(FdHeaderShun.HEARTBEAT)); // send heartbeat request if(trace) System.out.println("FD_SHUN: sending are-you-alive msg to " + ping_dest); passDown(new Event(Event.MSG, hb_req)); } skip_heartbeat=false; Util.sleep(timeout); if(pinger == null) break; if(!ack_received && num_tries >= max_tries) { if(trace) System.out.println("FD_SHUN(" + local_addr + "): received no heartbeat ack from " + ping_dest + ", suspecting it"); hdr=new FdHeaderShun(FdHeaderShun.SUSPECT); hdr.suspected_mbr=ping_dest; hdr.from=local_addr; suspect_msg=new Message(null, null, null); // mcast SUSPECT to all members suspect_msg.addHeader(hdr); passDown(new Event(Event.MSG, suspect_msg)); members.removeElement(ping_dest); // try the next neighbor ping_dest=(Address)getPingDest(members); } else { if(trace && !skip_heartbeat) System.out.println("FD_SHUN: received heartbeat ack from " + ping_dest); num_tries++; } } } void start() { if(pinger == null) { pinger=new Thread(this, "FD_SHUN.PingerThread"); pinger.start(); } } void stop() { Thread tmp=null; if(pinger != null && pinger.isAlive()) { tmp=pinger; pinger=null; tmp.interrupt(); try {tmp.join(timeout);} catch(Exception ex) {} if(tmp.isAlive()) log.error("**** FD_SHUN.stop(): interrupted pinger thread is still alive !"); } pinger=null; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/UDP_NIO.java.txt0000644000175000017500000015154311366547366030074 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.LogicalAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.Util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.*; import java.net.*; import java.util.*; /** * Multicast transport. Similar to UDP, but binds to multiple (or all) interfaces for sending and receiving * multicast and unicast traffic.
    * The list of interfaces can be set via a property (comma-delimited list of IP addresses or "all" for all * interfaces). Note that this class only works under JDK 1.4 and higher.
    * For each of the interfaces listed we create a Listener, which listens on the group multicast address and creates * a unicast datagram socket. The address of this member is determined at startup time, and is the host name plus * a timestamp (LogicalAddress). It does not change during the lifetime of the process. The LogicalAddress contains * a list of all unicast socket addresses to which we can send back unicast messages. When we send a message, the * Listener adds the sender's return address. When we receive a message, we add that address to our routing cache, which * contains logical addresses and physical addresses. When we need to send a unicast address, we first check whether * the logical address has a physical address associated with it in the cache. If so, we send a message to that address. * If not, we send the unicast message to all physical addresses contained in the LogicalAddress.
    * UDP_NIO guarantees that - in scenarios with multiple subnets and multi-homed machines - members do see each other. * There is some overhead in multicasting the same message on multiple interfaces, and potentially sending a unicast * on multiple interfaces as well, but the advantage is that we don't need stuff like bind_addr any longer. Plus, * the unicast routing caches should ensure that unicasts are only sent via 1 interface in almost all cases. * * @author Bela Ban Oct 2003 * @version $Id: UDP_NIO.java.txt,v 1.1.10.1 2008/01/22 10:01:11 belaban Exp $ * @deprecated Use UDP instead with send_interfaces or receive_interfaces properties defined */ public class UDP_NIO extends Protocol implements Receiver { static final String name="UDP_NIO"; /** Maintains a list of Connectors, one for each interface we're listening on */ ConnectorTable ct=null; /** A List of bind addresses, we create 1 Connector for each interface */ List bind_addrs=null; /** The name of the group to which this member is connected */ String group_name=null; /** The multicast address (mcast address and port) this member uses (default: 230.1.2.3:7500) */ InetSocketAddress mcast_addr=null; /** The address of this member. Valid for the lifetime of the JVM in which this member runs */ LogicalAddress local_addr=new LogicalAddress(null, null); /** Logical address without list of physical addresses */ LogicalAddress local_addr_canonical=local_addr.copy(); /** Pre-allocated byte stream. Used for serializing datagram packets */ ByteArrayOutputStream out_stream=new ByteArrayOutputStream(65535); /** * The port to which the unicast receiver socket binds. * 0 means to bind to any (ephemeral) port */ int local_bind_port=0; int port_range=1; // 27-6-2003 bgooren, Only try one port by default /** * Whether to enable IP multicasting. If false, multiple unicast datagram * packets are sent rather than one multicast packet */ boolean ip_mcast=true; /** The time-to-live (TTL) for multicast datagram packets */ int ip_ttl=32; /** The members of this group (updated when a member joins or leaves) */ Vector members=new Vector(); /** * Header to be added to all messages sent via this protocol. It is * preallocated for efficiency */ UdpHeader udp_hdr=null; /** Send buffer size of the multicast datagram socket */ int mcast_send_buf_size=300000; /** Receive buffer size of the multicast datagram socket */ int mcast_recv_buf_size=300000; /** Send buffer size of the unicast datagram socket */ int ucast_send_buf_size=300000; /** Receive buffer size of the unicast datagram socket */ int ucast_recv_buf_size=300000; /** * If true, messages sent to self are treated specially: unicast messages are * looped back immediately, multicast messages get a local copy first and - * when the real copy arrives - it will be discarded. Useful for Window * media (non)sense * @deprecated This is used by default now */ boolean loopback=true; //todo: remove /** * Sometimes receivers are overloaded (they have to handle de-serialization etc). * Packet handler is a separate thread taking care of de-serialization, receiver * thread(s) simply put packet in queue and return immediately. Setting this to * true adds one more thread */ boolean use_packet_handler=false; /** Used by packet handler to store incoming DatagramPackets */ Queue packet_queue=null; /** * If set it will be added to local_addr. Used to implement * for example transport independent addresses */ byte[] additional_data=null; /** * Dequeues DatagramPackets from packet_queue, unmarshalls them and * calls handleIncomingUdpPacket() */ PacketHandler packet_handler=null; /** Number of bytes to allocate to receive a packet. Needs to be set to be higher than frag_size * (handle CONFIG event) */ static final int DEFAULT_RECEIVE_BUFFER_SIZE=120000; // todo: make settable and/or use CONFIG event /** * Creates the UDP_NIO protocol, and initializes the * state variables, does however not start any sockets or threads. */ public UDP_NIO() { } /** * debug only */ public String toString() { return "Protocol UDP(local address: " + local_addr + ')'; } public void receive(DatagramPacket packet) { int len=packet.getLength(); byte[] data=packet.getData(); SocketAddress sender=packet.getSocketAddress(); if(len == 4) { // received a diagnostics probe if(data[0] == 'd' && data[1] == 'i' && data[2] == 'a' && data[3] == 'g') { handleDiagnosticProbe(sender); return; } } if(trace) log.trace("received " + len + " bytes from " + sender); if(use_packet_handler && packet_queue != null) { byte[] tmp=new byte[len]; System.arraycopy(data, 0, tmp, 0, len); try { Object[] arr=new Object[]{tmp, sender}; packet_queue.add(arr); return; } catch(QueueClosedException e) { if(warn) log.warn("packet queue for packet handler thread is closed"); // pass through to handleIncomingPacket() } } handleIncomingUdpPacket(data, sender); } /* ----------------------- Receiving of MCAST UDP packets ------------------------ */ // public void run() { // DatagramPacket packet; // byte receive_buf[]=new byte[65000]; // int len; // byte[] tmp1, tmp2; // // // moved out of loop to avoid excessive object creations (bela March 8 2001) // packet=new DatagramPacket(receive_buf, receive_buf.length); // // while(mcast_receiver != null && mcast_sock != null) { // try { // packet.setData(receive_buf, 0, receive_buf.length); // mcast_sock.receive(packet); // len=packet.getLength(); // if(len == 1 && packet.getData()[0] == 0) { // if(trace) if(log.isInfoEnabled()) log.info("UDP_NIO.run()", "received dummy packet"); // continue; // } // // if(len == 4) { // received a diagnostics probe // byte[] tmp=packet.getData(); // if(tmp[0] == 'd' && tmp[1] == 'i' && tmp[2] == 'a' && tmp[3] == 'g') { // handleDiagnosticProbe(null, null); // continue; // } // } // // if(trace) // if(log.isInfoEnabled()) log.info("UDP_NIO.receive()", "received (mcast) " + packet.getLength() + " bytes from " + // packet.getAddress() + ":" + packet.getPort() + " (size=" + len + " bytes)"); // if(len > receive_buf.length) { // if(log.isErrorEnabled()) log.error("UDP_NIO.run()", "size of the received packet (" + len + ") is bigger than " + // "allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + // "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); // } // // if(Version.compareTo(packet.getData()) == false) { // if(warn) log.warn("UDP_NIO.run()", // "packet from " + packet.getAddress() + ":" + packet.getPort() + // " has different version (" + // Version.printVersionId(packet.getData(), Version.version_id.length) + // ") from ours (" + Version.printVersionId(Version.version_id) + // "). This may cause problems"); // } // // if(use_packet_handler) { // tmp1=packet.getData(); // tmp2=new byte[len]; // System.arraycopy(tmp1, 0, tmp2, 0, len); // packet_queue.add(tmp2); // } else // handleIncomingUdpPacket(packet.getData()); // } catch(SocketException sock_ex) { // if(log.isInfoEnabled()) log.info("UDP_NIO.run()", "multicast socket is closed, exception=" + sock_ex); // break; // } catch(InterruptedIOException io_ex) { // thread was interrupted // ; // go back to top of loop, where we will terminate loop // } catch(Throwable ex) { // if(log.isErrorEnabled()) log.error("UDP_NIO.run()", "exception=" + ex + ", stack trace=" + Util.printStackTrace(ex)); // Util.sleep(1000); // so we don't get into 100% cpu spinning (should NEVER happen !) // } // } // if(log.isInfoEnabled()) log.info("UDP_NIO.run()", "multicast thread terminated"); // } void handleDiagnosticProbe(SocketAddress sender) { try { byte[] diag_rsp=getDiagResponse().getBytes(); DatagramPacket rsp=new DatagramPacket(diag_rsp, 0, diag_rsp.length, sender); if(log.isInfoEnabled()) log.info("sending diag response to " + sender); ct.send(rsp); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender + ", exception=" + t); } } String getDiagResponse() { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" (").append(group_name).append(')'); sb.append(" [").append(mcast_addr).append("]\n"); sb.append("Version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); sb.append("physical addresses: ").append(local_addr.getPhysicalAddresses()).append('\n'); sb.append("members: ").append(members).append('\n'); return sb.toString(); } /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return name; } public void init() throws Exception { if(use_packet_handler) { packet_queue=new Queue(); packet_handler=new PacketHandler(); } } /** * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */ public void start() throws Exception { if(log.isInfoEnabled()) log.info("creating sockets and starting threads"); if(ct == null) { ct=new ConnectorTable(mcast_addr, DEFAULT_RECEIVE_BUFFER_SIZE, mcast_recv_buf_size, ip_mcast, this); for(Iterator it=bind_addrs.iterator(); it.hasNext();) { String bind_addr=(String)it.next(); ct.listenOn(bind_addr, local_bind_port, port_range, DEFAULT_RECEIVE_BUFFER_SIZE, ucast_recv_buf_size, ucast_send_buf_size, ip_ttl, this); } // add physical addresses to local_addr List physical_addrs=ct.getConnectorAddresses(); // must be non-null and size() >= 1 for(Iterator it=physical_addrs.iterator(); it.hasNext();) { SocketAddress address=(SocketAddress)it.next(); local_addr.addPhysicalAddress(address); } if(additional_data != null) local_addr.setAdditionalData(additional_data); ct.start(); passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); if(use_packet_handler) packet_handler.start(); } } public void stop() { if(log.isInfoEnabled()) log.info("closing sockets and stopping threads"); if(packet_handler != null) packet_handler.stop(); if(ct != null) { ct.stop(); ct=null; } local_addr.removeAllPhysicalAddresses(); } /** * Setup the Protocol instance acording to the configuration string. * The following properties are being read by the UDP protocol: *

      *
    • param mcast_addr - the multicast address to use default is 224.0.0.200 *
    • param mcast_port - (int) the port that the multicast is sent on default is 7500 *
    • param ip_mcast - (boolean) flag whether to use IP multicast - default is true *
    • param ip_ttl - Set the default time-to-live for multicast packets sent out on this socket. default is 32 *
    * @return true if no other properties are left. * false if the properties still have data in them, ie , * properties are left over and not handled by the protocol stack */ public boolean setProperties(Properties props) { String str; List exclude_list=null; String mcast_addr_name="230.8.8.8"; int mcast_port=7500; super.setProperties(props); str=props.getProperty("bind_addrs"); if(str != null) { str=str.trim(); if("all".equals(str.toLowerCase())) { try { bind_addrs=determineAllBindInterfaces(); } catch(SocketException e) { e.printStackTrace(); bind_addrs=null; } } else { bind_addrs=Util.parseCommaDelimitedStrings(str); } props.remove("bind_addrs"); } str=props.getProperty("bind_addrs_exclude"); if(str != null) { str=str.trim(); exclude_list=Util.parseCommaDelimitedStrings(str); props.remove("bind_addrs_exclude"); } str=props.getProperty("bind_port"); if(str != null) { local_bind_port=Integer.parseInt(str); props.remove("bind_port"); } str=props.getProperty("start_port"); if(str != null) { local_bind_port=Integer.parseInt(str); props.remove("start_port"); } str=props.getProperty("port_range"); if(str != null) { port_range=Integer.parseInt(str); props.remove("port_range"); } str=props.getProperty("mcast_addr"); if(str != null) { mcast_addr_name=str; props.remove("mcast_addr"); } str=props.getProperty("mcast_port"); if(str != null) { mcast_port=Integer.parseInt(str); props.remove("mcast_port"); } str=props.getProperty("ip_mcast"); if(str != null) { ip_mcast=Boolean.valueOf(str).booleanValue(); props.remove("ip_mcast"); } str=props.getProperty("ip_ttl"); if(str != null) { ip_ttl=Integer.parseInt(str); props.remove("ip_ttl"); } str=props.getProperty("mcast_send_buf_size"); if(str != null) { mcast_send_buf_size=Integer.parseInt(str); props.remove("mcast_send_buf_size"); } str=props.getProperty("mcast_recv_buf_size"); if(str != null) { mcast_recv_buf_size=Integer.parseInt(str); props.remove("mcast_recv_buf_size"); } str=props.getProperty("ucast_send_buf_size"); if(str != null) { ucast_send_buf_size=Integer.parseInt(str); props.remove("ucast_send_buf_size"); } str=props.getProperty("ucast_recv_buf_size"); if(str != null) { ucast_recv_buf_size=Integer.parseInt(str); props.remove("ucast_recv_buf_size"); } str=props.getProperty("use_packet_handler"); if(str != null) { use_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_packet_handler"); } // determine mcast_addr mcast_addr=new InetSocketAddress(mcast_addr_name, mcast_port); // handling of bind_addrs if(bind_addrs == null) bind_addrs=new ArrayList(); if(bind_addrs.size() == 0) { try { String default_bind_addr=determineDefaultBindInterface(); bind_addrs.add(default_bind_addr); } catch(SocketException ex) { if(log.isErrorEnabled()) log.error("failed determining the default bind interface: " + ex); } } if(exclude_list != null) { bind_addrs.removeAll(exclude_list); } if(bind_addrs.size() == 0) { if(log.isErrorEnabled()) log.error("no valid bind interface found, unable to listen for network traffic"); return false; } else { if(log.isInfoEnabled()) log.info("bind interfaces are " + bind_addrs); } if(props.size() > 0) { log.error("UDP_NIO.setProperties(): the following properties are not recognized: " + props); return false; } return true; } /** * This prevents the up-handler thread to be created, which essentially is superfluous: * messages are received from the network rather than from a layer below. * DON'T REMOVE ! */ public void startUpHandler() { } /** * handle the UP event. * * @param evt - the event being send from the stack */ public void up(Event evt) { passUp(evt); switch(evt.getType()) { case Event.CONFIG: passUp(evt); if(log.isInfoEnabled()) log.info("received CONFIG event: " + evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); return; } passUp(evt); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling Down). */ public void down(Event evt) { Message msg; Object dest_addr; if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond handleDownEvent(evt); return; } msg=(Message)evt.getArg(); if(udp_hdr != null && udp_hdr.channel_name != null) { // added patch by Roland Kurmann (March 20 2003) msg.putHeader(name, udp_hdr); } dest_addr=msg.getDest(); // Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). // This way, we still have performance numbers for UDP if(observer != null) observer.passDown(evt); if(dest_addr == null) { // 'null' means send to all group members if(ip_mcast == false) { //sends a separate UDP message to each address sendMultipleUdpMessages(msg, members); return; } } try { sendUdpMessage(msg); // either unicast (dest != null) or multicast (dest == null) } catch(Exception e) { if(log.isErrorEnabled()) log.error("exception=" + e + ", msg=" + msg + ", mcast_addr=" + mcast_addr); } } /*--------------------------- End of Protocol interface -------------------------- */ /* ------------------------------ Private Methods -------------------------------- */ void handleMessage(Message msg) { } /** * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because * mcast or unicast socket reads can be concurrent */ void handleIncomingUdpPacket(byte[] data, SocketAddress sender) { ByteArrayInputStream inp_stream; ObjectInputStream inp; Message msg=null; UdpHeader hdr=null; Event evt; Address dst, src; short version; try { // skip the first n bytes (default: 4), this is the version info inp_stream=new ByteArrayInputStream(data); inp=new ObjectInputStream(inp_stream); version=inp.readShort(); if(Version.compareTo(version) == false) { if(warn) log.warn("packet from " + sender + " has different version (" + version + ") from ours (" + Version.version + "). This may cause problems"); } msg=new Message(); msg.readExternal(inp); dst=msg.getDest(); src=msg.getSrc(); if(src == null) { if(log.isErrorEnabled()) log.error("sender's address is null"); } else { ((LogicalAddress)src).setPrimaryPhysicalAddress(sender); } // discard my own multicast loopback copy if((dst == null || dst.isMulticastAddress()) && src != null && local_addr.equals(src)) { if(trace) log.trace("discarded own loopback multicast packet"); // System.out.println("-- discarded " + msg.getObject()); return; } evt=new Event(Event.MSG, msg); if(trace) log.trace("Message is " + msg + ", headers are " + msg.getHeaders()); /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. * This allows e.g. PerfObserver to get the time of reception of a message */ if(observer != null) observer.up(evt, up_queue.size()); hdr=(UdpHeader)msg.removeHeader(name); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception=" + Util.getStackTrace(e)); return; } if(hdr != null) { /* Discard all messages destined for a channel with a different name */ String ch_name=null; if(hdr.channel_name != null) ch_name=hdr.channel_name; // Discard if message's group name is not the same as our group name unless the // message is a diagnosis message (special group name DIAG_GROUP) if(ch_name != null && group_name != null && !group_name.equals(ch_name) && !ch_name.equals(Util.DIAG_GROUP)) { if(warn) log.warn("discarded message from different group (" + ch_name + "). Sender was " + msg.getSrc()); return; } } passUp(evt); } /** * Send a message to the address specified in dest */ void sendUdpMessage(Message msg) throws Exception { Address dest, src; ObjectOutputStream out; byte buf[]; DatagramPacket packet; Message copy; Event evt; // for loopback messages dest=msg.getDest(); // if non-null: unicast, else multicast src=msg.getSrc(); if(src == null) { src=local_addr_canonical; // no physical addresses present msg.setSrc(src); } if(trace) log.trace("sending message to " + msg.getDest() + " (src=" + msg.getSrc() + "), headers are " + msg.getHeaders()); // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, // we will discard our own multicast message if(dest == null || dest.isMulticastAddress() || dest.equals(local_addr)) { copy=msg.copy(); copy.removeHeader(name); evt=new Event(Event.MSG, copy); /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. This allows e.g. PerfObserver to get the time of reception of a message */ if(observer != null) observer.up(evt, up_queue.size()); if(trace) log.trace("looped back local message " + copy); // System.out.println("\n-- passing up packet id=" + copy.getObject()); passUp(evt); // System.out.println("-- passed up packet id=" + copy.getObject()); if(dest != null && !dest.isMulticastAddress()) return; // it is a unicast message to myself, no need to put on the network } out_stream.reset(); out=new ObjectOutputStream(out_stream); out.writeShort(Version.version); msg.writeExternal(out); out.flush(); // needed if out buffers its output to out_stream buf=out_stream.toByteArray(); packet=new DatagramPacket(buf, buf.length, mcast_addr); //System.out.println("-- sleeping 4 secs"); // Thread.sleep(4000); // System.out.println("\n-- sending packet " + msg.getObject()); ct.send(packet); // System.out.println("-- sent " + msg.getObject()); } void sendMultipleUdpMessages(Message msg, Vector dests) { Address dest; for(int i=0; i < dests.size(); i++) { dest=(Address)dests.elementAt(i); msg.setDest(dest); try { sendUdpMessage(msg); } catch(Exception e) { if(log.isDebugEnabled()) log.debug("exception=" + e); } } } // // /** // * Workaround for the problem encountered in certains JDKs that a thread listening on a socket // * cannot be interrupted. Therefore we just send a dummy datagram packet so that the thread 'wakes up' // * and realizes it has to terminate. Should be removed when all JDKs support Thread.interrupt() on // * reads. Uses send_sock t send dummy packet, so this socket has to be still alive. // * // * @param dest The destination host. Will be local host if null // * @param port The destination port // */ // void sendDummyPacket(InetAddress dest, int port) { // DatagramPacket packet; // byte[] buf={0}; // // if(dest == null) { // try { // dest=InetAddress.getLocalHost(); // } catch(Exception e) { // } // } // // if(trace) if(log.isInfoEnabled()) log.info("UDP_NIO.sendDummyPacket()", "sending packet to " + dest + ":" + port); // // if(ucast_sock == null || dest == null) { // if(warn) log.warn("UDP_NIO.sendDummyPacket()", "send_sock was null or dest was null, cannot send dummy packet"); // return; // } // packet=new DatagramPacket(buf, buf.length, dest, port); // try { // ucast_sock.send(packet); // } catch(Throwable e) { // if(log.isErrorEnabled()) log.error("UDP_NIO.sendDummyPacket()", "exception sending dummy packet to " + // dest + ":" + port + ": " + e); // } // } void handleDownEvent(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: synchronized(members) { members.removeAllElements(); Vector tmpvec=((View)evt.getArg()).getMembers(); for(int i=0; i < tmpvec.size(); i++) members.addElement(tmpvec.elementAt(i)); } break; case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local) passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); break; case Event.CONNECT: group_name=(String)evt.getArg(); udp_hdr=new UdpHeader(group_name); // removed March 18 2003 (bela), not needed (handled by GMS) // changed July 2 2003 (bela): we discard CONNECT_OK at the GMS level anyway, this might // be needed if we run without GMS though passUp(new Event(Event.CONNECT_OK)); break; case Event.DISCONNECT: passUp(new Event(Event.DISCONNECT_OK)); break; case Event.CONFIG: if(log.isInfoEnabled()) log.info("received CONFIG event: " + evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); break; } } void handleConfigEvent(HashMap map) { if(map == null) return; if(map.containsKey("additional_data")) additional_data=(byte[])map.get("additional_data"); if(map.containsKey("send_buf_size")) { mcast_send_buf_size=((Integer)map.get("send_buf_size")).intValue(); ucast_send_buf_size=mcast_send_buf_size; } if(map.containsKey("recv_buf_size")) { mcast_recv_buf_size=((Integer)map.get("recv_buf_size")).intValue(); ucast_recv_buf_size=mcast_recv_buf_size; } } /** Return the first non-loopback interface */ public String determineDefaultBindInterface() throws SocketException { for(Enumeration en=NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface ni=(NetworkInterface)en.nextElement(); for(Enumeration en2=ni.getInetAddresses(); en2.hasMoreElements();) { InetAddress bind_addr=(InetAddress)en2.nextElement(); if(!bind_addr.isLoopbackAddress()) { return bind_addr.getHostAddress(); } } } return null; } public List determineAllBindInterfaces() throws SocketException { List ret=new ArrayList(); for(Enumeration en=NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface ni=(NetworkInterface)en.nextElement(); for(Enumeration en2=ni.getInetAddresses(); en2.hasMoreElements();) { InetAddress bind_addr=(InetAddress)en2.nextElement(); ret.add(bind_addr.getHostAddress()); } } return ret; } /* ----------------------------- End of Private Methods ---------------------------------------- */ /* ----------------------------- Inner Classes ---------------------------------------- */ /** * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up * to the higher layer (done in handleIncomingUdpPacket()). */ class PacketHandler implements Runnable { Thread t=null; public void run() { byte[] data; SocketAddress sender; while(packet_queue != null && packet_handler != null) { try { Object[] arr=(Object[])packet_queue.remove(); data=(byte[])arr[0]; sender=(SocketAddress)arr[1]; } catch(QueueClosedException closed_ex) { if(log.isInfoEnabled()) log.info("packet_handler thread terminating"); break; } handleIncomingUdpPacket(data, sender); data=null; // let's give the poor garbage collector a hand... } } void start() { if(t == null) { t=new Thread(this, "UDP_NIO.PacketHandler thread"); t.setDaemon(true); t.start(); } } void stop() { if(packet_queue != null) packet_queue.close(false); // should terminate the packet_handler thread too t=null; packet_queue=null; } } /** * Manages a multicast and unicast socket on a given interface (NIC). The multicast socket is used * to listen for incoming multicast packets, the unicast socket is used to (1) listen for incoming * unicast packets, (2) to send unicast packets and (3) to send multicast packets */ public static class Connector implements Runnable { protected Thread t=null; protected SenderThread sender_thread=null; /** Interface on which ucast_sock and mcast_sender_sock are created */ NetworkInterface bind_interface; /** Used for sending/receiving unicast/multicast packets. The reason we have to use a MulticastSocket versus a * DatagramSocket is that only MulticastSockets allow to set the interface over which a multicast * is sent: DatagramSockets consult the routing table to find the interface */ MulticastSocket mcast_sock=null; /** Local port of the mcast_sock */ SocketAddress localAddr=null; /** The receiver which handles incoming packets */ Receiver receiver=null; /** Buffer for incoming unicast packets */ protected byte[] receive_buffer=null; Queue send_queue=new Queue(); static final Log mylog=LogFactory.getLog(Connector.class); static final boolean mywarn=mylog.isWarnEnabled(); class SenderThread extends Thread { public SenderThread() { super(Util.getGlobalThreadGroup(), "SenderThread"); } public void run() { Object[] arr; byte[] buf; SocketAddress dest; while(send_queue != null) { try { arr=(Object[])send_queue.remove(); buf=(byte[])arr[0]; dest=(SocketAddress)arr[1]; mcast_sock.send(new DatagramPacket(buf, buf.length, dest)); } catch(QueueClosedException e) { break; } catch(SocketException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); } } } } public Connector(NetworkInterface bind_interface, int local_bind_port, int port_range, int receive_buffer_size, int receive_sock_buf_size, int send_sock_buf_size, int ip_ttl, Receiver receiver) throws IOException { this.bind_interface=bind_interface; this.receiver=receiver; this.receive_buffer=new byte[receive_buffer_size]; mcast_sock=createMulticastSocket(local_bind_port, port_range); // changed Bela Dec 31 2003: if loopback is disabled other members on the same machine won't be able // to receive our multicasts // mcast_sock.setLoopbackMode(true); // we don't want to receive our own multicasts mcast_sock.setReceiveBufferSize(receive_sock_buf_size); mcast_sock.setSendBufferSize(send_sock_buf_size); mcast_sock.setTimeToLive(ip_ttl); System.out.println("ttl=" + mcast_sock.getTimeToLive()); mcast_sock.setNetworkInterface(this.bind_interface); // for outgoing multicasts localAddr=mcast_sock.getLocalSocketAddress(); System.out.println("-- local_addr=" + localAddr); System.out.println("-- mcast_sock: send_bufsize=" + mcast_sock.getSendBufferSize() + ", recv_bufsize=" + mcast_sock.getReceiveBufferSize()); } public SocketAddress getLocalAddress() { return localAddr; } public NetworkInterface getBindInterface() { return bind_interface; } public void start() throws Exception { if(mcast_sock == null) throw new Exception("UDP_NIO.Connector.start(): connector has been stopped (start() cannot be called)"); if(t != null && t.isAlive()) { if(mywarn) mylog.warn("connector thread is already running"); return; } t=new Thread(this, "ConnectorThread for " + localAddr); t.setDaemon(true); t.start(); sender_thread=new SenderThread(); sender_thread.start(); } /** Stops the connector. After this call, start() cannot be called, but a new connector has to * be created */ public void stop() { if(mcast_sock != null) mcast_sock.close(); // terminates the thread if running t=null; mcast_sock=null; } /** Sends a message using mcast_sock */ public void send(DatagramPacket packet) throws Exception { //mcast_sock.send(packet); byte[] buf=(byte[])packet.getData().clone(); Object[] arr=new Object[]{buf, packet.getSocketAddress()}; send_queue.add(arr); } public void run() { DatagramPacket packet=new DatagramPacket(receive_buffer, receive_buffer.length); while(t != null) { try { packet.setData(receive_buffer, 0, receive_buffer.length); ConnectorTable.receivePacket(packet, mcast_sock, receiver); } catch(Throwable th) { if(th == null || mcast_sock == null || mcast_sock.isClosed()) break; if(mylog.isErrorEnabled()) mylog.error("[" + localAddr + "] exception=" + th); Util.sleep(300); // so we don't get into 100% cpu spinning (should NEVER happen !) } } t=null; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("local_addr=").append(localAddr).append(", mcast_group="); return sb.toString(); } // 27-6-2003 bgooren, find available port in range (start_port, start_port+port_range) private MulticastSocket createMulticastSocket(int local_bind_port, int port_range) throws IOException { MulticastSocket sock=null; int tmp_port=local_bind_port; int max_port=tmp_port + port_range; while(tmp_port <= max_port) { try { sock=new MulticastSocket(tmp_port); break; } catch(Exception bind_ex) { tmp_port++; } } if(sock == null) throw new IOException("could not create a MulticastSocket (port range: " + local_bind_port + " - " + (local_bind_port+port_range)); return sock; } } /** Manages a bunch of Connectors */ public static class ConnectorTable implements Receiver, Runnable { Thread t=null; /** Socket to receive multicast packets. Will be bound to n interfaces */ MulticastSocket mcast_sock=null; /** The multicast address which mcast_sock will join (e.g. 230.1.2.3:7500) */ InetSocketAddress mcastAddr=null; Receiver receiver=null; /** Buffer for incoming packets */ byte[] receive_buffer=null; /** Vector. A list of Connectors, one for each interface we listen on */ Vector connectors=new Vector(); boolean running=false; static final Log mylog=LogFactory.getLog(ConnectorTable.class); static final boolean mywarn=mylog.isWarnEnabled(); public ConnectorTable(InetSocketAddress mcast_addr, int receive_buffer_size, int receive_sock_buf_size, boolean ip_mcast, Receiver receiver) throws IOException { this.receiver=receiver; this.mcastAddr=mcast_addr; this.receive_buffer=new byte[receive_buffer_size]; if(ip_mcast) { mcast_sock=new MulticastSocket(mcast_addr.getPort()); // changed Bela Dec 31 2003: if loopback is disabled other members on the same machine won't be able // to receive our multicasts // mcast_sock.setLoopbackMode(true); // do not get own multicasts mcast_sock.setReceiveBufferSize(receive_sock_buf_size); } } public Receiver getReceiver() { return receiver; } public void setReceiver(Receiver receiver) { this.receiver=receiver; } /** Get all interfaces, create one Connector per interface and call start() on it */ public void start() throws Exception { Connector tmp; if(running) return; if(mcast_sock != null) { // Start the thread servicing the incoming multicasts t=new Thread(this, "ConnectorTable thread"); t.setDaemon(true); t.start(); } // Start all Connectors for(Iterator it=connectors.iterator(); it.hasNext();) { tmp=(Connector)it.next(); tmp.start(); } running=true; } public void stop() { Connector tmp; for(Iterator it=connectors.iterator(); it.hasNext();) { tmp=(Connector)it.next(); tmp.stop(); } connectors.clear(); t=null; if(mcast_sock != null) { mcast_sock.close(); mcast_sock=null; } running=false; } public void run() { // receive mcast packets on any interface of the list of interfaces we're listening on DatagramPacket p=new DatagramPacket(receive_buffer, receive_buffer.length); while(t != null && mcast_sock != null && !mcast_sock.isClosed()) { p.setData(receive_buffer, 0, receive_buffer.length); try { receivePacket(p, mcast_sock, this); } catch(Throwable th) { if(th == null || mcast_sock == null || mcast_sock.isClosed()) break; if(mylog.isErrorEnabled()) mylog.error("exception=" + th); Util.sleep(300); // so we don't get into 100% cpu spinning (should NEVER happen !) } } t=null; } /** * Returns a list of local addresses (one for each Connector) * @return List */ public List getConnectorAddresses() { Connector c; ArrayList ret=new ArrayList(); for(Iterator it=connectors.iterator(); it.hasNext();) { c=(Connector)it.next(); ret.add(c.getLocalAddress()); } return ret; } /** Sends a packet. If the destination is a multicast address, call send() on all connectors. * If destination is not null, send the message using any Connector: if we send a unicast * message, it doesn't matter to which interface we are bound; the kernel will choose the correct * interface based on the destination and the routing table. Note that the receiver will have the * interface which was chosen by the kernel to send the message as the receiver's address, so the * correct Connector will receive a possible response. * @param msg * @throws Exception */ public void send(DatagramPacket msg) throws Exception { InetAddress dest; if(msg == null) return; dest=msg.getAddress(); if(dest == null) throw new IOException("UDP_NIO.ConnectorTable.send(): destination address is null"); if(dest.isMulticastAddress()) { // send to all Connectors for(int i=0; i < connectors.size(); i++) { ((Connector)connectors.get(i)).send(msg); } } else { // send to a random connector Connector c=pickRandomConnector(connectors); c.send(msg); } } private Connector pickRandomConnector(Vector conns) { int size=conns.size(); int index=((int)(Util.random(size))) -1; return (Connector)conns.get(index); } /** * Adds the given interface address to the list of interfaces on which the receiver mcast * socket has to listen. * Also creates a new Connector. Calling this method twice on the same interface will throw an exception * @param bind_interface * @param local_port * @param port_range * @param receive_buffer_size * @throws IOException */ public void listenOn(String bind_interface, int local_port, int port_range, int receive_buffer_size, int receiver_sock_buf_size, int send_sock_buf_size, int ip_ttl, Receiver receiver) throws IOException { if(bind_interface == null) return; NetworkInterface ni=NetworkInterface.getByInetAddress(InetAddress.getByName(bind_interface)); if(ni == null) throw new IOException("UDP_NIO.ConnectorTable.listenOn(): bind interface for " + bind_interface + " not found"); Connector tmp=findConnector(ni); if(tmp != null) { if(mywarn) mylog.warn("connector for interface " + bind_interface + " is already present (will be skipped): " + tmp); return; } // 1. join the group on this interface if(mcast_sock != null) { mcast_sock.joinGroup(mcastAddr, ni); if(mylog.isInfoEnabled()) mylog.info("joining " + mcastAddr + " on interface " + ni); } // 2. create a new Connector tmp=new Connector(ni, local_port, port_range, receive_buffer_size, receiver_sock_buf_size, send_sock_buf_size, ip_ttl, receiver); connectors.add(tmp); } private Connector findConnector(NetworkInterface ni) { for(int i=0; i < connectors.size(); i++) { Connector c=(Connector)connectors.elementAt(i); if(c.getBindInterface().equals(ni)) return c; } return null; } public void receive(DatagramPacket packet) { if(receiver != null) { receiver.receive(packet); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("*** todo: implement ***"); return sb.toString(); } public static void receivePacket(DatagramPacket packet, DatagramSocket sock, Receiver receiver) throws IOException { int len; sock.receive(packet); len=packet.getLength(); if(len == 1 && packet.getData()[0] == 0) { if(mylog.isTraceEnabled()) mylog.trace("received dummy packet"); return; } if(receiver != null) receiver.receive(packet); } } public static class MyReceiver implements Receiver { ConnectorTable t=null; public MyReceiver() { } public void setConnectorTable(ConnectorTable t) { this.t=t; } public void receive(DatagramPacket packet) { System.out.println("-- received " + packet.getLength() + " bytes from " + packet.getSocketAddress()); InetAddress sender=packet.getAddress(); byte[] buf=packet.getData(); int len=packet.getLength(); String tmp=new String(buf, 0, len); if(len > 4) { if(tmp.startsWith("rsp:")) { System.out.println("-- received respose: \"" + tmp + '\"'); return; } } byte[] rsp_buf=("rsp: this is a response to " + tmp).getBytes(); DatagramPacket response=new DatagramPacket(rsp_buf, rsp_buf.length, sender, packet.getPort()); try { t.send(response); } catch(Exception e) { e.printStackTrace(); System.err.println("MyReceiver: problem sending response to " + sender); } } } public static class MulticastReceiver implements Runnable { Unmarshaller m=null; DatagramSocket sock=null; // may be DatagramSocket or MulticastSocket public void run() { // receives packet from socket // calls Unmarshaller.receive() } } public static class Unmarshaller { Queue q=null; void receive(byte[] data, SocketAddress sender) { // if (q) --> q.add() // unserialize and call handleMessage() } } static void help() { System.out.println("UDP_NIO [-help] [-bind_addrs ]"); } public static void main(String[] args) { MyReceiver r=new MyReceiver(); ConnectorTable ct; String line; InetSocketAddress mcast_addr; BufferedReader in=null; DatagramPacket packet; byte[] send_buf; int receive_buffer_size=65000; boolean ip_mcast=true; try { mcast_addr=new InetSocketAddress("230.1.2.3", 7500); ct=new ConnectorTable(mcast_addr, receive_buffer_size, 120000, ip_mcast, r); r.setConnectorTable(ct); } catch(Throwable t) { t.printStackTrace(); return; } for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); continue; } if("-bind_addrs".equals(args[i])) { while(++i < args.length && !args[i].trim().startsWith("-")) { try { ct.listenOn(args[i], 0, 1, receive_buffer_size, 120000, 12000, 32, r); } catch(IOException e) { e.printStackTrace(); return; } } } } try { ct.start(); // starts all Connectors in turn in=new BufferedReader(new InputStreamReader(System.in)); while(true) { System.out.print("> "); System.out.flush(); line=in.readLine(); if(line.startsWith("quit") || line.startsWith("exit")) break; send_buf=line.getBytes(); packet=new DatagramPacket(send_buf, send_buf.length, mcast_addr); ct.send(packet); } } catch(Exception e) { e.printStackTrace(); } finally { if(ct != null) ct.stop(); } } } interface Receiver { /** Called when data has been received on a socket. When the callback returns, the buffer will be * reused: therefore, if buf must be processed on a separate thread, it needs to be copied. * This method might be called concurrently by multiple threads, so it has to be reentrant * @param packet */ void receive(DatagramPacket packet); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/TCP.java.txt0000644000175000017500000004266611366547366027372 0ustar twernertwerner// $Id: TCP.java.txt,v 1.1 2005/06/27 08:48:12 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.blocks.ConnectionTable; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.Util; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Properties; import java.util.Vector; /** * TCP based protocol. Creates a server socket, which gives us the local address of this group member. For * each accept() on the server socket, a new thread is created that listens on the socket. * For each outgoing message m, if m.dest is in the ougoing hashtable, the associated socket will be reused * to send message, otherwise a new socket is created and put in the hashtable. * When a socket connection breaks or a member is removed from the group, the corresponding items in the * incoming and outgoing hashtables will be removed as well.
    * This functionality is in ConnectionTable, which isT used by TCP. TCP sends messages using ct.send() and * registers with the connection table to receive all incoming messages. * @author Bela Ban */ public class TCP extends Protocol implements ConnectionTable.Receiver { private ConnectionTable ct=null; protected Address local_addr=null; private String group_addr=null; private InetAddress bind_addr=null; // local IP address to bind srv sock to (m-homed systems) private InetAddress external_addr=null; // the IP address which is broadcast to other group members private int start_port=7800; // find first available port starting at this port private int end_port=0; // maximum port to bind to private final Vector members=new Vector(11); private long reaper_interval=0; // time in msecs between connection reaps private long conn_expire_time=0; // max time a conn can be idle before being reaped boolean loopback=false; // loops back msgs to self if true /** If set it will be added to local_addr. Used to implement * for example transport independent addresses */ byte[] additional_data=null; /** List the maintains the currently suspected members. This is used so we don't send too many SUSPECT * events up the stack (one per message !) */ final BoundedList suspected_mbrs=new BoundedList(20); /** Should we drop unicast messages to suspected members or not */ boolean skip_suspected_members=true; int recv_buf_size=150000; int send_buf_size=150000; int sock_conn_timeout=2000; // max time in millis for a socket creation in ConnectionTable static final String name="TCP"; static final String IGNORE_BIND_ADDRESS_PROPERTY="ignore.bind.address"; int num_msgs_sent=0, num_msgs_received=0; public TCP() { } public String toString() { return "Protocol TCP(local address: " + local_addr + ')'; } public String getName() { return "TCP"; } public long getNumMessagesSent() {return num_msgs_sent;} public long getNumMessagesReceived() {return num_msgs_received;} public int getOpenConnections() {return ct.getNumConnections();} public InetAddress getBindAddr() {return bind_addr;} public void setBindAddr(InetAddress bind_addr) {this.bind_addr=bind_addr;} public int getStartPort() {return start_port;} public void setStartPort(int start_port) {this.start_port=start_port;} public int getEndPort() {return end_port;} public void setEndPort(int end_port) {this.end_port=end_port;} public long getReaperInterval() {return reaper_interval;} public void setReaperInterval(long reaper_interval) {this.reaper_interval=reaper_interval;} public long getConnExpireTime() {return conn_expire_time;} public void setConnExpireTime(long conn_expire_time) {this.conn_expire_time=conn_expire_time;} public boolean isLoopback() {return loopback;} public void setLoopback(boolean loopback) {this.loopback=loopback;} public String printConnections() {return ct.toString();} public void resetStats() { super.resetStats(); num_msgs_sent=num_msgs_received=0; } protected final Vector getMembers() { return members; } /** DON'T REMOVE ! This prevents the up-handler thread to be created, which essentially is superfluous: messages are received from the network rather than from a layer below. */ public void startUpHandler() { ; } public void start() throws Exception { ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port); // ct.addConnectionListener(this); ct.setReceiveBufferSize(recv_buf_size); ct.setSendBufferSize(send_buf_size); ct.setSocketConnectionTimeout(sock_conn_timeout); local_addr=ct.getLocalAddress(); if(additional_data != null && local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(additional_data); passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } /** * @param reaperInterval * @param connExpireTime * @param bindAddress * @param startPort * @throws Exception * @return ConnectionTable * Sub classes overrides this method to initialize a different version of * ConnectionTable. */ protected ConnectionTable getConnectionTable(long reaperInterval, long connExpireTime, InetAddress bindAddress, InetAddress externalAddress, int startPort, int endPort) throws Exception { ConnectionTable cTable=null; if(reaperInterval == 0 && connExpireTime == 0) { cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort); } else { if(reaperInterval == 0) { reaperInterval=5000; if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + reaperInterval); } if(connExpireTime == 0) { connExpireTime=1000 * 60 * 5; if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + connExpireTime); } cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort, reaperInterval, connExpireTime); } return cTable; } public void stop() { ct.stop(); } /** Sent to destination(s) using the ConnectionTable class. */ public void down(Event evt) { Message msg; Object dest_addr; if(evt.getType() != Event.MSG) { handleDownEvent(evt); return; } msg=(Message)evt.getArg(); num_msgs_sent++; if(group_addr != null) { // added patch sent by Roland Kurmann (bela March 20 2003) /* Add header (includes channel name) */ msg.putHeader(name, new TcpHeader(group_addr)); } dest_addr=msg.getDest(); /* Because we don't call Protocol.passDown(), we notify the observer directly (e.g. PerfObserver). This way, we still have performance numbers for TCP */ if(observer != null) observer.passDown(evt); if(dest_addr == null) { // broadcast (to all members) if(group_addr == null) { if(log.isWarnEnabled()) log.warn("dest address of message is null, and " + "sending to default address fails as group_addr is null, too !" + " Discarding message."); return; } else { sendMulticastMessage(msg); // send to current membership } } else { sendUnicastMessage(msg); // send to a single member } } /** ConnectionTable.Receiver interface */ public void receive(Message msg) { TcpHeader hdr=null; Event evt=new Event(Event.MSG, msg); /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. This allows e.g. PerfObserver to get the time of reception of a message */ if(observer != null) observer.up(evt, up_queue.size()); if(log.isTraceEnabled()) log.trace("received msg " + msg); num_msgs_received++; hdr=(TcpHeader)msg.removeHeader(name); if(hdr != null) { /* Discard all messages destined for a channel with a different name */ String ch_name=null; if(hdr.group_addr != null) ch_name=hdr.group_addr; // below lines were commented as patch sent by Roland Kurmann (bela March 20 2003) // if(group_addr == null) { // if(log.isWarnEnabled()) log.warn("TCP.receive()", "group address in header was null, discarded"); // return; // } // Discard if message's group name is not the same as our group name unless the // message is a diagnosis message (special group name DIAG_GROUP) if(ch_name != null && !group_addr.equals(ch_name) && !ch_name.equals(Util.DIAG_GROUP)) { if(log.isWarnEnabled()) log.warn("discarded message from different group (" + ch_name + "). Sender was " + msg.getSrc()); return; } } passUp(evt); } // ConnectionTable.ConnectionListener interface // public void connectionOpened(Address peer_addr) { // if(log.isTraceEnabled()) log.trace("opened connection to " + peer_addr); // } // // public void connectionClosed(Address peer_addr) { // if(peer_addr != null) // if(log.isTraceEnabled()) log.trace("closed connection to " + peer_addr); // } /** Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { String str, tmp=null; super.setProperties(props); str=props.getProperty("start_port"); if(str != null) { start_port=Integer.parseInt(str); props.remove("start_port"); } str=props.getProperty("end_port"); if(str != null) { end_port=Integer.parseInt(str); props.remove("end_port"); } // PropertyPermission not granted if running in an untrusted environment with JNLP. try { tmp=System.getProperty("bind.address"); // set by JBoss if(Boolean.getBoolean(IGNORE_BIND_ADDRESS_PROPERTY)) { tmp=null; } } catch (SecurityException ex){ } if(tmp != null) str=tmp; else str=props.getProperty("bind_addr"); if(str != null) { try { bind_addr=InetAddress.getByName(str); } catch(UnknownHostException unknown) { if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); return false; } props.remove("bind_addr"); } str=props.getProperty("external_addr"); if(str != null) { try { external_addr=InetAddress.getByName(str); } catch(UnknownHostException unknown) { if(log.isFatalEnabled()) log.fatal("(external_addr): host " + str + " not known"); return false; } props.remove("external_addr"); } str=props.getProperty("reaper_interval"); if(str != null) { reaper_interval=Long.parseLong(str); props.remove("reaper_interval"); } str=props.getProperty("conn_expire_time"); if(str != null) { conn_expire_time=Long.parseLong(str); props.remove("conn_expire_time"); } str=props.getProperty("sock_conn_timeout"); if(str != null) { sock_conn_timeout=Integer.parseInt(str); props.remove("sock_conn_timeout"); } str=props.getProperty("recv_buf_size"); if(str != null) { recv_buf_size=Integer.parseInt(str); props.remove("recv_buf_size"); } str=props.getProperty("send_buf_size"); if(str != null) { send_buf_size=Integer.parseInt(str); props.remove("send_buf_size"); } str=props.getProperty("loopback"); if(str != null) { loopback=Boolean.valueOf(str).booleanValue(); props.remove("loopback"); } str=props.getProperty("skip_suspected_members"); if(str != null) { skip_suspected_members=Boolean.valueOf(str).booleanValue(); props.remove("skip_suspected_members"); } if(props.size() > 0) { log.error("TCP.setProperties(): the following properties are not recognized: " + props); return false; } return true; } /** If the sender is null, set our own address. We cannot just go ahead and set the address anyway, as we might be sending a message on behalf of someone else ! E.g. in case of retransmission, when the original sender has crashed, or in a FLUSH protocol when we have to return all unstable messages with the FLUSH_OK response. */ private void setSourceAddress(Message msg) { if(msg.getSrc() == null) msg.setSrc(local_addr); } /** Send a message to the address specified in msg.dest */ private void sendUnicastMessage(Message msg) { IpAddress dest; Message copy; Object hdr; Event evt; dest=(IpAddress)msg.getDest(); // guaranteed not to be null if(!(dest instanceof IpAddress)) { if(log.isErrorEnabled()) log.error("destination address is not of type IpAddress !"); return; } setSourceAddress(msg); /* Don't send if destination is local address. Instead, switch dst and src and put in up_queue */ if(loopback && local_addr != null && dest != null && dest.equals(local_addr)) { copy=msg.copy(); hdr=copy.getHeader(name); if(hdr != null && hdr instanceof TcpHeader) copy.removeHeader(name); copy.setSrc(local_addr); copy.setDest(local_addr); evt=new Event(Event.MSG, copy); /* Because Protocol.up() is never called by this bottommost layer, we call up() directly in the observer. This allows e.g. PerfObserver to get the time of reception of a message */ if(observer != null) observer.up(evt, up_queue.size()); passUp(evt); return; } if(log.isTraceEnabled()) log.trace("dest=" + msg.getDest() + ", hdrs:\n" + msg.printObjectHeaders()); try { if(skip_suspected_members) { if(suspected_mbrs.contains(dest)) { if(log.isTraceEnabled()) log.trace("will not send unicast message to " + dest + " as it is currently suspected"); return; } } ct.send(msg); } catch(SocketException e) { if(members.contains(dest)) { if(!suspected_mbrs.contains(dest)) { suspected_mbrs.add(dest); passUp(new Event(Event.SUSPECT, dest)); } } } } protected void sendMulticastMessage(Message msg) { Address dest; Vector mbrs=(Vector)members.clone(); for(int i=0; i < mbrs.size(); i++) { dest=(Address)mbrs.elementAt(i); msg.setDest(dest); sendUnicastMessage(msg); } } protected void handleDownEvent(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: suspected_mbrs.removeAll(); synchronized(members) { members.clear(); members.addAll(((View)evt.getArg()).getMembers()); } break; case Event.GET_LOCAL_ADDRESS: // return local address -> Event(SET_LOCAL_ADDRESS, local) passUp(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); break; case Event.CONNECT: group_addr=(String)evt.getArg(); // removed March 18 2003 (bela), not needed (handled by GMS) // Can't remove it; otherwise TCPGOSSIP breaks (bela May 8 2003) ! passUp(new Event(Event.CONNECT_OK)); break; case Event.DISCONNECT: passUp(new Event(Event.DISCONNECT_OK)); break; case Event.CONFIG: if(log.isTraceEnabled()) log.trace("received CONFIG event: " + evt.getArg()); handleConfigEvent((HashMap)evt.getArg()); break; } } void handleConfigEvent(HashMap map) { if(map == null) return; if(map.containsKey("additional_data")) additional_data=(byte[])map.get("additional_data"); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/FD_PROB.java.txt0000644000175000017500000005042111366547366030043 0ustar twernertwerner// $Id: FD_PROB.java.txt,v 1.1.4.1 2008/01/22 10:01:12 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.jgroups.util.Streamable; import java.io.*; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.Vector; /** * Probabilistic failure detection protocol based on "A Gossip-Style Failure Detection Service" * by Renesse, Minsky and Hayden.

    * Each member maintains a list of all other members: for each member P, 2 data are maintained, a heartbeat * counter and the time of the last increment of the counter. Each member periodically sends its own heartbeat * counter list to a randomly chosen member Q. Q updates its own heartbeat counter list and the associated * time (if counter was incremented). Each member periodically increments its own counter. If, when sending * its heartbeat counter list, a member P detects that another member Q's heartbeat counter was not incremented * for timeout seconds, Q will be suspected.

    * This protocol can be used both with a PBCAST *and* regular stacks. * @author Bela Ban 1999 * @version $Revision: 1.1.4.1 $ */ public class FD_PROB extends Protocol implements Runnable { Address local_addr=null; Thread hb=null; long timeout=3000; // before a member with a non updated timestamp is suspected long gossip_interval=1000; Vector members=null; final Hashtable counters=new Hashtable(); // keys=Addresses, vals=FdEntries final Hashtable invalid_pingers=new Hashtable(); // keys=Address, vals=Integer (number of pings from suspected mbrs) int max_tries=2; // number of times to send a are-you-alive msg (tot time= max_tries*timeout) public String getName() { return "FD_PROB"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("timeout"); if(str != null) { timeout=Long.parseLong(str); props.remove("timeout"); } str=props.getProperty("gossip_interval"); if(str != null) { gossip_interval=Long.parseLong(str); props.remove("gossip_interval"); } str=props.getProperty("max_tries"); if(str != null) { max_tries=Integer.parseInt(str); props.remove("max_tries"); } if(props.size() > 0) { log.error("FD_PROB.setProperties(): the following properties are not recognized: " + props); return false; } return true; } public void start() throws Exception { if(hb == null) { hb=new Thread(this, "FD_PROB.HeartbeatThread"); hb.setDaemon(true); hb.start(); } } public void stop() { Thread tmp=null; if(hb != null && hb.isAlive()) { tmp=hb; hb=null; tmp.interrupt(); try { tmp.join(timeout); } catch(Exception ex) { } } hb=null; } public Object up(Event evt) { Message msg; FdHeader hdr=null; Object obj; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address) evt.getArg(); break; case Event.MSG: msg=(Message) evt.getArg(); obj=msg.getHeader(getName()); if(obj == null || !(obj instanceof FdHeader)) { updateCounter(msg.getSrc()); // got a msg from this guy, reset its time (we heard from it now) break; } hdr=(FdHeader) msg.removeHeader(getName()); switch(hdr.type) { case FdHeader.HEARTBEAT: // heartbeat request; send heartbeat ack if(checkPingerValidity(msg.getSrc()) == false) // false == sender of heartbeat is not a member return; // 2. Update my own array of counters if(log.isInfoEnabled()) log.info("<-- HEARTBEAT from " + msg.getSrc()); updateCounters(hdr); return; // don't pass up ! case FdHeader.NOT_MEMBER: if(warn) log.warn("NOT_MEMBER: I'm being shunned; exiting"); passUp(new Event(Event.EXIT)); return; default: if(warn) log.warn("FdHeader type " + hdr.type + " not known"); return; } } passUp(evt); // pass up to the layer above us } public Object down(Event evt) { int num_mbrs; Vector excluded_mbrs; FdEntry entry; Address mbr; switch(evt.getType()) { // Start heartbeat thread when we have more than 1 member; stop it when membership drops below 2 case Event.VIEW_CHANGE: passDown(evt); synchronized(this) { View v=(View) evt.getArg(); // mark excluded members excluded_mbrs=computeExcludedMembers(members, v.getMembers()); if(excluded_mbrs != null && excluded_mbrs.size() > 0) { for(int i=0; i < excluded_mbrs.size(); i++) { mbr=(Address) excluded_mbrs.elementAt(i); entry=(FdEntry) counters.get(mbr); if(entry != null) entry.setExcluded(true); } } members=v != null ? v.getMembers() : null; if(members != null) { num_mbrs=members.size(); if(num_mbrs >= 2) { if(hb == null) { try { start(); } catch(Exception ex) { if(warn) log.warn("exception when calling start(): " + ex); } } } else stop(); } } break; default: passDown(evt); break; } } /** Loop while more than 1 member available. Choose a member randomly (not myself !) and send a heartbeat. Wait for ack. If ack not received withing timeout, mcast SUSPECT message. */ public void run() { Message hb_msg; FdHeader hdr; Address hb_dest, key; FdEntry entry; long curr_time, diff; if(log.isInfoEnabled()) log.info("heartbeat thread was started"); while(hb != null && members.size() > 1) { // 1. Get a random member P (excluding ourself) hb_dest=getHeartbeatDest(); if(hb_dest == null) { if(warn) log.warn("hb_dest is null"); Util.sleep(gossip_interval); continue; } // 2. Increment own counter entry=(FdEntry) counters.get(local_addr); if(entry == null) { entry=new FdEntry(); counters.put(local_addr, entry); } entry.incrementCounter(); // 3. Send heartbeat to P hdr=createHeader(); if(hdr == null) if(warn) log.warn("header could not be created. Heartbeat will not be sent"); else { hb_msg=new Message(hb_dest, null, null); hb_msg.putHeader(getName(), hdr); if(log.isInfoEnabled()) log.info("--> HEARTBEAT to " + hb_dest); passDown(new Event(Event.MSG, hb_msg)); } if(log.isInfoEnabled()) log.info("own counters are " + printCounters()); // 4. Suspect members from which we haven't heard for timeout msecs for(Enumeration e=counters.keys(); e.hasMoreElements();) { curr_time=System.currentTimeMillis(); key=(Address) e.nextElement(); entry=(FdEntry) counters.get(key); if(entry.getTimestamp() > 0 && (diff=curr_time - entry.getTimestamp()) >= timeout) { if(entry.excluded()) { if(diff >= 2 * timeout) { // remove members marked as 'excluded' after 2*timeout msecs counters.remove(key); if(log.isInfoEnabled()) log.info("removed " + key); } } else { if(log.isInfoEnabled()) log.info("suspecting " + key); passUp(new Event(Event.SUSPECT, key)); } } } Util.sleep(gossip_interval); } // end while if(log.isInfoEnabled()) log.info("heartbeat thread was stopped"); } /* -------------------------------- Private Methods ------------------------------- */ Address getHeartbeatDest() { Address retval=null; int r, size; Vector members_copy; if(members == null || members.size() < 2 || local_addr == null) return null; members_copy=(Vector) members.clone(); members_copy.removeElement(local_addr); // don't select myself as heartbeat destination size=members_copy.size(); r=((int) (Math.random() * (size + 1))) % size; retval=(Address) members_copy.elementAt(r); return retval; } /** Create a header containing the counters for all members */ FdHeader createHeader() { int num_mbrs=counters.size(), index=0; FdHeader ret=null; Address key; FdEntry entry; if(num_mbrs <= 0) return null; ret=new FdHeader(FdHeader.HEARTBEAT, num_mbrs); for(Enumeration e=counters.keys(); e.hasMoreElements();) { key=(Address) e.nextElement(); entry=(FdEntry) counters.get(key); if(entry.excluded()) continue; if(index >= ret.members.length) { if(warn) log.warn("index " + index + " is out of bounds (" + ret.members.length + ')'); break; } ret.members[index]=key; ret.counters[index]=entry.getCounter(); index++; } return ret; } /** Set my own counters values to max(own-counter, counter) */ void updateCounters(FdHeader hdr) { Address key; FdEntry entry; if(hdr == null || hdr.members == null || hdr.counters == null) { if(warn) log.warn("hdr is null or contains no counters"); return; } for(int i=0; i < hdr.members.length; i++) { key=hdr.members[i]; if(key == null) continue; entry=(FdEntry) counters.get(key); if(entry == null) { entry=new FdEntry(hdr.counters[i]); counters.put(key, entry); continue; } if(entry.excluded()) continue; // only update counter (and adjust timestamp) if new counter is greater then old one entry.setCounter(Math.max(entry.getCounter(), hdr.counters[i])); } } /** Resets the counter for mbr */ void updateCounter(Address mbr) { FdEntry entry; if(mbr == null) return; entry=(FdEntry) counters.get(mbr); if(entry != null) entry.setTimestamp(); } String printCounters() { StringBuilder sb=new StringBuilder(); Address mbr; FdEntry entry; for(Enumeration e=counters.keys(); e.hasMoreElements();) { mbr=(Address) e.nextElement(); entry=(FdEntry) counters.get(mbr); sb.append("\n" + mbr + ": " + entry._toString()); } return sb.toString(); } static Vector computeExcludedMembers(Vector old_mbrship, Vector new_mbrship) { Vector ret=new Vector(); if(old_mbrship == null || new_mbrship == null) return ret; for(int i=0; i < old_mbrship.size(); i++) if(!new_mbrship.contains(old_mbrship.elementAt(i))) ret.addElement(old_mbrship.elementAt(i)); return ret; } /** If hb_sender is not a member, send a SUSPECT to sender (after n pings received) */ boolean checkPingerValidity(Object hb_sender) { int num_pings=0; Message shun_msg; Header hdr; if(hb_sender != null && members != null && !members.contains(hb_sender)) { if(invalid_pingers.containsKey(hb_sender)) { num_pings=((Integer) invalid_pingers.get(hb_sender)).intValue(); if(num_pings >= max_tries) { if(log.isErrorEnabled()) log.error("sender " + hb_sender + " is not member in " + members + " ! Telling it to leave group"); shun_msg=new Message((Address) hb_sender, null, null); hdr=new FdHeader(FdHeader.NOT_MEMBER); shun_msg.putHeader(getName(), hdr); passDown(new Event(Event.MSG, shun_msg)); invalid_pingers.remove(hb_sender); } else { num_pings++; invalid_pingers.put(hb_sender, new Integer(num_pings)); } } else { num_pings++; invalid_pingers.put(hb_sender, new Integer(num_pings)); } return false; } else return true; } /* ----------------------------- End of Private Methods --------------------------- */ public static class FdHeader extends Header implements Streamable { static final byte HEARTBEAT=1; // sent periodically to a random member static final byte NOT_MEMBER=2; // sent to the sender, when it is not a member anymore (shunned) byte type=HEARTBEAT; Address[] members=null; long[] counters=null; // correlates with 'members' (same indexes) public FdHeader() { } // used for externalization FdHeader(byte type) { this.type=type; } FdHeader(byte type, int num_elements) { this(type); members=new Address[num_elements]; counters=new long[num_elements]; } public String toString() { switch(type) { case HEARTBEAT: return "[FD_PROB: HEARTBEAT]"; case NOT_MEMBER: return "[FD_PROB: NOT_MEMBER]"; default: return "[FD_PROB: unknown type (" + type + ")]"; } } public String printDetails() { StringBuilder sb=new StringBuilder(); Address mbr; if(members != null && counters != null) for(int i=0; i < members.length; i++) { mbr=members[i]; if(mbr == null) sb.append("\n"); else sb.append("\n" + mbr); sb.append(": " + counters[i]); } return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); if(members != null) { out.writeInt(members.length); out.writeObject(members); } else out.writeInt(0); if(counters != null) { out.writeInt(counters.length); for(int i=0; i < counters.length; i++) out.writeLong(counters[i]); } else out.writeInt(0); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int num; type=in.readByte(); num=in.readInt(); if(num == 0) members=null; else { members=(Address[]) in.readObject(); } num=in.readInt(); if(num == 0) counters=null; else { counters=new long[num]; for(int i=0; i < counters.length; i++) counters[i]=in.readLong(); } } public long size() { long retval=Global.BYTE_SIZE; retval+=Global.SHORT_SIZE; // number of members if(members != null && members.length > 0) { for(int i=0; i < members.length; i++) { Address member=members[i]; retval+=Util.size(member); } } retval+=Global.SHORT_SIZE; // counters if(counters != null && counters.length > 0) { retval+=counters.length * Global.LONG_SIZE; } return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); if(members == null || members.length == 0) out.writeShort(0); else { out.writeShort(members.length); for(int i=0; i < members.length; i++) { Address member=members[i]; Util.writeAddress(member, out); } } if(counters == null || counters.length == 0) { out.writeShort(0); } else { out.writeShort(counters.length); for(int i=0; i < counters.length; i++) { long counter=counters[i]; out.writeLong(counter); } } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); short len=in.readShort(); if(len > 0) { members=new Address[len]; for(int i=0; i < len; i++) { members[i]=Util.readAddress(in); } } len=in.readShort(); if(len > 0) { counters=new long[len]; for(int i=0; i < counters.length; i++) { counters[i]=in.readLong(); } } } } private static class FdEntry { private long counter=0; // heartbeat counter private long timestamp=0; // last time the counter was incremented private boolean excluded=false; // set to true if member was excluded from group FdEntry() { } FdEntry(long counter) { this.counter=counter; timestamp=System.currentTimeMillis(); } long getCounter() { return counter; } long getTimestamp() { return timestamp; } boolean excluded() { return excluded; } synchronized void setCounter(long new_counter) { if(new_counter > counter) { // only set time if counter was incremented timestamp=System.currentTimeMillis(); counter=new_counter; } } synchronized void incrementCounter() { counter++; timestamp=System.currentTimeMillis(); } synchronized void setTimestamp() { timestamp=System.currentTimeMillis(); } synchronized void setExcluded(boolean flag) { excluded=flag; } public String toString() { return "counter=" + counter + ", timestamp=" + timestamp + ", excluded=" + excluded; } public String _toString() { return "counter=" + counter + ", age=" + (System.currentTimeMillis() - timestamp) + ", excluded=" + excluded; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/obsolete/ENCRYPT1_4.java.txt0000644000175000017500000004721011366547366030362 0ustar twernertwerner// changes made by mandar // Added .* imports // replacing SecretKey with SecretKey // $Id: ENCRYPT1_4.java.txt,v 1.1 2007/01/11 11:42:28 belaban Exp $ package org.jgroups.protocols.obsolete; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.protocols.PingRsp; import org.jgroups.stack.Protocol; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.security.*; import java.security.spec.X509EncodedKeySpec; import java.util.Properties; import java.util.Vector; /** * ENCRYPT1_4 layer. Encrypt and decrypt the group communication in JGroups */ public class ENCRYPT1_4 extends Protocol { public static class EncryptHeader extends org.jgroups.Header { int type; static final int ENCRYPT=0; static final int KEY_REQUEST=1; static final int SERVER_PUBKEY=2; static final int SECRETKEY=3; static final int SECRETKEY_READY=4; // adding key for Message object purpose static final String KEY="encrypt"; public EncryptHeader(){} public EncryptHeader(int type) { this.type=type; } public void writeExternal(java.io.ObjectOutput out) throws IOException { out.writeInt(type); } public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException { type=in.readInt(); } public String toString() { return "[ENCTYPT: ]"; } } Address local_addr=null; Address keyServerAddr=null; boolean keyServer=false; String asymAlgorithm="RSA"; String symAlgorithm="DES/ECB/PKCS5Padding"; int asymInit=512; // initial public/private key length int symInit=56; // initial shared key length // for public/private Key KeyPair Kpair; // to store own's public/private Key SecretKey desKey=null; final PublicKey pubKey=null; // for server to store the temporary client public key PublicKey serverPubKey=null; // for client to store server's public Key Cipher cipher; Cipher rsa; final Vector members=new Vector(); final Vector notReady=new Vector(); public ENCRYPT1_4() { //Provider prov = Security.getProvider("SUN"); //Security.addProvider(prov); } public String getName() { return "ENCRYPT1_4"; } /* * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding" */ private static String getAlgorithm(String s) { int index=s.indexOf("/"); if(index == -1) return s; return s.substring(0, index); } public boolean setProperties(Properties props) { String str; super.setProperties(props); // asymmetric key length str=props.getProperty("asymInit"); if(str != null) { asymInit=Integer.parseInt(str); props.remove("asymInit"); if(log.isInfoEnabled()) log.info("Asym algo bits used is " + asymInit); } // symmetric key length str=props.getProperty("symInit"); if(str != null) { symInit=Integer.parseInt(str); props.remove("symInit"); if(log.isInfoEnabled()) log.info("Sym algo bits used is " + symInit); } // asymmetric algorithm name str=props.getProperty("asymAlgorithm"); if(str != null) { asymAlgorithm=str; props.remove("asymAlgorithm"); if(log.isInfoEnabled()) log.info("Asym algo used is " + asymAlgorithm); } // symmetric algorithm name str=props.getProperty("symAlgorithm"); if(str != null) { symAlgorithm=str; props.remove("symAlgorithm"); if(log.isInfoEnabled()) log.info("Sym algo used is " + symAlgorithm); } if(props.size() > 0) { if(log.isErrorEnabled()) log.error("these properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { // generate keys according to the specified algorithms // generate publicKey and Private Key using RSA KeyPairGenerator KpairGen=KeyPairGenerator.getInstance(getAlgorithm(asymAlgorithm)); KpairGen.initialize(asymInit, new SecureRandom()); Kpair=KpairGen.generateKeyPair(); // generate secret key KeyGenerator keyGen=KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); keyGen.init(symInit); desKey=keyGen.generateKey(); // initialize for rsa, cipher encryption/decryption rsa=Cipher.getInstance(asymAlgorithm); cipher=Cipher.getInstance(symAlgorithm); if(log.isInfoEnabled()) log.info(" Both asym and sym algo initialized with the single shared key"); } /** Just remove if you don't need to reset any state */ public static void reset() { } public void up(Event evt) { Message msg; Message newMsg; EncryptHeader hdr; if(log.isInfoEnabled()) log.info("Event going up is " + evt); switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: if(log.isInfoEnabled()) log.info("Set address call"); local_addr=(Address)evt.getArg(); break; case Event.FIND_INITIAL_MBRS_OK: Vector member=(Vector)evt.getArg(); if(log.isInfoEnabled()) log.info("FIND_INIT members call, left members are " + member.size()); // this check is required, to prevent keyServer= false when adding itself if (!keyServer) keyServer=member.size() <= 0; if(member != null && member.size() > 0) keyServerAddr=((PingRsp) member.firstElement()).coord_addr; else keyServerAddr=local_addr; if(!keyServer) { desKey=null; if(log.isDebugEnabled()) log.debug("This is not keyserver, deskey set to null"); // client send clien's public key to server and request server's public key newMsg=new Message(keyServerAddr, local_addr, Kpair.getPublic().getEncoded()); // making changes (MANDAR) newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.KEY_REQUEST)); passDown(new Event(Event.MSG, newMsg)); } if(log.isInfoEnabled()) log.info("Done parsing for encrypt headers, sending upwards" + evt); passUp(evt); return; case Event.MSG: msg=(Message) evt.getArg(); if(log.isInfoEnabled()) log.info("This is a message from peer, not control header" + msg); // making changes (MANDAR) if(msg == null) { if(log.isDebugEnabled()) log.debug("Null message"); passUp(evt); return; } // making changes (MANDAR) //Object obj=msg.peekHeader(); Object obj=msg.removeHeader(EncryptHeader.KEY); if(log.isInfoEnabled()) log.info("Stripping the required protocol header"); // if not encrypted message, pass up if(obj == null || !(obj instanceof EncryptHeader)) { if(log.isInfoEnabled()) log.info("Dropping package as ENCRYPT1_4 protocol is not been recognized, msg will not be passed up"); // BELA comment this out in case U think otherwise //passUp(evt); return; } // making changes (MANDAR) //hdr = (EncryptHeader)msg.removeHeader(); hdr=(EncryptHeader) obj; if(log.isInfoEnabled()) log.info("Header received " + hdr + ':' + hdr.type); switch(hdr.type) { // key request from client and send server's public key to client case EncryptHeader.KEY_REQUEST: try { if(log.isDebugEnabled()) log.debug("Request for key"); // store the this client to notReady list using client's address notReady.addElement(msg.getSrc()); // store the client's public key for temporary PublicKey tmpPubKey=generatePubKey(msg.getBuffer()); if(log.isDebugEnabled()) log.debug("Generated requestors public key"); // send server's publicKey newMsg=new Message(msg.getSrc(), local_addr, Kpair.getPublic().getEncoded()); // making changes (MANDAR) newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.SERVER_PUBKEY)); if(log.isDebugEnabled()) log.debug("Encoded servers public key using clients public key, only client can debug it using its private key and sending it back"); passDown(new Event(Event.MSG, newMsg)); // my changes (MANDAR) rsa.init(Cipher.ENCRYPT_MODE, tmpPubKey); byte[] encryptedKey = rsa.doFinal(desKey.getEncoded()); if(log.isDebugEnabled()) log.debug(" Generated encoded key which only client can decode"); // send shared DesKey to client // 1. Decrypt desKey with server's own private Key // 2. Encrypt decrypted desKey with client's own public Key // encrypt encoded desKey using server's private key /* rsa.init(Cipher.ENCRYPT_MODE, Kpair.getPrivate()); byte[] decryptedKey=rsa.doFinal(desKey.getEncoded()); // encrypt decrypted key using client's public key rsa.init(Cipher.ENCRYPT_MODE, pubKey); byte[] encryptedKey=rsa.doFinal(decryptedKey); */ //send encrypted deskey to client newMsg=new Message(msg.getSrc(), local_addr, encryptedKey); // making changes (MANDAR) newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.SECRETKEY)); if(log.isDebugEnabled()) log.debug(" Sending encoded key to client"); passDown(new Event(Event.MSG, newMsg)); } catch(Exception e) { e.printStackTrace(); System.out.println(e + "0"); } return; case EncryptHeader.SECRETKEY_READY: //server get client's public key and generate the secret key notReady.removeElement(msg.getSrc()); if(log.isDebugEnabled()) log.debug("Removed client " + msg.getSrc() + "from notready list"); return; case EncryptHeader.SERVER_PUBKEY: serverPubKey=generatePubKey(msg.getBuffer()); if(log.isDebugEnabled()) log.debug(" Obtained the servers public key"); return; case EncryptHeader.SECRETKEY: try { // decrypt using client's private Key rsa.init(Cipher.DECRYPT_MODE, Kpair.getPrivate()); // my changes (MANDAR) byte[] encodedKey = rsa.doFinal(msg.getBuffer()); if(log.isDebugEnabled()) log.debug("generating encoded key obtained from server-admin"); /* Piece commented out by MANDAR byte[] decryptedKey=rsa.doFinal(msg.getBuffer()); // decrypt using server's public Key rsa.init(Cipher.DECRYPT_MODE, serverPubKey); byte[] encodedKey=rsa.doFinal(decryptedKey); */ // decode secretKey desKey=decodedKey(encodedKey); if(desKey == null) log.error("ohh oh !! DES key is null"); // send ready message (MANDAR) null -> "" newMsg=new Message(msg.getSrc(), local_addr, null); // making changes (MANDAR) newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.SECRETKEY_READY)); passDown(new Event(Event.MSG, newMsg)); if(log.isDebugEnabled()) log.debug("Got the deskey, sending down sec_Ready header"); } catch(Exception e) { e.printStackTrace(); System.out.println(e + "5"); } return; default: break; } if (hdr.type != 0) log.error("Error , header is not 0"); // not have shared key yet // this encrypted message is of no use, drop it if(desKey == null) return; if(log.isInfoEnabled()) log.info(" Starting to decypher messages"); // if both the shared key and incoming message are not null // decrypt the message if(msg.getBuffer() != null) { try { cipher.init(Cipher.DECRYPT_MODE, desKey); msg.setBuffer(cipher.doFinal(msg.getBuffer())); } catch(Exception e) { e.printStackTrace(); } } break; } if(log.isInfoEnabled()) log.info("Passing up event"); passUp(evt); // Pass up to the layer above us } public void down(Event evt) { Message msg; Message newMsg; boolean leave=false; if(log.isInfoEnabled()) log.info("down:evt is " + evt + ':' + evt.getType()); switch(evt.getType()) { case Event.VIEW_CHANGE: if(log.isInfoEnabled()) log.info("View change call, new member coming in"); Vector new_members=((View)evt.getArg()).getMembers(); // member size decreases: member leaves, need a new key if(members.size() > new_members.size()) leave=true; // copy member list synchronized(members) { members.removeAllElements(); if(new_members != null && new_members.size() > 0) for(int i=0; i < new_members.size(); i++) members.addElement(new_members.elementAt(i)); }// end of sync // redistribute/regain the new key because old member leaves if(leave) { // get coordinator address Object obj=members.firstElement(); // if I'm the coordinator/key-server if(obj.equals(local_addr)) { //create the new shared key and distribute keyServer=true; keyServerAddr=local_addr; // reset shared key desKey=null; if(log.isInfoEnabled()) log.info(" leave caused deskey to be null "); try { //generate new shared key KeyGenerator keyGen=KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); keyGen.init(symInit); desKey=keyGen.generateKey(); } catch(Exception e) { e.printStackTrace(); } }//end of local_addr == obj // if I'm not the coordinator/key-server else { keyServer=false; keyServerAddr=(Address)obj; // reset shared key desKey=null; // client send clien's public key to server and request server's public key newMsg=new Message(keyServerAddr, local_addr, Kpair.getPublic().getEncoded()); // making changes (MANDAR) newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader(EncryptHeader.KEY_REQUEST)); passDown(new Event(Event.MSG, newMsg)); if(log.isDebugEnabled()) log.debug("Requesting new key to be part of group"); } // end of else } break; case Event.MSG: msg= (Message) evt.getArg(); if(log.isDebugEnabled()) log.debug("Its a message call " + msg); int i; // For Server: // if some members don't have the shared key yet if(!notReady.isEmpty()) { System.out.println("not Ready list :" + notReady.toString()); if(msg.getDest() == null) { for(i=0; i < notReady.size(); i++) { // making changes (MANDAR) newMsg=new Message((Address)notReady.elementAt(i), local_addr, msg.getBuffer()); passDown(new Event(Event.MSG, newMsg)); } break; } else { for(i=0; i < notReady.size(); i++) { if(msg.getDest().equals(notReady.elementAt(i))) { passDown(evt); return; }// end of if.. }// end of for.. }// end of else } // I already know the shared key if(desKey != null) { if(log.isInfoEnabled()) log.info("DESkey is not null, I know it "); try { // if the message is not empty, encrypt it if(msg.getBuffer() != null) { cipher.init(Cipher.ENCRYPT_MODE, desKey); msg.setBuffer(cipher.doFinal(msg.getBuffer())); msg.putHeader(EncryptHeader.KEY, new EncryptHeader(0)); if(log.isInfoEnabled()) log.info(" have DES key , package sent"); } else { msg.setBuffer(null); msg.putHeader(EncryptHeader.KEY, new EncryptHeader(0)); if(log.isInfoEnabled()) log.info(" buffer null, added header"); } }catch(Exception e) { e.printStackTrace(); } } break; }// check des key.. if(log.isInfoEnabled()) log.info("Pass Down: " + evt.toString()); passDown(evt); // Pass on to the layer below us } private SecretKey decodedKey(byte[] encodedKey) { SecretKey key=null; try { //change needed mandar SecretKeyFactory KeyFac=SecretKeyFactory.getInstance(getAlgorithm(symAlgorithm)); SecretKeySpec desKeySpec=new SecretKeySpec(encodedKey, getAlgorithm(symAlgorithm)); key=KeyFac.generateSecret(desKeySpec); } catch(Exception e) { e.printStackTrace(); } return key; } private PublicKey generatePubKey(byte[] encodedKey) { PublicKey tmpPubKey=null; try { KeyFactory KeyFac=KeyFactory.getInstance(getAlgorithm(asymAlgorithm)); X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(encodedKey); tmpPubKey=KeyFac.generatePublic(x509KeySpec); } catch(Exception e) { e.printStackTrace(); } return tmpPubKey; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FragHeader.java0000644000175000017500000000300311366547366026260 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.Global; import org.jgroups.util.Streamable; import java.io.*; /** * @author Bela Ban * @version $Id: FragHeader.java,v 1.3 2007/05/01 10:55:10 belaban Exp $ */ public class FragHeader extends Header implements Streamable { public long id=0; public int frag_id=0; public int num_frags=0; public FragHeader() { } // used for externalization public FragHeader(long id, int frag_id, int num_frags) { this.id=id; this.frag_id=frag_id; this.num_frags=num_frags; } public String toString() { return "[id=" + id + ", frag_id=" + frag_id + ", num_frags=" + num_frags + ']'; } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(id); out.writeInt(frag_id); out.writeInt(num_frags); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { id=in.readLong(); frag_id=in.readInt(); num_frags=in.readInt(); } public void writeTo(DataOutputStream out) throws IOException { out.writeLong(id); out.writeInt(frag_id); out.writeInt(num_frags); } public int size() { return Global.LONG_SIZE + 2*Global.INT_SIZE; } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { id=in.readLong(); frag_id=in.readInt(); num_frags=in.readInt(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/PERF_TP.java0000644000175000017500000000760711366547366025445 0ustar twernertwerner// $Id: PERF_TP.java,v 1.20 2007/04/27 07:59:20 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.Protocol; /** * Measures the time for a message to travel from the channel to the transport * @author Bela Ban * @version $Id: PERF_TP.java,v 1.20 2007/04/27 07:59:20 belaban Exp $ */ public class PERF_TP extends Protocol { private Address local_addr=null; static volatile PERF_TP instance=null; long stop, start; long num_msgs=0; long expected_msgs=0; boolean done=false; public static PERF_TP getInstance() { return instance; } public PERF_TP() { if(instance == null) instance=this; } public String toString() { return "Protocol PERF_TP (local address: " + local_addr + ')'; } public boolean done() { return done; } public long getNumMessages() { return num_msgs; } public void setExpectedMessages(long m) { expected_msgs=m; num_msgs=0; done=false; start=System.currentTimeMillis(); } public void reset() { num_msgs=expected_msgs=stop=start=0; done=false; } public long getTotalTime() { return stop-start; } /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return "PERF_TP"; } public void init() throws Exception { local_addr=new org.jgroups.stack.IpAddress("localhost", 10000); // fake address } public void start() throws Exception { up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling Down). */ public Object down(Event evt) { Message msg; Address dest_addr; switch(evt.getType()) { case Event.MSG: if(done) { break; } msg=(Message)evt.getArg(); dest_addr=msg.getDest(); if(dest_addr == null) num_msgs++; if(num_msgs >= expected_msgs) { stop=System.currentTimeMillis(); synchronized(this) { done=true; this.notifyAll(); } if(log.isInfoEnabled()) log.info("all done (num_msgs=" + num_msgs + ", expected_msgs=" + expected_msgs); } break; case Event.CONNECT: return null; } if(getDownProtocol() != null) return down_prot.down(evt); return null; } public Object up(Event evt) { Message msg; Address dest_addr; switch(evt.getType()) { case Event.MSG: if(done) { if(log.isWarnEnabled()) log.warn("all done (discarding msg)"); break; } msg=(Message)evt.getArg(); dest_addr=msg.getDest(); if(dest_addr == null) num_msgs++; if(num_msgs >= expected_msgs) { stop=System.currentTimeMillis(); synchronized(this) { done=true; this.notifyAll(); } if(log.isInfoEnabled()) log.info("all done (num_msgs=" + num_msgs + ", expected_msgs=" + expected_msgs); } else { return up_prot.up(evt); } } return up_prot.up(evt); } /*--------------------------- End of Protocol interface -------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FlushRsp.java0000644000175000017500000000120311366547366026036 0ustar twernertwerner// $Id: FlushRsp.java,v 1.1.1.1 2003/09/09 01:24:10 belaban Exp $ package org.jgroups.protocols; import java.util.Vector; public class FlushRsp { public boolean result=true; public Vector unstable_msgs=new Vector(); public Vector failed_mbrs=null; // when result is false public FlushRsp() {} public FlushRsp(boolean result, Vector unstable_msgs, Vector failed_mbrs) { this.result=result; this.unstable_msgs=unstable_msgs; this.failed_mbrs=failed_mbrs; } public String toString() { return "result=" + result + "\nunstable_msgs=" + unstable_msgs + "\nfailed_mbrs=" + failed_mbrs; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/VERIFY_SUSPECT.java0000644000175000017500000003260711366547366026516 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.*; /** * Catches SUSPECT events traveling up the stack. Verifies that the suspected member is really dead. If yes, * passes SUSPECT event up the stack, otherwise discards it. Has to be placed somewhere above the FD layer and * below the GMS layer (receiver of the SUSPECT event). Note that SUSPECT events may be reordered by this protocol. * @author Bela Ban * @version $Id: VERIFY_SUSPECT.java,v 1.32.2.2 2008/09/03 15:13:37 vlada Exp $ */ public class VERIFY_SUSPECT extends Protocol implements Runnable { private Address local_addr=null; private long timeout=2000; // number of millisecs to wait for an are-you-dead msg private int num_msgs=1; // number of are-you-alive msgs and i-am-not-dead responses (for redundancy) final Hashtable suspects=new Hashtable(); // keys=Addresses, vals=time in mcses since added private Thread timer=null; private boolean use_icmp=false; // use InetAddress.isReachable() to double-check (rather than an are-you-alive msg) private InetAddress bind_addr; // interface for ICMP pings /** network interface to be used to send the ICMP packets */ private NetworkInterface intf=null; static final String name="VERIFY_SUSPECT"; protected boolean shutting_down=false; public String getName() { return name; } public boolean setProperties(Properties props) { super.setProperties(props); boolean ignore_systemprops=Util.isBindAddressPropertyIgnored(); String str=Util.getProperty(new String[]{Global.BIND_ADDR, Global.BIND_ADDR_OLD}, props, "bind_addr", ignore_systemprops, null); if(str != null) { try { bind_addr=InetAddress.getByName(str); } catch(UnknownHostException unknown) { if(log.isFatalEnabled()) log.fatal("(bind_addr): host " + str + " not known"); return false; } props.remove("bind_addr"); } str=props.getProperty("timeout"); if(str != null) { timeout=Long.parseLong(str); props.remove("timeout"); } str=props.getProperty("num_msgs"); if(str != null) { num_msgs=Integer.parseInt(str); if(num_msgs <= 0) { if(log.isWarnEnabled()) log.warn("num_msgs is invalid (" + num_msgs + "): setting it to 1"); num_msgs=1; } props.remove("num_msgs"); } str=props.getProperty("use_icmp"); if(str != null) { use_icmp=Boolean.valueOf(str).booleanValue(); props.remove("use_icmp"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public Object down(Event evt) { if(evt.getType() == Event.SHUTDOWN) { shutting_down=true; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); shutting_down=false; break; case Event.SUSPECT: // it all starts here ... if(shutting_down) return null; Address suspected_mbr=(Address)evt.getArg(); if(suspected_mbr == null) { if(log.isErrorEnabled()) log.error("suspected member is null"); return null; } if(local_addr != null && local_addr.equals(suspected_mbr)) { if(log.isTraceEnabled()) log.trace("I was suspected; ignoring SUSPECT message"); return null; } if(!use_icmp) verifySuspect(suspected_mbr); else verifySuspectWithICMP(suspected_mbr); return null; // don't pass up; we will decide later (after verification) whether to pass it up case Event.MSG: Message msg=(Message)evt.getArg(); VerifyHeader hdr=(VerifyHeader)msg.getHeader(name); if(hdr == null) break; if(shutting_down) return null; switch(hdr.type) { case VerifyHeader.ARE_YOU_DEAD: if(hdr.from == null) { if(log.isErrorEnabled()) log.error("ARE_YOU_DEAD: hdr.from is null"); } else { Message rsp; for(int i=0; i < num_msgs; i++) { rsp=new Message(hdr.from, null, null); rsp.setFlag(Message.OOB); rsp.putHeader(name, new VerifyHeader(VerifyHeader.I_AM_NOT_DEAD, local_addr)); down_prot.down(new Event(Event.MSG, rsp)); } } return null; case VerifyHeader.I_AM_NOT_DEAD: if(hdr.from == null) { if(log.isErrorEnabled()) log.error("I_AM_NOT_DEAD: hdr.from is null"); return null; } unsuspect(hdr.from); return null; } return null; case Event.CONFIG: if(bind_addr == null) { Map config=(Map)evt.getArg(); bind_addr=(InetAddress)config.get("bind_addr"); } } return up_prot.up(evt); } /** * Will be started when a suspect is added to the suspects hashtable. Continually iterates over the * entries and removes entries whose time have elapsed. For each removed entry, a SUSPECT event is passed * up the stack (because elapsed time means verification of member's liveness failed). Computes the shortest * time to wait (min of all timeouts) and waits(time) msecs. Will be woken up when entry is removed (in case * of successful verification of that member's liveness). Terminates when no entry remains in the hashtable. */ public void run() { long val, diff; while(!suspects.isEmpty()) { diff=0; List

    confirmed_suspects=new LinkedList
    (); synchronized(suspects) { for(Enumeration
    e=suspects.keys(); e.hasMoreElements();) { Address mbr=e.nextElement(); val=suspects.get(mbr).longValue(); diff=System.currentTimeMillis() - val; if(diff >= timeout) { // haven't been unsuspected, pass up SUSPECT if(log.isTraceEnabled()) log.trace("diff=" + diff + ", mbr " + mbr + " is dead (passing up SUSPECT event)"); confirmed_suspects.add(mbr); suspects.remove(mbr); continue; } diff=Math.max(diff, timeout - diff); } } for(Address suspect:confirmed_suspects) up_prot.up(new Event(Event.SUSPECT,suspect)); if(diff > 0) Util.sleep(diff); } } /* --------------------------------- Private Methods ----------------------------------- */ /** * Sends ARE_YOU_DEAD message to suspected_mbr, wait for return or timeout */ void verifySuspect(Address mbr) { Message msg; if(mbr == null) return; synchronized(suspects) { if(suspects.containsKey(mbr)) return; suspects.put(mbr, new Long(System.currentTimeMillis())); } //start timer before we send out are you dead messages startTimer(); // moved out of synchronized statement (bela): http://jira.jboss.com/jira/browse/JGRP-302 if(log.isTraceEnabled()) log.trace("verifying that " + mbr + " is dead"); for(int i=0;i < num_msgs;i++) { msg=new Message(mbr, null, null); msg.setFlag(Message.OOB); msg.putHeader(name, new VerifyHeader(VerifyHeader.ARE_YOU_DEAD, local_addr)); down_prot.down(new Event(Event.MSG, msg)); } } void verifySuspectWithICMP(Address suspected_mbr) { InetAddress host=suspected_mbr instanceof IpAddress? ((IpAddress)suspected_mbr).getIpAddress() : null; if(host == null) throw new IllegalArgumentException("suspected_mbr is not of type IpAddress - FD_ICMP only works with these"); try { if(log.isTraceEnabled()) log.trace("pinging host " + suspected_mbr + " using interface " + intf); long start=System.currentTimeMillis(), stop; boolean rc=host.isReachable(intf, 0, (int)timeout); stop=System.currentTimeMillis(); if(rc) { // success if(log.isTraceEnabled()) log.trace("successfully received response from " + host + " (after " + (stop-start) + "ms)"); } else { // failure if(log.isTraceEnabled()) log.debug("could not ping " + suspected_mbr + " after " + (stop-start) + "ms; " + "passing up SUSPECT event"); suspects.remove(suspected_mbr); up_prot.up(new Event(Event.SUSPECT, suspected_mbr)); } } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed pinging " + suspected_mbr, ex); } } void unsuspect(Address mbr) { if(mbr == null) return; boolean removed=false; synchronized(suspects) { if(suspects.containsKey(mbr)) { if(log.isTraceEnabled()) log.trace("member " + mbr + " is not dead !"); suspects.remove(mbr); removed=true; } } if(removed) { down_prot.down(new Event(Event.UNSUSPECT, mbr)); up_prot.up(new Event(Event.UNSUSPECT, mbr)); } } private synchronized void startTimer() { if(timer == null || !timer.isAlive()) { timer=getThreadFactory().newThread(this,"VERIFY_SUSPECT.TimerThread"); timer.setDaemon(true); timer.start(); } } public void init() throws Exception { super.init(); if(bind_addr != null) intf=NetworkInterface.getByInetAddress(bind_addr); } public void start() throws Exception { super.start(); shutting_down=false; } public synchronized void stop() { Thread tmp; if(timer != null && timer.isAlive()) { tmp=timer; timer=null; tmp.interrupt(); tmp=null; } timer=null; } /* ----------------------------- End of Private Methods -------------------------------- */ public static class VerifyHeader extends Header implements Streamable { static final short ARE_YOU_DEAD=1; // 'from' is sender of verify msg static final short I_AM_NOT_DEAD=2; // 'from' is suspected member short type=ARE_YOU_DEAD; Address from=null; // member who wants to verify that suspected_mbr is dead public VerifyHeader() { } // used for externalization VerifyHeader(short type) { this.type=type; } VerifyHeader(short type, Address from) { this(type); this.from=from; } public String toString() { switch(type) { case ARE_YOU_DEAD: return "[VERIFY_SUSPECT: ARE_YOU_DEAD]"; case I_AM_NOT_DEAD: return "[VERIFY_SUSPECT: I_AM_NOT_DEAD]"; default: return "[VERIFY_SUSPECT: unknown type (" + type + ")]"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeShort(type); out.writeObject(from); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readShort(); from=(Address)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeShort(type); Util.writeAddress(from, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readShort(); from=Util.readAddress(in); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/JMS.java0000644000175000017500000005537311366547366024742 0ustar twernertwerner// $Id: JMS.java,v 1.24.4.1 2008/07/30 12:04:52 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import javax.naming.Context; import javax.naming.InitialContext; import java.io.*; import java.util.Hashtable; import java.util.Properties; import java.util.Vector; /** * Implementation of the transport protocol using the Java Message Service (JMS). * This implementation depends on the JMS server that will distribute messages * published to the specific topic to all topic subscribers. *

    * Protocol parameters are: *

      *
    • topicName - (required), full JNDI name of the topic to be * used for message publishing; * *
    • cf - (optional), full JNDI name of the topic connection * factory that will create topic connection, default value is * "ConnectionFactory"; * *
    • jndiCtx - (optional), value of the * javax.naming.Context.INITIAL_CONTEXT_FACTORY property; you can * specify it as the JVM system property * -Djava.naming.factory.initial=factory.class.Name; * *
    • providerURL - (optional), value of the * javax.naming.Context.PROVIDER_URL property; you can specify it * as the JVM system property -Djava.naming.provider.url=some_url * *
    • ttl - (required), time to live in milliseconds. Default * value is 0, that means that messages will never expire and will be * accumulated by a JMS server. * *
    * *

    * Note, when you are using the JMS protocol, try to avoid using protocols * that open server socket connections, like FD_SOCK. I belive that FD is more * appropriate failure detector for JMS case. * * @author Roman Rokytskyy (rrokytskyy@acm.org) */ public class JMS extends Protocol implements javax.jms.MessageListener { public static final String DEFAULT_CONNECTION_FACTORY = "ConnectionFactory"; public static final String INIT_CONNECTION_FACTORY = "cf"; public static final String INIT_TOPIC_NAME = "topicName"; public static final String INIT_JNDI_CONTEXT = "jndiCtx"; public static final String INIT_PROVIDER_URL = "providerURL"; public static final String TIME_TO_LIVE = "ttl"; public static final String GROUP_NAME_PROPERTY = "jgroups_group_name"; public static final String SRC_PROPERTY = "src"; public static final String DEST_PROPERTY = "dest"; private final Vector members = new Vector(); private javax.jms.TopicConnectionFactory connectionFactory; private javax.jms.Topic topic; private javax.jms.TopicConnection connection; private javax.jms.TopicSession session; private javax.jms.TopicPublisher publisher; private javax.jms.TopicSubscriber subscriber; private String cfName; private String topicName; private String initCtxFactory; private String providerUrl; private long timeToLive; private Context ctx; private String group_addr; private Address local_addr; private Address mcast_addr; private final ByteArrayOutputStream out_stream = new ByteArrayOutputStream(65535); private static final java.util.Random RND = new java.util.Random(); /** * Empty constructor. */ public JMS() { } /** * Get the name of the protocol. * * @return always returns the "JMS" string. */ public String getName() { return "JMS"; } /** * Get the string representation of the protocol. * * @return string representation of the protocol (not very useful though). */ public String toString() { return "Protocol JMS(local address: " + local_addr + ')'; } /** * Set protocol properties. Properties are: *

      *
    • topicName - (required), full JNDI name of the topic to be * used for message publishing; * *
    • cf - (optional), full JNDI name of the topic connection * factory that will create topic connection, default value is * "ConnectionFactory"; * *
    • jndiCtx - (optional), value of the * javax.naming.Context.INITIAL_CONTEXT_FACTORY property; you can * specify it as the JVM system property * -Djava.naming.factory.initial=factory.class.Name; * *
    • providerURL - (optional), value of the * javax.naming.Context.PROVIDER_URL property; you can specify it * as the JVM system property -Djava.naming.provider.url=some_url *
    * */ public boolean setProperties(Properties props) { super.setProperties(props); cfName = props.getProperty(INIT_CONNECTION_FACTORY, DEFAULT_CONNECTION_FACTORY); props.remove(INIT_CONNECTION_FACTORY); topicName = props.getProperty(INIT_TOPIC_NAME); if (topicName == null) throw new IllegalArgumentException( "JMS topic has not been specified."); props.remove(INIT_TOPIC_NAME); initCtxFactory = props.getProperty(INIT_JNDI_CONTEXT); props.remove(INIT_JNDI_CONTEXT); providerUrl = props.getProperty(INIT_PROVIDER_URL); props.remove(INIT_PROVIDER_URL); String ttl = props.getProperty(TIME_TO_LIVE); if (ttl == null) { if(log.isErrorEnabled()) log.error("ttl property not found."); return false; } props.remove(TIME_TO_LIVE); // try to parse ttl property try { timeToLive = Long.parseLong(ttl); } catch(NumberFormatException nfex) { if(log.isErrorEnabled()) log.error("ttl property does not contain numeric value."); return false; } return props.isEmpty(); } /** * Implementation of the javax.jms.MessageListener interface. * This method receives the JMS message, checks the destination group name. * If the group name is the same as the group name of this channel, it * checks the destination address. If destination address is either * multicast or is the same as local address then message is unwrapped and * passed up the protocol stack. Otherwise it is ignored. * * @param jmsMessage instance of javax.jms.Message. */ public void onMessage(javax.jms.Message jmsMessage) { try { String groupName = jmsMessage.getStringProperty(GROUP_NAME_PROPERTY); // there might be other messages in this topic if (groupName == null) return; if(log.isDebugEnabled()) log.debug("Got message for group [" + groupName + ']' + ", my group is [" + group_addr + ']'); // not our message, ignore it if (!group_addr.equals(groupName)) return; JMSAddress src = jmsMessage.getStringProperty(SRC_PROPERTY) != null ? new JMSAddress(jmsMessage.getStringProperty(SRC_PROPERTY)) : null; JMSAddress dest = jmsMessage.getStringProperty(DEST_PROPERTY) != null ? new JMSAddress(jmsMessage.getStringProperty(DEST_PROPERTY)) : null; // if message is unicast message and I'm not the destination - ignore if (src != null && dest != null && !dest.equals(local_addr) && !dest.isMulticastAddress()) return; if (jmsMessage instanceof javax.jms.ObjectMessage) { byte[] buf = (byte[])((javax.jms.ObjectMessage)jmsMessage).getObject(); ByteArrayInputStream inp_stream=new ByteArrayInputStream(buf); DataInputStream inp=new DataInputStream(inp_stream); Message msg=new Message(); msg.readFrom(inp); Event evt=new Event(Event.MSG, msg); // +++ remove if(log.isDebugEnabled()) log.debug("Message is " + msg + ", headers are " + msg.printHeaders()); up_prot.up(evt); } } catch(javax.jms.JMSException ex) { ex.printStackTrace(); if(log.isErrorEnabled()) log.error("JMSException : " + ex.toString()); } catch(IOException ioex) { ioex.printStackTrace(); if(log.isErrorEnabled()) log.error("IOException : " + ioex.toString()); } catch(InstantiationException e) { e.printStackTrace(); } catch(IllegalAccessException e) { e.printStackTrace(); } } /** * Handle down event, if it is not a Event.MSG type. * * @param evt event to handle. */ protected Object handleDownEvent(Event evt) { switch(evt.getType()) { // we do not need this at present time, maybe in the future case Event.TMP_VIEW: case Event.VIEW_CHANGE: synchronized(members) { members.removeAllElements(); Vector tmpvec=((View)evt.getArg()).getMembers(); for(int i=0; i < tmpvec.size(); i++) members.addElement(tmpvec.elementAt(i)); } break; case Event.CONNECT: group_addr=(String)evt.getArg(); break; } return null; } /** * Called by the protocol above this. We check the event type, and if it is * message, we publish it in the topic, otherwise we let the * {@link #handleDownEvent(Event)} take care of it. * * @param evt event to process. */ public Object down(Event evt) { if(log.isTraceEnabled()) log.trace("event is " + evt + ", group_addr=" + group_addr + ", hdrs are " + Util.printEvent(evt)); // handle all non-message events if(evt.getType() != Event.MSG) { return handleDownEvent(evt); } // extract message Message msg=(Message)evt.getArg(); // publish the message to the topic sendMessage(msg); return null; } /** * Publish message in the JMS topic. We set the message source and * destination addresses if they were null. * * @param msg message to publish. */ protected void sendMessage(Message msg) { try { if (msg.getSrc() == null) msg.setSrc(local_addr); if (msg.getDest() == null) msg.setDest(mcast_addr); if(log.isInfoEnabled()) log.info("msg is " + msg); // convert the message into byte array. out_stream.reset(); DataOutputStream out=new DataOutputStream(out_stream); msg.writeTo(out); out.flush(); byte[] buf = out_stream.toByteArray(); javax.jms.ObjectMessage jmsMessage = session.createObjectMessage(); // set the payload jmsMessage.setObject(buf); // set the group name jmsMessage.setStringProperty(GROUP_NAME_PROPERTY, group_addr); // if the source is JMSAddress, copy it to the header if (msg.getSrc() instanceof JMSAddress) jmsMessage.setStringProperty( SRC_PROPERTY, msg.getSrc().toString()); // if the destination is JMSAddress, copy it to the header if (msg.getDest() instanceof JMSAddress) jmsMessage.setStringProperty( DEST_PROPERTY, msg.getDest().toString()); // publish message publisher.publish(jmsMessage); } catch(javax.jms.JMSException ex) { if(log.isErrorEnabled()) log.error("JMSException : " + ex.toString()); } catch(IOException ioex) { if(log.isErrorEnabled()) log.error("IOException : " + ioex.toString()); } } /** * Start the JMS protocol. This method instantiates the JNDI initial context * and looks up the topic connection factory and topic itself. If this step * is successful, it creates a connection to JMS server, opens a session * and obtains publisher and subscriber instances. * * @throws javax.jms.JMSException if something goes wrong with JMS. * @throws javax.naming.NamingException if something goes wrong with JNDI. * @throws IllegalArgumentException if the connection factory or topic * cannot be found under specified names. */ public void start() throws Exception { if (initCtxFactory != null && providerUrl != null) { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, initCtxFactory); env.put(Context.PROVIDER_URL, providerUrl); ctx = new InitialContext(env); } else ctx = new InitialContext(); connectionFactory = (javax.jms.TopicConnectionFactory)ctx.lookup(cfName); if (connectionFactory == null) throw new IllegalArgumentException( "Topic connection factory cannot be found in JNDI."); topic = (javax.jms.Topic)ctx.lookup(topicName); if (topic == null) throw new IllegalArgumentException("Topic cannot be found in JNDI."); connection = connectionFactory.createTopicConnection(); boolean addressAssigned = false; // check if JMS connection contains client ID, // if not, try to assign randomly generated one /*while(!addressAssigned) { if (connection.getClientID() != null) addressAssigned = true; else try { connection.setClientID(generateLocalAddress()); addressAssigned = true; } catch(javax.jms.InvalidClientIDException ex) { // duplicate... ok, let's try again } }*/ // Patch below submitted by Greg Woolsey // Check if JMS connection contains client ID, if not, try to assign randomly generated one // setClientID() must be the first method called on a new connection, per the JMS spec. // If the client ID is already set, this will throw IllegalStateException and keep the original value. while(!addressAssigned) { try { connection.setClientID(generateLocalAddress()); addressAssigned = true; } catch (javax.jms.IllegalStateException e) { // expected if connection already has a client ID. addressAssigned = true; } catch(javax.jms.InvalidClientIDException ex) { // duplicate... OK, let's try again } } local_addr = new JMSAddress(connection.getClientID(), false); mcast_addr = new JMSAddress(topicName, true); session = connection.createTopicSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE); publisher = session.createPublisher(topic); publisher.setTimeToLive(timeToLive); subscriber = session.createSubscriber(topic); subscriber.setMessageListener(this); connection.start(); up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } /** * Stops the work of the JMS protocol. This method closes JMS session and * connection and deregisters itself from the message notification. */ public void stop() { if(log.isInfoEnabled()) log.info("finishing JMS transport layer."); try { connection.stop(); subscriber.setMessageListener(null); session.close(); connection.close(); } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("exception is " + ex); } } /** * Generate random local address. This method takes host name and appends * it with randomly generated integer. * * @return randomly generated local address. */ protected String generateLocalAddress() throws java.net.UnknownHostException { String hostName = java.net.InetAddress.getLocalHost().getHostName(); int rndPort = RND.nextInt(65535); return hostName + ':' + rndPort; } /** * Simple {@link Address} representing the JMS node ID or JMS topic group. */ public static class JMSAddress implements Address { private static final long serialVersionUID = -2311584492745452246L; private String address; private boolean isMCast; /** * Empty constructor to allow externalization work. */ public JMSAddress() { } /** * Create instance of this class for given address string. * * Current implementation uses a hash mark '#' to determine * if the address is a unicast or multicast. Therefore, this character is * considered as reserved and is not allowed in the address * parameter passed to this constructor. * * @param address string representing the address of the node connected * to the JMS topic, usually, a value of * connection.getClientID(), where the connection is * instance of javax.jms.TopicConnection. * * @param isMCast true if the address is multicast address, * otherwise - false. */ JMSAddress(String address, boolean isMCast) { this.address = address; this.isMCast = isMCast; } /** * Reconstruct the address from the string representation. If the * str starts with '#', address is considered * as unicast, and node address is the substring after '#'. * Otherwise, address is multicast and address is str * itself. * * @param str string used to reconstruct the instance. */ JMSAddress(String str) { if (str.startsWith("#")) { address = str.substring(1); isMCast = false; } else { address = str; isMCast = true; } } /** * Get the node address. * * @return node address in the form passed to the constructor * {@link #JMS.JMSAddress(String, boolean)}. */ public String getAddress() { return address; } /** * Set the node address. * * @param address new node address. */ public void setAddress(String address) { this.address = address; } /** * Is the address a multicast address? * * @return true if the address is multicast address. */ public boolean isMulticastAddress() { return isMCast; } public int size() { return 22; } /** * Clone the object. */ protected Object clone() throws CloneNotSupportedException { return new JMSAddress(address, isMCast); } /** * Compare this object to o. It is possible to compare only * addresses of the same class. Also they both should be either * multicast or unicast addresses. * * @return value compliant with the {@link Comparable#compareTo(Object)} * specififaction. */ public int compareTo(Object o) throws ClassCastException { if (!(o instanceof JMSAddress)) throw new ClassCastException("Cannot compare different classes."); JMSAddress that = (JMSAddress)o; if (that.isMCast != this.isMCast) throw new ClassCastException( "Addresses are different: one is multicast, and one is not"); return this.address.compareTo(that.address); } /** * Test is this object is equal to obj. * * @return true iff the obj is * JMSAddress, node addresses are equal and they both are * either multicast or unicast addresses. */ public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof JMSAddress)) return false; JMSAddress that = (JMSAddress)obj; if (this.isMCast) return this.isMCast == that.isMCast; else return !(this.address == null || that.address == null) && this.address.equals(that.address) && this.isMCast == that.isMCast; } /** * Get the hash code of this address. * * @return hash code of this object. */ public int hashCode() { return toString().hashCode(); } /** * Read object from external input. */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { address = (String)in.readObject(); isMCast = in.readBoolean(); } /** * Get the string representation of the address. The following property * holds: a2.equals(a1) is always true, where * a2 is * JMSAddress a2 = new JMSAddress(a1.toString()); * * @return string representation of the address. */ public String toString() { return !isMCast ? '#' + address : address; } /** * Write the object to external output. */ public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(address); out.writeBoolean(isMCast); } public void writeTo(DataOutputStream outstream) throws IOException { outstream.writeUTF(address); outstream.writeBoolean(isMCast); } public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { address=instream.readUTF(); isMCast=instream.readBoolean(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TP.java.orig0000644000175000017500000024370611366547366025572 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.*; import org.jgroups.util.Queue; import org.jgroups.util.ThreadFactory; import java.io.DataInputStream; import java.io.IOException; import java.io.DataOutputStream; import java.net.*; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; /** * Generic transport - specific implementations should extend this abstract class. * Features which are provided to the subclasses include *
      *
    • version checking *
    • marshalling and unmarshalling *
    • message bundling (handling single messages, and message lists) *
    • incoming packet handler *
    • loopback *
    * A subclass has to override *
      *
    • {@link #sendToAllMembers(byte[], int, int)} *
    • {@link #sendToSingleMember(org.jgroups.Address, byte[], int, int)} *
    • {@link #init()} *
    • {@link #start()}: subclasses must call super.start() after they initialize themselves * (e.g., created their sockets). *
    • {@link #stop()}: subclasses must call super.stop() after they deinitialized themselves *
    • {@link #destroy()} *
    * The create() or start() method has to create a local address.
    of interfaces to receive multicasts on. The multicast receive socket will listen * on all of these interfaces. This is a comma-separated list of IP addresses or interface names. E.g. * "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded; we only bind to an interface once. * If this property is set, it override receive_on_all_interfaces. */ List receive_interfaces=null; /** If true, the transport should use all available interfaces to send multicast messages. This means * the same multicast message is sent N times, so use with care */ boolean send_on_all_interfaces=false; /** List of interfaces to send multicasts on. The multicast send socket will send the * same multicast message on all of these interfaces. This is a comma-separated list of IP addresses or * interface names. E.g. "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded. * If this property is set, it override send_on_all_interfaces. */ List send_interfaces=null; /** The port to which the transport binds. 0 means to bind to any (ephemeral) port */ int bind_port=0; int port_range=1; // 27-6-2003 bgooren, Only try one port by default /** The members of this group (updated when a member joins or leaves) */ final protected HashSet
    members=new HashSet
    (11); protected View view=null; final ExposedByteArrayInputStream in_stream=new ExposedByteArrayInputStream(new byte[]{'0'}); final DataInputStream dis=new DataInputStream(in_stream); /** If true, messages sent to self are treated specially: unicast messages are * looped back immediately, multicast messages get a local copy first and - * when the real copy arrives - it will be discarded. Useful for Window * media (non)sense */ boolean loopback=false; /** Discard packets with a different version. Usually minor version differences are okay. Setting this property * to true means that we expect the exact same version on all incoming packets */ protected boolean discard_incompatible_packets=false; /** Sometimes receivers are overloaded (they have to handle de-serialization etc). * Packet handler is a separate thread taking care of de-serialization, receiver * thread(s) simply put packet in queue and return immediately. Setting this to * true adds one more thread */ boolean use_incoming_packet_handler=true; /** Used by packet handler to store incoming DatagramPackets */ Queue incoming_packet_queue=null; /** Dequeues DatagramPackets from packet_queue, unmarshalls them and * calls handleIncomingUdpPacket() */ IncomingPacketHandler incoming_packet_handler=null; /** Used by packet handler to store incoming Messages */ Queue incoming_msg_queue=null; IncomingMessageHandler incoming_msg_handler; boolean use_concurrent_stack=true; ThreadGroup pool_thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "Thread Pools"); /** * Names the current thread. Valid values are "pcl": * p: include the previous (original) name, e.g. "Incoming thread-1", "UDP ucast receiver" * c: include the cluster name, e.g. "MyCluster" * l: include the local address of the current member, e.g. "192.168.5.1:5678" */ protected String thread_naming_pattern="cl"; public String getThreadNamingPattern() {return thread_naming_pattern;} /** Keeps track of connects and disconnects, in order to start and stop threads */ int connect_count=0; /** Number of times init() was called. Incremented on init(), decremented on destroy() */ int init_count=0; /** ================================== OOB thread pool ============================== */ /** The thread pool which handles OOB messages */ Executor oob_thread_pool; /** Factory which is used by oob_thread_pool */ ThreadFactory oob_thread_factory=null; boolean oob_thread_pool_enabled=true; int oob_thread_pool_min_threads=2; int oob_thread_pool_max_threads=10; /** Number of milliseconds after which an idle thread is removed */ long oob_thread_pool_keep_alive_time=30000; long num_oob_msgs_received=0; /** Used if oob_thread_pool is a ThreadPoolExecutor and oob_thread_pool_queue_enabled is true */ BlockingQueue oob_thread_pool_queue=null; /** Whether of not to use a queue with ThreadPoolExecutor (ignored with direct executor) */ boolean oob_thread_pool_queue_enabled=true; /** max number of elements in queue (bounded) */ int oob_thread_pool_queue_max_size=500; /** Possible values are "Abort", "Discard", "DiscardOldest" and "Run". These values might change once we switch to * JDK 5's java.util.concurrent package */ String oob_thread_pool_rejection_policy="Run"; public Executor getOOBThreadPool() { return oob_thread_pool; } public void setOOBThreadPool(Executor oob_thread_pool) { if(this.oob_thread_pool != null) { shutdownThreadPool(oob_thread_pool); } this.oob_thread_pool=oob_thread_pool; } public ThreadFactory getOOBThreadPoolThreadFactory() { return oob_thread_factory; } public void setOOBThreadPoolThreadFactory(ThreadFactory factory) { oob_thread_factory=factory; } /** ================================== Regular thread pool ============================== */ /** The thread pool which handles unmarshalling, version checks and dispatching of regular messages */ Executor thread_pool; /** Factory which is used by oob_thread_pool */ ThreadFactory default_thread_factory=null; boolean thread_pool_enabled=true; int thread_pool_min_threads=2; int thread_pool_max_threads=10; /** Number of milliseconds after which an idle thread is removed */ long thread_pool_keep_alive_time=30000; long num_incoming_msgs_received=0; /** Used if thread_pool is a ThreadPoolExecutor and thread_pool_queue_enabled is true */ BlockingQueue thread_pool_queue=null; /** Whether of not to use a queue with ThreadPoolExecutor (ignored with directE decutor) */ boolean thread_pool_queue_enabled=true; /** max number of elements in queue (bounded) */ int thread_pool_queue_max_size=500; /** Possible values are "Abort", "Discard", "DiscardOldest" and "Run". These values might change once we switch to * JDK 5's java.util.concurrent package */ String thread_pool_rejection_policy="Run"; public Executor getDefaultThreadPool() { return thread_pool; } public void setDefaultThreadPool(Executor thread_pool) { if(this.thread_pool != null) shutdownThreadPool(this.thread_pool); this.thread_pool=thread_pool; } public ThreadFactory getDefaultThreadPoolThreadFactory() { return default_thread_factory; } public void setDefaultThreadPoolThreadFactory(ThreadFactory factory) { default_thread_factory=factory; } /** ================================== Timer thread pool ================================= */ protected TimeScheduler timer=null; protected ThreadFactory timer_thread_factory; /** Max number of threads to be used by the timer thread pool */ int max_timer_threads=4; public ThreadFactory getTimerThreadFactory() { return timer_thread_factory; } public void setTimerThreadFactory(ThreadFactory factory) { timer_thread_factory=factory; timer.setThreadFactory(factory); } public TimeScheduler getTimer() {return timer;} /** =================================Default thread factory ================================== */ /** Used by all threads created by JGroups outside of the thread pools */ protected ThreadFactory global_thread_factory=null; public ThreadFactory getThreadFactory() { return global_thread_factory; } public void setThreadFactory(ThreadFactory factory) { global_thread_factory=factory; } /** ============================= End of default thread factory ============================== */ /** If set it will be added to local_addr. Used to implement * for example transport independent addresses */ byte[] additional_data=null; /** Maximum number of bytes for messages to be queued until they are sent. This value needs to be smaller than the largest datagram packet size in case of UDP */ int max_bundle_size=65535; /** Max number of milliseconds until queued messages are sent. Messages are sent when max_bundle_size or * max_bundle_timeout has been exceeded (whichever occurs faster) */ long max_bundle_timeout=20; /** Enable bundling of smaller messages into bigger ones */ boolean enable_bundling=false; /** Enable bundling for unicast messages. Ignored if enable_bundling is off */ boolean enable_unicast_bundling=true; private Bundler bundler=null; private DiagnosticsHandler diag_handler=null; boolean enable_diagnostics=true; String diagnostics_addr="224.0.0.75"; int diagnostics_port=7500; /** If this transport is shared, identifies all the transport instances which are to be shared */ String singleton_name=null; /** If singleton_name is enabled, this map is used to de-multiplex incoming messages according to their * cluster names (attached to the message by the transport anyway). The values are the next protocols above * the transports. */ private final ConcurrentMap up_prots=new ConcurrentHashMap(); TpHeader header; final String name=getName(); protected PortsManager pm=null; protected String persistent_ports_file=null; protected long pm_expiry_time=30000L; protected boolean persistent_ports=false; static final byte LIST = 1; // we have a list of messages rather than a single message when set static final byte MULTICAST = 2; // message is a multicast (versus a unicast) message when set static final byte OOB = 4; // message has OOB flag set (Message.OOB) long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; static NumberFormat f; private static final int INITIAL_BUFSIZE=1024; static { f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } /** * Creates the TP protocol, and initializes the * state variables, does however not start any sockets or threads. */ protected TP() { } /** * debug only */ public String toString() { return name + "(local address: " + local_addr + ')'; } public void resetStats() { num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=0; num_oob_msgs_received=num_incoming_msgs_received=0; } public long getNumMessagesSent() {return num_msgs_sent;} public long getNumMessagesReceived() {return num_msgs_received;} public long getNumBytesSent() {return num_bytes_sent;} public long getNumBytesReceived() {return num_bytes_received;} public String getBindAddress() {return bind_addr != null? bind_addr.toString() : "null";} public void setBindAddress(String bind_addr) throws UnknownHostException { this.bind_addr=InetAddress.getByName(bind_addr); } /** @deprecated Use {@link #isReceiveOnAllInterfaces()} instead */ public boolean getBindToAllInterfaces() {return receive_on_all_interfaces;} public void setBindToAllInterfaces(boolean flag) {this.receive_on_all_interfaces=flag;} public boolean isReceiveOnAllInterfaces() {return receive_on_all_interfaces;} public java.util.List getReceiveInterfaces() {return receive_interfaces;} public boolean isSendOnAllInterfaces() {return send_on_all_interfaces;} public java.util.List getSendInterfaces() {return send_interfaces;} public boolean isDiscardIncompatiblePackets() {return discard_incompatible_packets;} public void setDiscardIncompatiblePackets(boolean flag) {discard_incompatible_packets=flag;} public boolean isEnableBundling() {return enable_bundling;} public void setEnableBundling(boolean flag) {enable_bundling=flag;} public boolean isEnable_unicast_bundling() { return enable_unicast_bundling; } public void setEnable_unicast_bundling(boolean enable_unicast_bundling) { this.enable_unicast_bundling=enable_unicast_bundling; } public int getMaxBundleSize() {return max_bundle_size;} public void setMaxBundleSize(int size) {max_bundle_size=size;} public long getMaxBundleTimeout() {return max_bundle_timeout;} public void setMaxBundleTimeout(long timeout) {max_bundle_timeout=timeout;} public Address getLocalAddress() {return local_addr;} public String getChannelName() {return channel_name;} public boolean isLoopback() {return loopback;} public void setLoopback(boolean b) {loopback=b;} public boolean isUseIncomingPacketHandler() {return use_incoming_packet_handler;} public ConcurrentMap getUpProtocols() { return up_prots; } public int getOOBMinPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getCorePoolSize() : 0; } public void setOOBMinPoolSize(int size) { if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setCorePoolSize(size); } public int getOOBMaxPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getMaximumPoolSize() : 0; } public void setOOBMaxPoolSize(int size) { if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setMaximumPoolSize(size); } public int getOOBPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getPoolSize() : 0; } public long getOOBKeepAliveTime() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getKeepAliveTime(TimeUnit.MILLISECONDS) : 0; } public void setOOBKeepAliveTime(long time) { if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getOOBMessages() { return num_oob_msgs_received; } public int getOOBQueueSize() { return oob_thread_pool_queue.size(); } public int getOOBMaxQueueSize() { return oob_thread_pool_queue_max_size; } public int getIncomingMinPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getCorePoolSize() : 0; } public void setIncomingMinPoolSize(int size) { if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setCorePoolSize(size); } public int getIncomingMaxPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getMaximumPoolSize() : 0; } public void setIncomingMaxPoolSize(int size) { if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size); } public int getIncomingPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getPoolSize() : 0; } public long getIncomingKeepAliveTime() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getKeepAliveTime(TimeUnit.MILLISECONDS) : 0; } public void setIncomingKeepAliveTime(long time) { if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getIncomingMessages() { return num_incoming_msgs_received; } public int getIncomingQueueSize() { return thread_pool_queue.size(); } public int getIncomingMaxQueueSize() { return thread_pool_queue_max_size; } public Map dumpStats() { Map retval=super.dumpStats(); if(retval == null) retval=new HashMap(); retval.put("num_msgs_sent", new Long(num_msgs_sent)); retval.put("num_msgs_received", new Long(num_msgs_received)); retval.put("num_bytes_sent", new Long(num_bytes_sent)); retval.put("num_bytes_received", new Long(num_bytes_received)); return retval; } /** * Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N * messages, one for each member * @param data The data to be sent. This is not a copy, so don't modify it * @param offset * @param length * @throws Exception */ public abstract void sendToAllMembers(byte[] data, int offset, int length) throws Exception; /** * Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N * messages, one for each member * @param dest Must be a non-null unicast address * @param data The data to be sent. This is not a copy, so don't modify it * @param offset * @param length * @throws Exception */ public abstract void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception; public abstract String getInfo(); public abstract void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast); public abstract void postUnmarshallingList(Message msg, Address dest, boolean multicast); private StringBuilder _getInfo(Channel ch) { StringBuilder sb=new StringBuilder(); sb.append(ch.getLocalAddress()).append(" (").append(ch.getClusterName()).append(") ").append("\n"); sb.append("local_addr=").append(ch.getLocalAddress()).append("\n"); sb.append("group_name=").append(ch.getClusterName()).append("\n"); sb.append("version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); sb.append("view: ").append(ch.getView()).append('\n'); sb.append(getInfo()); return sb; } private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request) { if(singleton_name != null && singleton_name.length() > 0) { for(Protocol prot: up_prots.values()) { ProtocolStack st=prot.getProtocolStack(); handleDiagnosticProbe(sender, sock, request, st); } } else { handleDiagnosticProbe(sender, sock, request, stack); } } private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request, ProtocolStack stack) { try { StringTokenizer tok=new StringTokenizer(request); String req=tok.nextToken(); StringBuilder info=new StringBuilder("n/a"); if(req.trim().toLowerCase().startsWith("query")) { ArrayList l=new ArrayList(tok.countTokens()); while(tok.hasMoreTokens()) l.add(tok.nextToken().trim().toLowerCase()); info=_getInfo(stack.getChannel()); if(l.contains("jmx")) { Channel ch=stack.getChannel(); if(ch != null) { Map m=ch.dumpStats(); StringBuilder sb=new StringBuilder(); sb.append("stats:\n"); for(Iterator> it=m.entrySet().iterator(); it.hasNext();) { sb.append(it.next()).append("\n"); } info.append(sb); } } if(l.contains("props")) { String p=stack.printProtocolSpecAsXML(); info.append("\nprops:\n").append(p); } if(l.contains("info")) { Map tmp=stack.getChannel().getInfo(); info.append("INFO:\n"); for(Map.Entry entry: tmp.entrySet()) { info.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } } byte[] diag_rsp=info.toString().getBytes(); if(log.isDebugEnabled()) log.debug("sending diag response to " + sender); sendResponse(sock, sender, diag_rsp); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender, t); } } private static void sendResponse(DatagramSocket sock, SocketAddress sender, byte[] buf) throws IOException { DatagramPacket p=new DatagramPacket(buf, 0, buf.length, sender); sock.send(p); } /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ public void init() throws Exception { if(init_count++ >= 1) { return; } super.init(); // Create the default thread factory global_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); // Create the timer and the associated thread factory - depends on singleton_name // timer_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); timer_thread_factory=new LazyThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); if(singleton_name != null && singleton_name.trim().length() > 0) { ((ExtendedThreadFactory)timer_thread_factory).setIncludeClusterName(false); } default_thread_factory=new DefaultThreadFactory(pool_thread_group, "Incoming", false, true); oob_thread_factory=new DefaultThreadFactory(pool_thread_group, "OOB", false, true); setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); timer=new TimeScheduler(timer_thread_factory, max_timer_threads); verifyRejectionPolicy(oob_thread_pool_rejection_policy); verifyRejectionPolicy(thread_pool_rejection_policy); if(persistent_ports){ pm = new PortsManager(pm_expiry_time,persistent_ports_file); } if(bind_addr != null) { Map m=new HashMap(1); m.put("bind_addr", bind_addr); up(new Event(Event.CONFIG, m)); } Map map=new HashMap(); map.put("thread_naming_pattern", thread_naming_pattern); map.put("timer", timer); up(new Event(Event.INFO,map)); } public void destroy() { if(init_count == 0) return; init_count=Math.max(init_count -1, 0); if(init_count == 0) { super.destroy(); if(timer != null) { try { timer.stop(); } catch(InterruptedException e) { log.error("failed stopping the timer", e); } } } } /** * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */ public void start() throws Exception { if(timer == null) throw new Exception("timer is null"); if(enable_diagnostics) { diag_handler=new DiagnosticsHandler(); diag_handler.start(); } if(use_incoming_packet_handler && !use_concurrent_stack) { incoming_packet_queue=new Queue(); incoming_packet_handler=new IncomingPacketHandler(); incoming_packet_handler.start(); } // ========================================== OOB thread pool ============================== // create a ThreadPoolExecutor for the unmarshaller thread pool if(oob_thread_pool == null) { // only create if not yet set (e.g. by a user) if(oob_thread_pool_enabled) { if(oob_thread_pool_queue_enabled) oob_thread_pool_queue=new LinkedBlockingQueue(oob_thread_pool_queue_max_size); else oob_thread_pool_queue=new SynchronousQueue(); oob_thread_pool=createThreadPool(oob_thread_pool_min_threads, oob_thread_pool_max_threads, oob_thread_pool_keep_alive_time, oob_thread_pool_rejection_policy, oob_thread_pool_queue, oob_thread_factory); } else { // otherwise use the caller's thread to unmarshal the byte buffer into a message oob_thread_pool=new DirectExecutor(); } } // ====================================== Regular thread pool =========================== // create a ThreadPoolExecutor for the unmarshaller thread pool if(thread_pool == null) { // only create if not yet set (e.g.by a user) if(thread_pool_enabled) { if(thread_pool_queue_enabled) thread_pool_queue=new LinkedBlockingQueue(thread_pool_queue_max_size); else thread_pool_queue=new SynchronousQueue(); thread_pool=createThreadPool(thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time, thread_pool_rejection_policy, thread_pool_queue, default_thread_factory); } else { // otherwise use the caller's thread to unmarshal the byte buffer into a message thread_pool=new DirectExecutor(); } } if(loopback && !use_concurrent_stack) { incoming_msg_queue=new Queue(); incoming_msg_handler=new IncomingMessageHandler(); incoming_msg_handler.start(); } if(enable_bundling) { bundler=new Bundler(); } setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); sendUpLocalAddressEvent(); } public void stop() { if(diag_handler != null) { diag_handler.stop(); diag_handler=null; } // 1. Stop the incoming packet handler thread if(incoming_packet_handler != null) incoming_packet_handler.stop(); // 2. Stop the incoming message handler if(incoming_msg_handler != null) incoming_msg_handler.stop(); // 3. Stop the thread pools if(oob_thread_pool instanceof ThreadPoolExecutor) { shutdownThreadPool(oob_thread_pool); oob_thread_pool=null; } if(thread_pool instanceof ThreadPoolExecutor) { shutdownThreadPool(thread_pool); thread_pool=null; } } protected void handleConnect() throws Exception { connect_count++; } protected void handleDisconnect() { connect_count=Math.max(0, connect_count -1); } public String getSingletonName() { return singleton_name; } /** * Setup the Protocol instance according to the configuration string * @return true if no other properties are left. * false if the properties still have data in them, ie , * properties are left over and not handled by the protocol stack */ public boolean setProperties(Properties props) { super.setProperties(props); String str; try { bind_addr=Util.getBindAddress(props); } catch(IOException unknown) { throw new IllegalArgumentException("failed determining bind address", unknown); } str=props.getProperty("use_local_host"); if(str != null) { use_local_host=Boolean.parseBoolean(str); props.remove("use_local_host"); } str=props.getProperty("bind_to_all_interfaces"); if(str != null) { receive_on_all_interfaces=Boolean.parseBoolean(str); props.remove("bind_to_all_interfaces"); log.warn("bind_to_all_interfaces has been deprecated; use receive_on_all_interfaces instead"); } str=props.getProperty("receive_on_all_interfaces"); if(str != null) { receive_on_all_interfaces=Boolean.parseBoolean(str); props.remove("receive_on_all_interfaces"); } str=props.getProperty("receive_interfaces"); if(str != null) { try { receive_interfaces=Util.parseInterfaceList(str); props.remove("receive_interfaces"); } catch(Exception e) { log.error("error determining interfaces (" + str + ")", e); return false; } } str=props.getProperty("send_on_all_interfaces"); if(str != null) { send_on_all_interfaces=Boolean.parseBoolean(str); props.remove("send_on_all_interfaces"); } str=props.getProperty("send_interfaces"); if(str != null) { try { send_interfaces=Util.parseInterfaceList(str); props.remove("send_interfaces"); } catch(Exception e) { log.error("error determining interfaces (" + str + ")", e); return false; } } str=props.getProperty("bind_port"); if(str != null) { bind_port=Integer.parseInt(str); props.remove("bind_port"); } str=props.getProperty("port_range"); if(str != null) { port_range=Integer.parseInt(str); props.remove("port_range"); } str=props.getProperty("persistent_ports_file"); if(str != null) { persistent_ports_file=str; props.remove("persistent_ports_file"); } str=props.getProperty("ports_expiry_time"); if(str != null) { pm_expiry_time=Integer.parseInt(str); if(pm != null) pm.setExpiryTime(pm_expiry_time); props.remove("ports_expiry_time"); } str=props.getProperty("persistent_ports"); if(str != null) { if(Boolean.valueOf(str).booleanValue()) pm=new PortsManager(pm_expiry_time, persistent_ports_file); props.remove("persistent_ports"); } str=props.getProperty("loopback"); if(str != null) { loopback=Boolean.valueOf(str).booleanValue(); props.remove("loopback"); } str=props.getProperty("discard_incompatible_packets"); if(str != null) { discard_incompatible_packets=Boolean.valueOf(str).booleanValue(); props.remove("discard_incompatible_packets"); } // this is deprecated, just left for compatibility (use use_incoming_packet_handler) str=props.getProperty("use_packet_handler"); if(str != null) { use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_packet_handler"); if(log.isWarnEnabled()) log.warn("'use_packet_handler' is deprecated; use 'use_incoming_packet_handler' instead"); } str=props.getProperty("use_incoming_packet_handler"); if(str != null) { use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_incoming_packet_handler"); } str=props.getProperty("use_concurrent_stack"); if(str != null) { use_concurrent_stack=Boolean.valueOf(str).booleanValue(); props.remove("use_concurrent_stack"); } str=props.getProperty("thread_naming_pattern"); if(str != null) { thread_naming_pattern=str; props.remove("thread_naming_pattern"); } // ======================================= OOB thread pool ========================================= str=props.getProperty("oob_thread_pool.enabled"); if(str != null) { oob_thread_pool_enabled=Boolean.valueOf(str).booleanValue(); props.remove("oob_thread_pool.enabled"); } str=props.getProperty("oob_thread_pool.min_threads"); if(str != null) { oob_thread_pool_min_threads=Integer.valueOf(str).intValue(); props.remove("oob_thread_pool.min_threads"); } str=props.getProperty("oob_thread_pool.max_threads"); if(str != null) { oob_thread_pool_max_threads=Integer.valueOf(str).intValue(); props.remove("oob_thread_pool.max_threads"); } str=props.getProperty("oob_thread_pool.keep_alive_time"); if(str != null) { oob_thread_pool_keep_alive_time=Long.valueOf(str).longValue(); props.remove("oob_thread_pool.keep_alive_time"); } str=props.getProperty("oob_thread_pool.queue_enabled"); if(str != null) { oob_thread_pool_queue_enabled=Boolean.valueOf(str).booleanValue(); props.remove("oob_thread_pool.queue_enabled"); } str=props.getProperty("oob_thread_pool.queue_max_size"); if(str != null) { oob_thread_pool_queue_max_size=Integer.valueOf(str).intValue(); props.remove("oob_thread_pool.queue_max_size"); } str=props.getProperty("oob_thread_pool.rejection_policy"); if(str != null) { str=str.toLowerCase().trim(); oob_thread_pool_rejection_policy=str; if(!(str.equals("run") || str.equals("abort")|| str.equals("discard")|| str.equals("discardoldest"))) { log.error("rejection policy of " + str + " is unknown"); return false; } props.remove("oob_thread_pool.rejection_policy"); } // ======================================= Regular thread pool ========================================= str=props.getProperty("thread_pool.enabled"); if(str != null) { thread_pool_enabled=Boolean.valueOf(str).booleanValue(); props.remove("thread_pool.enabled"); } str=props.getProperty("thread_pool.min_threads"); if(str != null) { thread_pool_min_threads=Integer.valueOf(str).intValue(); props.remove("thread_pool.min_threads"); } str=props.getProperty("thread_pool.max_threads"); if(str != null) { thread_pool_max_threads=Integer.valueOf(str).intValue(); props.remove("thread_pool.max_threads"); } str=props.getProperty("thread_pool.keep_alive_time"); if(str != null) { thread_pool_keep_alive_time=Long.valueOf(str).longValue(); props.remove("thread_pool.keep_alive_time"); } str=props.getProperty("thread_pool.queue_enabled"); if(str != null) { thread_pool_queue_enabled=Boolean.valueOf(str).booleanValue(); props.remove("thread_pool.queue_enabled"); } str=props.getProperty("thread_pool.queue_max_size"); if(str != null) { thread_pool_queue_max_size=Integer.valueOf(str).intValue(); props.remove("thread_pool.queue_max_size"); } str=props.getProperty("thread_pool.rejection_policy"); if(str != null) { str=str.toLowerCase().trim(); thread_pool_rejection_policy=str; if(!(str.equals("run") || str.equals("abort")|| str.equals("discard")|| str.equals("discardoldest"))) { log.error("rejection policy of " + str + " is unknown"); return false; } props.remove("thread_pool.rejection_policy"); } str=props.getProperty("use_outgoing_packet_handler"); if(str != null) { log.warn("Attribute \"use_outgoing_packet_handler\" has been deprecated and is ignored"); props.remove("use_outgoing_packet_handler"); } str=props.getProperty("outgoing_queue_max_size"); if(str != null) { log.warn("Attribute \"use_outgoing_queue_max_size\" has been deprecated and is ignored"); props.remove("outgoing_queue_max_size"); } str=props.getProperty("max_bundle_size"); if(str != null) { int bundle_size=Integer.parseInt(str); if(bundle_size > max_bundle_size) { if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is greater than largest TP fragmentation size (" + max_bundle_size + ')'); return false; } if(bundle_size <= 0) { if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is <= 0"); return false; } max_bundle_size=bundle_size; props.remove("max_bundle_size"); } str=props.getProperty("max_bundle_timeout"); if(str != null) { max_bundle_timeout=Long.parseLong(str); if(max_bundle_timeout <= 0) { if(log.isErrorEnabled()) log.error("max_bundle_timeout of " + max_bundle_timeout + " is invalid"); return false; } props.remove("max_bundle_timeout"); } str=props.getProperty("enable_bundling"); if(str != null) { enable_bundling=Boolean.valueOf(str).booleanValue(); props.remove("enable_bundling"); } str=props.getProperty("enable_unicast_bundling"); if(str != null) { enable_unicast_bundling=Boolean.valueOf(str).booleanValue(); props.remove("enable_unicast_bundling"); } str=props.getProperty("enable_diagnostics"); if(str != null) { enable_diagnostics=Boolean.valueOf(str).booleanValue(); props.remove("enable_diagnostics"); } str=props.getProperty("diagnostics_addr"); if(str != null) { diagnostics_addr=str; props.remove("diagnostics_addr"); } str=props.getProperty("diagnostics_port"); if(str != null) { diagnostics_port=Integer.parseInt(str); props.remove("diagnostics_port"); } str=props.getProperty("timer.max_threads"); if(str != null) { max_timer_threads=Integer.parseInt(str); props.remove("timer.max_threads"); } str=props.getProperty(Global.SINGLETON_NAME); if(str != null) { singleton_name=str; props.remove(Global.SINGLETON_NAME); } return true; } /** * handle the UP event. * @param evt - the event being send from the stack */ public Object up(Event evt) { switch(evt.getType()) { case Event.CONFIG: if(singleton_name != null) passToAllUpProtocols(evt); else up_prot.up(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); return null; } if(singleton_name != null) { passToAllUpProtocols(evt); return null; } else return up_prot.up(evt); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling down()). */ public Object down(Event evt) { if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond return handleDownEvent(evt); } Message msg=(Message)evt.getArg(); if(header != null) { // added patch by Roland Kurmann (March 20 2003) // msg.putHeader(name, new TpHeader(channel_name)); msg.putHeaderIfAbsent(name, header); } setSourceAddress(msg); // very important !! listToBuffer() will fail with a null src address !! if(log.isTraceEnabled()) { log.trace("sending msg to " + msg.getDest() + ", src=" + msg.getSrc() + ", headers are " + msg.printHeaders()); } // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, // we will discard our own multicast message Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); if(loopback && (multicast || dest.equals(local_addr))) { // we *have* to make a copy, or else up_prot.up() might remove headers from msg which will then *not* // be available for marshalling further down (when sending the message) final Message copy=msg.copy(); if(log.isTraceEnabled()) log.trace(new StringBuilder("looping back message ").append(copy)); // up_prot.up(new Event(Event.MSG, copy)); // changed to fix http://jira.jboss.com/jira/browse/JGRP-506 Executor pool=msg.isFlagSet(Message.OOB)? oob_thread_pool : thread_pool; pool.execute(new Runnable() { public void run() { passMessageUp(copy, false); } }); if(!multicast) return null; } try { send(msg, dest, multicast); } catch(InterruptedException interruptedEx) { Thread.currentThread().interrupt(); // let someone else handle the interrupt } catch(Throwable e) { if(log.isErrorEnabled()) { String dst=msg.getDest() == null? "null" : msg.getDest().toString(); log.error("failed sending message to " + dst + " (" + msg.size() + " bytes)", e); } } return null; } /*--------------------------- End of Protocol interface -------------------------- */ /* ------------------------------ Private Methods -------------------------------- */ /** * If the sender is null, set our own address. We cannot just go ahead and set the address * anyway, as we might be sending a message on behalf of someone else ! E.gin case of * retransmission, when the original sender has crashed, or in a FLUSH protocol when we * have to return all unstable messages with the FLUSH_OK response. */ private void setSourceAddress(Message msg) { if(msg.getSrc() == null) msg.setSrc(local_addr); } private void passMessageUp(Message msg, boolean perform_cluster_name_matching) { TpHeader hdr=(TpHeader)msg.getHeader(name); // replaced removeHeader() with getHeader() if(hdr == null) { if(channel_name == null) { Event evt=new Event(Event.MSG, msg); if(singleton_name != null) { passMessageToAll(evt); } else { up_prot.up(evt); } } else { if(log.isErrorEnabled()) log.error(new StringBuilder("message does not have a transport header, msg is ").append(msg). append(", headers are ").append(msg.printHeaders()).append(", will be discarded")); } return; } String ch_name=hdr.channel_name; if(singleton_name != null) { Protocol tmp_prot=up_prots.get(ch_name); if(tmp_prot != null) { Event evt=new Event(Event.MSG, msg); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("message is ").append(msg).append(", headers are ").append(msg.printHeaders()); log.trace(sb); } tmp_prot.up(evt); } else { // we discard messages for a group we don't have. If we had a scenario with channel C1 and A,B on it, // and channel C2 and only A on it (asymmetric setup), then C2 would always log warnings that B was // not found (Jan 25 2008 (bela)) // if(log.isWarnEnabled()) // log.warn(new StringBuilder("discarded message from group \"").append(ch_name). // append("\" (our groups are ").append(up_prots.keySet()).append("). Sender was ").append(msg.getSrc())); } } else { // Discard if message's group name is not the same as our group name if(perform_cluster_name_matching && channel_name != null && !channel_name.equals(ch_name)) { if(log.isWarnEnabled()) log.warn(new StringBuilder("discarded message from different group \"").append(ch_name). append("\" (our group is \"").append(channel_name).append("\"). Sender was ").append(msg.getSrc())); } else { Event evt=new Event(Event.MSG, msg); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("message is ").append(msg).append(", headers are ").append(msg.printHeaders()); log.trace(sb); } up_prot.up(evt); } } } private void passMessageToAll(Event evt) { for(Protocol tmp_prot: up_prots.values()) { try { tmp_prot.up(evt); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failure passing message up: message is " + evt.getArg(), ex); } } } /** * Subclasses must call this method when a unicast or multicast message has been received. * Declared final so subclasses cannot override this method. * * @param dest * @param sender * @param data * @param offset * @param length */ protected final void receive(Address dest, Address sender, byte[] data, int offset, int length) { if(data == null) return; if(log.isTraceEnabled()){ boolean mcast=dest == null || dest.isMulticastAddress(); StringBuilder sb=new StringBuilder("received ("); sb.append(mcast? "mcast) " : "ucast) ").append(length).append(" bytes from ").append(sender); log.trace(sb); } try { // determine whether OOB or not by looking at first byte of 'data' boolean oob=false; byte oob_flag=data[Global.SHORT_SIZE]; // we need to skip the first 2 bytes (version) if((oob_flag & OOB) == OOB) oob=true; if(use_concurrent_stack) { if(oob) { num_oob_msgs_received++; dispatchToThreadPool(oob_thread_pool, dest, sender, data, offset, length); } else { num_incoming_msgs_received++; dispatchToThreadPool(thread_pool, dest, sender, data, offset, length); } } else { if(use_incoming_packet_handler) { byte[] tmp=new byte[length]; System.arraycopy(data, offset, tmp, 0, length); incoming_packet_queue.add(new IncomingPacket(dest, sender, tmp, 0, length)); } else handleIncomingPacket(dest, sender, data, offset, length); } } catch(Throwable t) { if(log.isErrorEnabled()) log.error(new StringBuilder("failed handling data from ").append(sender), t); } } private void dispatchToThreadPool(Executor pool, Address dest, Address sender, byte[] data, int offset, int length) { if(pool instanceof DirectExecutor) { // we don't make a copy of the buffer if we execute on this thread pool.execute(new IncomingPacket(dest, sender, data, offset, length)); } else { byte[] tmp=new byte[length]; System.arraycopy(data, offset, tmp, 0, length); pool.execute(new IncomingPacket(dest, sender, tmp, 0, length)); } } /** * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because * mcast or unicast socket reads can be concurrent. * Correction (bela April 19 2005): we access no instance variables, all vars are allocated on the stack, so * this method should be reentrant: removed 'synchronized' keyword */ private void handleIncomingPacket(Address dest, Address sender, byte[] data, int offset, int length) { Message msg=null; short version=0; boolean is_message_list, multicast; byte flags; List msgs; try { synchronized(in_stream) { in_stream.setData(data, offset, length); try { version=dis.readShort(); } catch(IOException ex) { if(discard_incompatible_packets) return; throw ex; } if(Version.isBinaryCompatible(version) == false) { if(log.isWarnEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("packet from ").append(sender).append(" has different version (").append(Version.print(version)); sb.append(") from ours (").append(Version.printVersion()).append("). "); if(discard_incompatible_packets) sb.append("Packet is discarded"); else sb.append("This may cause problems"); log.warn(sb); } if(discard_incompatible_packets) return; } flags=dis.readByte(); is_message_list=(flags & LIST) == LIST; multicast=(flags & MULTICAST) == MULTICAST; if(is_message_list) msgs=readMessageList(dis, dest, multicast); else { msg=readMessage(dis, dest, sender, multicast); msgs=new LinkedList(); msgs.add(msg); } } Address src; for(Iterator it=msgs.iterator(); it.hasNext();) { msg=(Message)it.next(); src=msg.getSrc(); if(loopback) { if(multicast && src != null && local_addr.equals(src)) { // discard own loopback multicast packets it.remove(); } } else handleIncomingMessage(msg); } if(incoming_msg_queue != null && !msgs.isEmpty()) incoming_msg_queue.addAll(msgs); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed unmarshalling message", t); } } private void handleIncomingMessage(Message msg) { if(stats) { num_msgs_received++; num_bytes_received+=msg.getLength(); } passMessageUp(msg, true); } /** Internal method to serialize and send a message. This method is not reentrant */ private void send(Message msg, Address dest, boolean multicast) throws Exception { // bundle only regular messages; send OOB messages directly if(enable_bundling && !msg.isFlagSet(Message.OOB)) { if(!enable_unicast_bundling && !multicast) { ; // don't bundle unicast msgs if enable_unicast_bundling is off (http://jira.jboss.com/jira/browse/JGRP-429) } else { bundler.send(msg, dest); return; } } ExposedByteArrayOutputStream out_stream=null; ExposedDataOutputStream dos=null; Buffer buf; out_stream=new ExposedByteArrayOutputStream(INITIAL_BUFSIZE); dos=new ExposedDataOutputStream(out_stream); writeMessage(msg, dos, multicast); buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); doSend(buf, dest, multicast); } private void doSend(Buffer buf, Address dest, boolean multicast) throws Exception { if(stats) { num_msgs_sent++; num_bytes_sent+=buf.getLength(); } if(multicast) { sendToAllMembers(buf.getBuf(), buf.getOffset(), buf.getLength()); } else { sendToSingleMember(dest, buf.getBuf(), buf.getOffset(), buf.getLength()); } } /** * This method needs to be synchronized on out_stream when it is called * @param msg * @return * @throws java.io.IOException */ private static void writeMessage(Message msg, DataOutputStream dos, boolean multicast) throws Exception { byte flags=0; dos.writeShort(Version.version); // write the version if(multicast) flags+=MULTICAST; if(msg.isFlagSet(Message.OOB)) flags+=OOB; dos.writeByte(flags); msg.writeTo(dos); } private Message readMessage(DataInputStream instream, Address dest, Address sender, boolean multicast) throws Exception { Message msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(instream); postUnmarshalling(msg, dest, sender, multicast); // allows for optimization by subclass return msg; } private static void writeMessageList(List msgs, DataOutputStream dos, boolean multicast) throws Exception { Address src; byte flags=0; int len=msgs != null? msgs.size() : 0; boolean src_written=false; dos.writeShort(Version.version); flags+=LIST; if(multicast) flags+=MULTICAST; dos.writeByte(flags); dos.writeInt(len); if(msgs != null) { for(Message msg: msgs) { src=msg.getSrc(); if(!src_written) { Util.writeAddress(src, dos); src_written=true; } msg.writeTo(dos); } } } private List readMessageList(DataInputStream instream, Address dest, boolean multicast) throws Exception { List list=new LinkedList(); int len; Message msg; Address src; len=instream.readInt(); src=Util.readAddress(instream); for(int i=0; i < len; i++) { msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(instream); postUnmarshallingList(msg, dest, multicast); msg.setSrc(src); list.add(msg); } return list; } protected Object handleDownEvent(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: synchronized(members) { view=(View)evt.getArg(); members.clear(); if(singleton_name == null) { Vector
    tmpvec=view.getMembers(); members.addAll(tmpvec); } else { for(Protocol prot: up_prots.values()) { if(prot instanceof ProtocolAdapter) { ProtocolAdapter ad=(ProtocolAdapter)prot; List
    tmp=ad.getMembers(); members.addAll(tmp); } } } } break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: channel_name=(String)evt.getArg(); header=new TpHeader(channel_name); setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); setThreadNames(); try { handleConnect(); } catch(Exception e) { throw new RuntimeException(e); } return null; case Event.DISCONNECT: unsetThreadNames(); handleDisconnect(); break; case Event.CONFIG: if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); break; } return null; } protected void setThreadNames() { if(!(global_thread_factory instanceof ExtendedThreadFactory)) return; ExtendedThreadFactory tmp=(ExtendedThreadFactory)global_thread_factory; if(incoming_packet_handler != null) tmp.renameThread(IncomingPacketHandler.THREAD_NAME, incoming_packet_handler.getThread()); if(incoming_msg_handler != null) { tmp.renameThread(IncomingMessageHandler.THREAD_NAME, incoming_msg_handler.getThread()); } if(diag_handler != null) { tmp.renameThread(DiagnosticsHandler.THREAD_NAME, diag_handler.getThread()); } } protected void unsetThreadNames() { if(incoming_packet_handler != null && incoming_packet_handler.getThread() != null) incoming_packet_handler.getThread().setName(IncomingPacketHandler.THREAD_NAME); if(incoming_msg_handler != null && incoming_msg_handler.getThread() != null) incoming_msg_handler.getThread().setName(IncomingMessageHandler.THREAD_NAME); if(diag_handler != null && diag_handler.getThread() != null) diag_handler.getThread().setName(DiagnosticsHandler.THREAD_NAME); } private void setInAllThreadFactories(String cluster_name, Address local_address, String pattern) { ThreadFactory[] factories={timer_thread_factory, default_thread_factory, oob_thread_factory, global_thread_factory}; boolean is_shared_transport=singleton_name != null && singleton_name.trim().length() > 0; for(ThreadFactory factory: factories) { if(factory instanceof ExtendedThreadFactory) { ExtendedThreadFactory tmp=(ExtendedThreadFactory)factory; if(pattern != null) { tmp.setPattern(pattern); if(is_shared_transport) tmp.setIncludeClusterName(false); } if(cluster_name != null && !is_shared_transport) // only set cluster name if we don't have a shared transport tmp.setClusterName(cluster_name); if(local_address != null) tmp.setAddress(local_address.toString()); } } } protected void handleConfigEvent(Map map) { if(map == null) return; if(map.containsKey("additional_data")) { additional_data=(byte[])map.get("additional_data"); if(local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(additional_data); } } protected static ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time, String rejection_policy, BlockingQueue queue, final ThreadFactory factory) { ThreadPoolExecutor pool=new ThreadPoolExecutor(min_threads, max_threads, keep_alive_time, TimeUnit.MILLISECONDS, queue); pool.setThreadFactory(factory); //default RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); if(rejection_policy != null) { if(rejection_policy.equals("abort")) handler = new ThreadPoolExecutor.AbortPolicy(); else if(rejection_policy.equals("discard")) handler = new ThreadPoolExecutor.DiscardPolicy(); else if(rejection_policy.equals("discardoldest")) handler = new ThreadPoolExecutor.DiscardOldestPolicy(); } pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler)); return pool; } private static void shutdownThreadPool(Executor thread_pool) { if(thread_pool instanceof ExecutorService) { ExecutorService service=(ExecutorService)thread_pool; service.shutdownNow(); try { service.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } } private void verifyRejectionPolicy(String str) throws Exception{ if(!(str.equalsIgnoreCase("run") || str.equalsIgnoreCase("abort")|| str.equalsIgnoreCase("discard")|| str.equalsIgnoreCase("discardoldest"))) { log.error("rejection policy of " + str + " is unknown"); throw new Exception("Unknown rejection policy " + str); } } protected void passToAllUpProtocols(Event evt) { for(Protocol prot: up_prots.values()) { try { prot.up(evt); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed passing up event " + evt, e); } } } public void sendUpLocalAddressEvent() { if(up_prot != null) up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); else { for(Map.Entry entry: up_prots.entrySet()) { String tmp=entry.getKey(); if(tmp.startsWith(Global.DUMMY)) continue; Protocol prot=entry.getValue(); prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } } } /* ----------------------------- End of Private Methods ---------------------------------------- */ /* ----------------------------- Inner Classes ---------------------------------------- */ class IncomingPacket implements Runnable { Address dest=null; Address sender=null; byte[] buf; int offset, length; IncomingPacket(Address dest, Address sender, byte[] buf, int offset, int length) { this.dest=dest; this.sender=sender; this.buf=buf; this.offset=offset; this.length=length; } /** Code copied from handleIncomingPacket */ public void run() { short version=0; boolean is_message_list, multicast; byte flags; ExposedByteArrayInputStream in_stream=null; DataInputStream dis=null; try { in_stream=new ExposedByteArrayInputStream(buf, offset, length); dis=new DataInputStream(in_stream); try { version=dis.readShort(); } catch(IOException ex) { if(discard_incompatible_packets) return; throw ex; } if(Version.isBinaryCompatible(version) == false) { if(log.isWarnEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("packet from ").append(sender).append(" has different version (").append(Version.print(version)); sb.append(") from ours (").append(Version.printVersion()).append("). "); if(discard_incompatible_packets) sb.append("Packet is discarded"); else sb.append("This may cause problems"); log.warn(sb); } if(discard_incompatible_packets) return; } flags=dis.readByte(); is_message_list=(flags & LIST) == LIST; multicast=(flags & MULTICAST) == MULTICAST; if(is_message_list) { // used if message bundling is enabled List msgs=readMessageList(dis, dest, multicast); for(Message msg: msgs) { if(msg.isFlagSet(Message.OOB)) { log.warn("bundled message should not be marked as OOB"); } handleMyMessage(msg, multicast); } } else { Message msg=readMessage(dis, dest, sender, multicast); handleMyMessage(msg, multicast); } } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed handling incoming message", t); } } private void handleMyMessage(Message msg, boolean multicast) { if(stats) { num_msgs_received++; num_bytes_received+=msg.getLength(); } Address src=msg.getSrc(); if(loopback && multicast && src != null && local_addr.equals(src)) { return; // drop message that was already looped back and delivered } passMessageUp(msg, true); } } /** * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up * to the higher layer (done in handleIncomingUdpPacket()). */ class IncomingPacketHandler implements Runnable { public static final String THREAD_NAME="IncomingPacketHandler"; Thread t=null; Thread getThread(){ return t; } void start() { if(t == null || !t.isAlive()) { t=global_thread_factory.newThread(this, THREAD_NAME); t.setDaemon(true); t.start(); } } void stop() { incoming_packet_queue.close(true); // should terminate the packet_handler thread too if(t != null) { try { t.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } } } public void run() { IncomingPacket entry; while(!incoming_packet_queue.closed() && Thread.currentThread().equals(t)) { try { entry=(IncomingPacket)incoming_packet_queue.remove(); handleIncomingPacket(entry.dest, entry.sender, entry.buf, entry.offset, entry.length); } catch(QueueClosedException closed_ex) { break; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("error processing incoming packet", ex); } } if(log.isTraceEnabled()) log.trace("incoming packet handler terminating"); } } class IncomingMessageHandler implements Runnable { public static final String THREAD_NAME = "IncomingMessageHandler"; Thread t; Thread getThread(){ return t; } public void start() { if(t == null || !t.isAlive()) { t=global_thread_factory.newThread(this, THREAD_NAME); t.setDaemon(true); t.start(); } } public void stop() { incoming_msg_queue.close(true); if(t != null) { try { t.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } } } public void run() { Message msg; while(!incoming_msg_queue.closed() && Thread.currentThread().equals(t)) { try { msg=(Message)incoming_msg_queue.remove(); handleIncomingMessage(msg); } catch(QueueClosedException closed_ex) { break; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("error processing incoming message", ex); } } if(log.isTraceEnabled()) log.trace("incoming message handler terminating"); } } private class Bundler { static final int MIN_NUMBER_OF_BUNDLING_TASKS=2; /** HashMap>. Keys are destinations, values are lists of Messages */ final Map> msgs=new HashMap>(36); @GuardedBy("lock") long count=0; // current number of bytes accumulated int num_msgs=0; @GuardedBy("lock") int num_bundling_tasks=0; long last_bundle_time; final ReentrantLock lock=new ReentrantLock(); private void send(Message msg, Address dest) throws Exception { long length=msg.size(); checkLength(length); Map> bundled_msgs=null; lock.lock(); try { if(count + length >= max_bundle_size) { bundled_msgs=removeBundledMessages(); } addMessage(msg, dest); count+=length; if(num_bundling_tasks < MIN_NUMBER_OF_BUNDLING_TASKS) { num_bundling_tasks++; timer.schedule(new BundlingTimer(), max_bundle_timeout, TimeUnit.MILLISECONDS); } } finally { lock.unlock(); } if(bundled_msgs != null) { sendBundledMessages(bundled_msgs); } } /** Run with lock acquired */ private void addMessage(Message msg, Address dest) { // no sync needed, always called with lock held if(msgs.isEmpty()) last_bundle_time=System.currentTimeMillis(); List tmp=msgs.get(dest); if(tmp == null) { tmp=new LinkedList(); msgs.put(dest, tmp); } tmp.add(msg); num_msgs++; } /** Must always be called with lock held */ private Map> removeBundledMessages() { if(msgs.isEmpty()) return null; Map> copy=new HashMap>(msgs); if(log.isTraceEnabled()) { long stop=System.currentTimeMillis(); double percentage=100.0 / max_bundle_size * count; StringBuilder sb=new StringBuilder("sending ").append(num_msgs).append(" msgs ("); num_msgs=0; sb.append(count).append(" bytes (" + f.format(percentage) + "% of max_bundle_size)"); if(last_bundle_time > 0) { sb.append(", collected in ").append(stop-last_bundle_time).append("ms) "); } sb.append(" to ").append(copy.size()).append(" destination(s)"); if(copy.size() > 1) sb.append(" (dests=").append(copy.keySet()).append(")"); log.trace(sb); } msgs.clear(); count=0; return copy; } /** * Sends all messages from the map, all messages for the same destination are bundled into 1 message. * This method may be called by timer and bundler concurrently * @param msgs */ private void sendBundledMessages(Map> msgs) { boolean multicast; Buffer buffer; Map.Entry> entry; Address dst; ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(INITIAL_BUFSIZE); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); boolean first=true; for(Iterator>> it=msgs.entrySet().iterator(); it.hasNext();) { entry=it.next(); List list=entry.getValue(); if(list.isEmpty()) continue; dst=entry.getKey(); multicast=dst == null || dst.isMulticastAddress(); try { if(first) { first=false; } else { out_stream.reset(); dos.reset(); } writeMessageList(list, dos, multicast); // flushes output stream when done buffer=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); doSend(buffer, dst, multicast); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception sending msg: " + e.toString(), e.getCause()); } } } private void checkLength(long len) throws Exception { if(len > max_bundle_size) throw new Exception("message size (" + len + ") is greater than max bundling size (" + max_bundle_size + "). Set the fragmentation/bundle size in FRAG and TP correctly"); } private class BundlingTimer implements Runnable { public void run() { Map> msgs=null; boolean unlocked=false; lock.lock(); try { msgs=removeBundledMessages(); if(msgs != null) { lock.unlock(); unlocked=true; sendBundledMessages(msgs); } } finally { if(unlocked) lock.lock(); num_bundling_tasks--; lock.unlock(); } } } } private class DiagnosticsHandler implements Runnable { public static final String THREAD_NAME = "DiagnosticsHandler"; Thread thread=null; MulticastSocket diag_sock=null; DiagnosticsHandler() { } Thread getThread(){ return thread; } void start() throws IOException { diag_sock=new MulticastSocket(diagnostics_port); java.util.List interfaces=Util.getAllAvailableInterfaces(); bindToInterfaces(interfaces, diag_sock); if(thread == null || !thread.isAlive()) { thread=global_thread_factory.newThread(this, THREAD_NAME); thread.setDaemon(true); thread.start(); } } void stop() { if(diag_sock != null) diag_sock.close(); if(thread != null){ try{ thread.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e){ Thread.currentThread().interrupt(); // set interrupt flag } } } public void run() { byte[] buf=new byte[1500]; // MTU on most LANs DatagramPacket packet; while(!diag_sock.isClosed() && Thread.currentThread().equals(thread)) { packet=new DatagramPacket(buf, 0, buf.length); try { diag_sock.receive(packet); handleDiagnosticProbe(packet.getSocketAddress(), diag_sock, new String(packet.getData(), packet.getOffset(), packet.getLength())); } catch(IOException e) { } } } private void bindToInterfaces(java.util.List interfaces, MulticastSocket s) { SocketAddress group_addr=new InetSocketAddress(diagnostics_addr, diagnostics_port); for(Iterator it=interfaces.iterator(); it.hasNext();) { NetworkInterface i=(NetworkInterface)it.next(); try { if (i.getInetAddresses().hasMoreElements()) { // fix for VM crash - suggested by JJalenak@netopia.com s.joinGroup(group_addr, i); if(log.isTraceEnabled()) log.trace("joined " + group_addr + " on " + i.getName()); } } catch(IOException e) { log.warn("failed to join " + group_addr + " on " + i.getName() + ": " + e); } } } } public static class ProtocolAdapter extends Protocol { final String cluster_name; final String transport_name; final TpHeader header; final List
    members=new ArrayList
    (); final ExtendedThreadFactory factory; public ProtocolAdapter(String cluster_name, String transport_name, Protocol up, Protocol down, String pattern, Address addr) { this.cluster_name=cluster_name; this.transport_name=transport_name; this.up_prot=up; this.down_prot=down; this.header=new TpHeader(cluster_name); this.factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); factory.setPattern(pattern); if(addr != null) factory.setAddress(addr.toString()); } public List
    getMembers() { return Collections.unmodifiableList(members); } public ThreadFactory getThreadFactory() { return factory; } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); msg.putHeader(transport_name, header); break; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); Vector
    tmp=view.getMembers(); members.clear(); members.addAll(tmp); break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: factory.setClusterName((String)evt.getArg()); break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: Address addr=(Address)evt.getArg(); if(addr != null) factory.setAddress(addr.toString()); break; } return up_prot.up(evt); } public String getName() { return null; } public String toString() { return cluster_name + " (" + transport_name + ")"; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/HDRS.java0000644000175000017500000000347011366547366025040 0ustar twernertwerner// $Id: HDRS.java,v 1.6 2007/06/20 10:41:02 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Header; import org.jgroups.stack.Protocol; import java.util.Map; /** * Example of a protocol layer. Contains no real functionality, can be used as a template. */ public class HDRS extends Protocol { public String getName() {return "HDRS";} private static void printMessage(Message msg, String label) { StringBuilder sb=new StringBuilder(); sb.append(label).append(":\n"); Map hdrs=msg.getHeaders(); sb.append(print(msg, hdrs)); System.out.println(sb); } private static String print(Message msg, Map hdrs) { StringBuilder sb=new StringBuilder(); int hdrs_size=0; for(Map.Entry entry: hdrs.entrySet()) { String name=entry.getKey(); Header hdr=entry.getValue(); int size=hdr.size(); hdrs_size+=size; sb.append(name).append(": ").append(" ").append(size).append(" bytes\n"); } sb.append("headers=").append(hdrs_size).append(", total msg size=").append(msg.size()); sb.append(", msg payload=").append(msg.getLength()).append("\n"); return sb.toString(); } public Object up(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); printMessage(msg, "up"); } return up_prot.up(evt); // Pass up to the layer above us } public Object down(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); printMessage(msg, "down"); } return down_prot.down(evt); // Pass on to the layer below us } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/DISCARD_PAYLOAD.java0000644000175000017500000000445511366547366026526 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.Protocol; import java.util.Properties; /** * Discards a message whose sequence number (in the payload, as a Long) matches seqno 2 times, * before passing it up. Used for unit testing * of OOB messages * @author Bela Ban * @version $Id: DISCARD_PAYLOAD.java,v 1.6 2007/01/12 14:19:40 belaban Exp $ */ public class DISCARD_PAYLOAD extends Protocol { long seqno=3; // drop 3 long duplicate=4; // duplicate 4 (one time) int num_discards=0; public DISCARD_PAYLOAD() { } public String getName() { return "DISCARD_PAYLOAD"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("seqno"); if(str != null) { seqno=Long.parseLong(str); props.remove("seqno"); } str=props.getProperty("duplicate"); if(str != null) { duplicate=Long.parseLong(str); props.remove("duplicate"); } if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } public Object up(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); if(msg.getLength() > 0) { try { Long payload=(Long)msg.getObject(); if(payload != null) { long val=payload.longValue(); if(val == seqno) { synchronized(this) { if(num_discards < 3) { num_discards++; return null; } } } if(val == duplicate) { // inject a duplicate message super.up(evt); // pass it up, will passed up a second time by the default up_prot.up(evt) } } } catch(Throwable t) { ; } } } return up_prot.up(evt); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/S3_PING.java0000644000175000017500000035633111366547366025411 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.util.Util; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import static java.lang.String.valueOf; /** * Discovery protocol using Amazon's S3 storage. The S3 access code reuses the example shipped by Amazon. * This protocol is unsupported and experimental ! * @author Bela Ban * @version $Id: S3_PING.java,v 1.1.2.4 2009/08/05 10:14:32 belaban Exp $ */ public class S3_PING extends FILE_PING { protected String access_key=null; protected String secret_access_key=null; protected AWSAuthConnection conn=null; protected final Set keys=new HashSet(); public boolean setProperties(Properties props) { String str; str=props.getProperty("access_key"); if(str != null) { access_key=str; props.remove("access_key"); } str=props.getProperty("secret_access_key"); if(str != null) { secret_access_key=str; props.remove("secret_access_key"); } if(access_key == null || secret_access_key == null) throw new IllegalArgumentException("access_key and secret_access_key must be non-null"); return super.setProperties(props); } public void init() throws Exception { super.init(); conn=new AWSAuthConnection(access_key, secret_access_key); if(!conn.checkBucketExists(location)) { conn.createBucket(location, AWSAuthConnection.LOCATION_DEFAULT, null).connection.getResponseMessage(); } Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { for(Entry entry : keys) { remove(entry.cluster, entry.addr); } keys.clear(); } }); } public void stop() { if(group_addr != null && local_addr != null) { // remove from keys: for(Iterator it=keys.iterator(); it.hasNext();) { Entry entry=it.next(); if(group_addr.equals(entry.cluster) && local_addr.equals(entry.addr)) { it.remove(); } } remove(group_addr, local_addr); } super.stop(); } protected List
    readAll(String clustername) { if(clustername == null) return null; List
    retval=new ArrayList
    (); try { ListBucketResponse rsp=conn.listBucket(location, clustername, null, null, null); if(rsp.entries != null) { for(Iterator it=rsp.entries.iterator(); it.hasNext();) { ListEntry key=it.next(); GetResponse val=conn.get(location, key.key, null); if(val.object != null) { byte[] buf=val.object.data; if(buf != null) { try { Address addr=(Address)Util.objectFromByteBuffer(buf); retval.add(addr); } catch(Exception e) { log.error("failed marshalling buffer to address", e); } } } } } return retval; } catch(IOException ex) { log.error("failed reading addresses", ex); return retval; } } protected void writeToFile(Address addr, String clustername) { if(clustername == null || addr == null) return; String key=clustername + "/" + addr.toString(); try { Map headers=new TreeMap(); headers.put("Content-Type", Arrays.asList("text/plain")); byte[] buf=Util.objectToByteBuffer(addr); S3Object val=new S3Object(buf, null); String response=conn.put(location, key, val, headers).connection.getResponseMessage(); if(log.isTraceEnabled()) log.trace("response: " + response); keys.add(new Entry(clustername, addr)); } catch(Exception e) { log.error("failed marshalling address " + addr + " to buffer", e); } } protected void remove(String clustername, Address addr) { if(clustername == null || addr == null) return; String key=clustername + "/" + addr.toString(); try { Map headers=new TreeMap(); headers.put("Content-Type", Arrays.asList("text/plain")); String response=conn.delete(location, key, headers).connection.getResponseMessage(); if(log.isTraceEnabled()) log.trace("response: " + response); } catch(Exception e) { log.error("failed marshalling address " + addr + " to buffer", e); } } private static class Entry { final String cluster; final Address addr; Entry(String cluster, Address addr) { this.cluster=cluster; this.addr=addr; } } /** * The following classes have been copied from Amazon's sample code */ static class AWSAuthConnection { public static final String LOCATION_DEFAULT=null; public static final String LOCATION_EU="EU"; private String awsAccessKeyId; private String awsSecretAccessKey; private boolean isSecure; private String server; private int port; private CallingFormat callingFormat; public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey) { this(awsAccessKeyId, awsSecretAccessKey, true); } public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure) { this(awsAccessKeyId, awsSecretAccessKey, isSecure, Utils.DEFAULT_HOST); } public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure, String server) { this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, isSecure? Utils.SECURE_PORT : Utils.INSECURE_PORT); } public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure, String server, int port) { this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, port, CallingFormat.getSubdomainCallingFormat()); } public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure, String server, CallingFormat format) { this(awsAccessKeyId, awsSecretAccessKey, isSecure, server, isSecure? Utils.SECURE_PORT : Utils.INSECURE_PORT, format); } /** * Create a new interface to interact with S3 with the given credential and connection * parameters * @param awsAccessKeyId Your user key into AWS * @param awsSecretAccessKey The secret string used to generate signatures for authentication. * @param isSecure use SSL encryption * @param server Which host to connect to. Usually, this will be s3.amazonaws.com * @param port Which port to use. * @param format Type of request Regular/Vanity or Pure Vanity domain */ public AWSAuthConnection(String awsAccessKeyId, String awsSecretAccessKey, boolean isSecure, String server, int port, CallingFormat format) { this.awsAccessKeyId=awsAccessKeyId; this.awsSecretAccessKey=awsSecretAccessKey; this.isSecure=isSecure; this.server=server; this.port=port; this.callingFormat=format; } /** * Creates a new bucket. * @param bucket The name of the bucket to create. * @param headers A Map of String to List of Strings representing the http headers to pass (can be null). */ public Response createBucket(String bucket, Map headers) throws IOException { return createBucket(bucket, null, headers); } /** * Creates a new bucket. * @param bucket The name of the bucket to create. * @param location Desired location ("EU") (or null for default). * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). * @throws IllegalArgumentException on invalid location */ public Response createBucket(String bucket, String location, Map headers) throws IOException { String body; if(location == null) { body=null; } else if(LOCATION_EU.equals(location)) { if(!callingFormat.supportsLocatedBuckets()) throw new IllegalArgumentException("Creating location-constrained bucket with unsupported calling-format"); body="" + location + ""; } else throw new IllegalArgumentException("Invalid Location: " + location); // validate bucket name if(!Utils.validateBucketName(bucket, callingFormat)) throw new IllegalArgumentException("Invalid Bucket Name: " + bucket); HttpURLConnection request=makeRequest("PUT", bucket, "", null, headers); if(body != null) { request.setDoOutput(true); request.getOutputStream().write(body.getBytes("UTF-8")); } return new Response(request); } /** * Check if the specified bucket exists (via a HEAD request) * @param bucket The name of the bucket to check * @return true if HEAD access returned success */ public boolean checkBucketExists(String bucket) throws IOException { HttpURLConnection response=makeRequest("HEAD", bucket, "", null, null); int httpCode=response.getResponseCode(); return httpCode >= 200 && httpCode < 300; } /** * Lists the contents of a bucket. * @param bucket The name of the bucket to create. * @param prefix All returned keys will start with this string (can be null). * @param marker All returned keys will be lexographically greater than * this string (can be null). * @param maxKeys The maximum number of keys to return (can be null). * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public ListBucketResponse listBucket(String bucket, String prefix, String marker, Integer maxKeys, Map headers) throws IOException { return listBucket(bucket, prefix, marker, maxKeys, null, headers); } /** * Lists the contents of a bucket. * @param bucket The name of the bucket to list. * @param prefix All returned keys will start with this string (can be null). * @param marker All returned keys will be lexographically greater than * this string (can be null). * @param maxKeys The maximum number of keys to return (can be null). * @param delimiter Keys that contain a string between the prefix and the first * occurrence of the delimiter will be rolled up into a single element. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public ListBucketResponse listBucket(String bucket, String prefix, String marker, Integer maxKeys, String delimiter, Map headers) throws IOException { Map pathArgs=Utils.paramsForListOptions(prefix, marker, maxKeys, delimiter); return new ListBucketResponse(makeRequest("GET", bucket, "", pathArgs, headers)); } /** * Deletes a bucket. * @param bucket The name of the bucket to delete. * @param headers A Map of String to List of Strings representing the http headers to pass (can be null). */ public Response deleteBucket(String bucket, Map headers) throws IOException { return new Response(makeRequest("DELETE", bucket, "", null, headers)); } /** * Writes an object to S3. * @param bucket The name of the bucket to which the object will be added. * @param key The name of the key to use. * @param object An S3Object containing the data to write. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public Response put(String bucket, String key, S3Object object, Map headers) throws IOException { HttpURLConnection request= makeRequest("PUT", bucket, Utils.urlencode(key), null, headers, object); request.setDoOutput(true); request.getOutputStream().write(object.data == null? new byte[]{} : object.data); return new Response(request); } /** * Creates a copy of an existing S3 Object. In this signature, we will copy the * existing metadata. The default access control policy is private; if you want * to override it, please use x-amz-acl in the headers. * @param sourceBucket The name of the bucket where the source object lives. * @param sourceKey The name of the key to copy. * @param destinationBucket The name of the bucket to which the object will be added. * @param destinationKey The name of the key to use. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). You may wish to set the x-amz-acl header appropriately. */ public Response copy(String sourceBucket, String sourceKey, String destinationBucket, String destinationKey, Map headers) throws IOException { S3Object object=new S3Object(new byte[]{}, new HashMap()); headers=headers == null? new HashMap() : new HashMap(headers); headers.put("x-amz-copy-source", Arrays.asList(sourceBucket + "/" + sourceKey)); headers.put("x-amz-metadata-directive", Arrays.asList("COPY")); return verifyCopy(put(destinationBucket, destinationKey, object, headers)); } /** * Creates a copy of an existing S3 Object. In this signature, we will replace the * existing metadata. The default access control policy is private; if you want * to override it, please use x-amz-acl in the headers. * @param sourceBucket The name of the bucket where the source object lives. * @param sourceKey The name of the key to copy. * @param destinationBucket The name of the bucket to which the object will be added. * @param destinationKey The name of the key to use. * @param metadata A Map of String to List of Strings representing the S3 metadata * for the new object. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). You may wish to set the x-amz-acl header appropriately. */ public Response copy(String sourceBucket, String sourceKey, String destinationBucket, String destinationKey, Map metadata, Map headers) throws IOException { S3Object object=new S3Object(new byte[]{}, metadata); headers=headers == null? new HashMap() : new HashMap(headers); headers.put("x-amz-copy-source", Arrays.asList(sourceBucket + "/" + sourceKey)); headers.put("x-amz-metadata-directive", Arrays.asList("REPLACE")); return verifyCopy(put(destinationBucket, destinationKey, object, headers)); } /** * Copy sometimes returns a successful response and starts to send whitespace * characters to us. This method processes those whitespace characters and * will throw an exception if the response is either unknown or an error. * @param response Response object from the PUT request. * @return The response with the input stream drained. * @throws IOException If anything goes wrong. */ private static Response verifyCopy(Response response) throws IOException { if(response.connection.getResponseCode() < 400) { byte[] body=GetResponse.slurpInputStream(response.connection.getInputStream()); String message=new String(body); if(message.contains("")) { // It worked! } else { throw new IOException("Unexpected response: " + message); } } return response; } /** * Reads an object from S3. * @param bucket The name of the bucket where the object lives. * @param key The name of the key to use. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public GetResponse get(String bucket, String key, Map headers) throws IOException { return new GetResponse(makeRequest("GET", bucket, Utils.urlencode(key), null, headers)); } /** * Deletes an object from S3. * @param bucket The name of the bucket where the object lives. * @param key The name of the key to use. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public Response delete(String bucket, String key, Map headers) throws IOException { return new Response(makeRequest("DELETE", bucket, Utils.urlencode(key), null, headers)); } /** * Get the requestPayment xml document for a given bucket * @param bucket The name of the bucket * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public GetResponse getBucketRequestPayment(String bucket, Map headers) throws IOException { Map pathArgs=new HashMap(); pathArgs.put("requestPayment", null); return new GetResponse(makeRequest("GET", bucket, "", pathArgs, headers)); } /** * Write a new requestPayment xml document for a given bucket * @param bucket The name of the bucket * @param requestPaymentXMLDoc * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public Response putBucketRequestPayment(String bucket, String requestPaymentXMLDoc, Map headers) throws IOException { Map pathArgs=new HashMap(); pathArgs.put("requestPayment", null); S3Object object=new S3Object(requestPaymentXMLDoc.getBytes(), null); HttpURLConnection request=makeRequest("PUT", bucket, "", pathArgs, headers, object); request.setDoOutput(true); request.getOutputStream().write(object.data == null? new byte[]{} : object.data); return new Response(request); } /** * Get the logging xml document for a given bucket * @param bucket The name of the bucket * @param headers A Map of String to List of Strings representing the http headers to pass (can be null). */ public GetResponse getBucketLogging(String bucket, Map headers) throws IOException { Map pathArgs=new HashMap(); pathArgs.put("logging", null); return new GetResponse(makeRequest("GET", bucket, "", pathArgs, headers)); } /** * Write a new logging xml document for a given bucket * @param loggingXMLDoc The xml representation of the logging configuration as a String * @param bucket The name of the bucket * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public Response putBucketLogging(String bucket, String loggingXMLDoc, Map headers) throws IOException { Map pathArgs=new HashMap(); pathArgs.put("logging", null); S3Object object=new S3Object(loggingXMLDoc.getBytes(), null); HttpURLConnection request=makeRequest("PUT", bucket, "", pathArgs, headers, object); request.setDoOutput(true); request.getOutputStream().write(object.data == null? new byte[]{} : object.data); return new Response(request); } /** * Get the ACL for a given bucket * @param bucket The name of the bucket where the object lives. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public GetResponse getBucketACL(String bucket, Map headers) throws IOException { return getACL(bucket, "", headers); } /** * Get the ACL for a given object (or bucket, if key is null). * @param bucket The name of the bucket where the object lives. * @param key The name of the key to use. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public GetResponse getACL(String bucket, String key, Map headers) throws IOException { if(key == null) key=""; Map pathArgs=new HashMap(); pathArgs.put("acl", null); return new GetResponse( makeRequest("GET", bucket, Utils.urlencode(key), pathArgs, headers) ); } /** * Write a new ACL for a given bucket * @param aclXMLDoc The xml representation of the ACL as a String * @param bucket The name of the bucket where the object lives. * @param headers A Map of String to List of Strings representing the http headers to pass (can be null). */ public Response putBucketACL(String bucket, String aclXMLDoc, Map headers) throws IOException { return putACL(bucket, "", aclXMLDoc, headers); } /** * Write a new ACL for a given object * @param aclXMLDoc The xml representation of the ACL as a String * @param bucket The name of the bucket where the object lives. * @param key The name of the key to use. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public Response putACL(String bucket, String key, String aclXMLDoc, Map headers) throws IOException { S3Object object=new S3Object(aclXMLDoc.getBytes(), null); Map pathArgs=new HashMap(); pathArgs.put("acl", null); HttpURLConnection request= makeRequest("PUT", bucket, Utils.urlencode(key), pathArgs, headers, object); request.setDoOutput(true); request.getOutputStream().write(object.data == null? new byte[]{} : object.data); return new Response(request); } public LocationResponse getBucketLocation(String bucket) throws IOException { Map pathArgs=new HashMap(); pathArgs.put("location", null); return new LocationResponse(makeRequest("GET", bucket, "", pathArgs, null)); } /** * List all the buckets created by this account. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ public ListAllMyBucketsResponse listAllMyBuckets(Map headers) throws IOException { return new ListAllMyBucketsResponse(makeRequest("GET", "", "", null, headers)); } /** * Make a new HttpURLConnection without passing an S3Object parameter. * Use this method for key operations that do require arguments * @param method The method to invoke * @param bucketName the bucket this request is for * @param key the key this request is for * @param pathArgs the * @param headers * @return * @throws MalformedURLException * @throws IOException */ private HttpURLConnection makeRequest(String method, String bucketName, String key, Map pathArgs, Map headers) throws IOException { return makeRequest(method, bucketName, key, pathArgs, headers, null); } /** * Make a new HttpURLConnection. * @param method The HTTP method to use (GET, PUT, DELETE) * @param bucket The bucket name this request affects * @param key The key this request is for * @param pathArgs parameters if any to be sent along this request * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). * @param object The S3Object that is to be written (can be null). */ private HttpURLConnection makeRequest(String method, String bucket, String key, Map pathArgs, Map headers, S3Object object) throws IOException { CallingFormat format=Utils.getCallingFormatForBucket(this.callingFormat, bucket); if(isSecure && format != CallingFormat.getPathCallingFormat() && bucket.contains(".")) { System.err.println("You are making an SSL connection, however, the bucket contains periods and the wildcard certificate will not match by default. Please consider using HTTP."); } // build the domain based on the calling format URL url=format.getURL(isSecure, server, this.port, bucket, key, pathArgs); HttpURLConnection connection=(HttpURLConnection)url.openConnection(); connection.setRequestMethod(method); // subdomain-style urls may encounter http redirects. // Ensure that redirects are supported. if(!connection.getInstanceFollowRedirects() && format.supportsLocatedBuckets()) throw new RuntimeException("HTTP redirect support required."); addHeaders(connection, headers); if(object != null) addMetadataHeaders(connection, object.metadata); addAuthHeader(connection, method, bucket, key, pathArgs); return connection; } /** * Add the given headers to the HttpURLConnection. * @param connection The HttpURLConnection to which the headers will be added. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). */ private static void addHeaders(HttpURLConnection connection, Map headers) { addHeaders(connection, headers, ""); } /** * Add the given metadata fields to the HttpURLConnection. * @param connection The HttpURLConnection to which the headers will be added. * @param metadata A Map of String to List of Strings representing the s3 * metadata for this resource. */ private static void addMetadataHeaders(HttpURLConnection connection, Map metadata) { addHeaders(connection, metadata, Utils.METADATA_PREFIX); } /** * Add the given headers to the HttpURLConnection with a prefix before the keys. * @param connection The HttpURLConnection to which the headers will be added. * @param headers A Map of String to List of Strings representing the http * headers to pass (can be null). * @param prefix The string to prepend to each key before adding it to the connection. */ private static void addHeaders(HttpURLConnection connection, Map headers, String prefix) { if(headers != null) { for(Iterator i=headers.keySet().iterator(); i.hasNext();) { String key=(String)i.next(); for(Iterator j=((List)headers.get(key)).iterator(); j.hasNext();) { String value=(String)j.next(); connection.addRequestProperty(prefix + key, value); } } } } /** * Add the appropriate Authorization header to the HttpURLConnection. * @param connection The HttpURLConnection to which the header will be added. * @param method The HTTP method to use (GET, PUT, DELETE) * @param bucket the bucket name this request is for * @param key the key this request is for * @param pathArgs path arguments which are part of this request */ private void addAuthHeader(HttpURLConnection connection, String method, String bucket, String key, Map pathArgs) { if(connection.getRequestProperty("Date") == null) { connection.setRequestProperty("Date", httpDate()); } if(connection.getRequestProperty("Content-Type") == null) { connection.setRequestProperty("Content-Type", ""); } String canonicalString= Utils.makeCanonicalString(method, bucket, key, pathArgs, connection.getRequestProperties()); String encodedCanonical=Utils.encode(this.awsSecretAccessKey, canonicalString, false); connection.setRequestProperty("Authorization", "AWS " + this.awsAccessKeyId + ":" + encodedCanonical); } /** * Generate an rfc822 date for use in the Date HTTP header. */ public static String httpDate() { final String DateFormat="EEE, dd MMM yyyy HH:mm:ss "; SimpleDateFormat format=new SimpleDateFormat(DateFormat, Locale.US); format.setTimeZone(TimeZone.getTimeZone("GMT")); return format.format(new Date()) + "GMT"; } } static class ListEntry { /** * The name of the object */ public String key; /** * The date at which the object was last modified. */ public Date lastModified; /** * The object's ETag, which can be used for conditional GETs. */ public String eTag; /** * The size of the object in bytes. */ public long size; /** * The object's storage class */ public String storageClass; /** * The object's owner */ public Owner owner; public String toString() { return key; } } static class Owner { public String id; public String displayName; } static class Response { public HttpURLConnection connection; public Response(HttpURLConnection connection) throws IOException { this.connection=connection; } } static class GetResponse extends Response { public S3Object object; /** * Pulls a representation of an S3Object out of the HttpURLConnection response. */ public GetResponse(HttpURLConnection connection) throws IOException { super(connection); if(connection.getResponseCode() < 400) { Map metadata=extractMetadata(connection); byte[] body=slurpInputStream(connection.getInputStream()); this.object=new S3Object(body, metadata); } } /** * Examines the response's header fields and returns a Map from String to List of Strings * representing the object's metadata. */ private static Map extractMetadata(HttpURLConnection connection) { TreeMap metadata=new TreeMap(); Map headers=connection.getHeaderFields(); for(Iterator i=headers.keySet().iterator(); i.hasNext();) { String key=(String)i.next(); if(key == null) continue; if(key.startsWith(Utils.METADATA_PREFIX)) { metadata.put(key.substring(Utils.METADATA_PREFIX.length()), headers.get(key)); } } return metadata; } /** * Read the input stream and dump it all into a big byte array */ static byte[] slurpInputStream(InputStream stream) throws IOException { final int chunkSize=2048; byte[] buf=new byte[chunkSize]; ByteArrayOutputStream byteStream=new ByteArrayOutputStream(chunkSize); int count; while((count=stream.read(buf)) != -1) byteStream.write(buf, 0, count); return byteStream.toByteArray(); } } static class LocationResponse extends Response { String location; /** * Parse the response to a ?location query. */ public LocationResponse(HttpURLConnection connection) throws IOException { super(connection); if(connection.getResponseCode() < 400) { try { XMLReader xr=Utils.createXMLReader(); ; LocationResponseHandler handler=new LocationResponseHandler(); xr.setContentHandler(handler); xr.setErrorHandler(handler); xr.parse(new InputSource(connection.getInputStream())); this.location=handler.loc; } catch(SAXException e) { throw new RuntimeException("Unexpected error parsing ListAllMyBuckets xml", e); } } else { this.location=""; } } /** * Report the location-constraint for a bucket. * A value of null indicates an error; * the empty string indicates no constraint; * and any other value is an actual location constraint value. */ public String getLocation() { return location; } /** * Helper class to parse LocationConstraint response XML */ static class LocationResponseHandler extends DefaultHandler { String loc=null; private StringBuffer currText=null; public void startDocument() { } public void startElement(String uri, String name, String qName, Attributes attrs) { if(name.equals("LocationConstraint")) { this.currText=new StringBuffer(); } } public void endElement(String uri, String name, String qName) { if(name.equals("LocationConstraint")) { loc=this.currText.toString(); this.currText=null; } } public void characters(char ch[], int start, int length) { if(currText != null) this.currText.append(ch, start, length); } } } static class Bucket { /** * The name of the bucket. */ public String name; /** * The bucket's creation date. */ public Date creationDate; public Bucket() { this.name=null; this.creationDate=null; } public Bucket(String name, Date creationDate) { this.name=name; this.creationDate=creationDate; } public String toString() { return this.name; } } static class ListBucketResponse extends Response { /** * The name of the bucket being listed. Null if request fails. */ public String name=null; /** * The prefix echoed back from the request. Null if request fails. */ public String prefix=null; /** * The marker echoed back from the request. Null if request fails. */ public String marker=null; /** * The delimiter echoed back from the request. Null if not specified in * the request, or if it fails. */ public String delimiter=null; /** * The maxKeys echoed back from the request if specified. 0 if request fails. */ public int maxKeys=0; /** * Indicates if there are more results to the list. True if the current * list results have been truncated. false if request fails. */ public boolean isTruncated=false; /** * Indicates what to use as a marker for subsequent list requests in the event * that the results are truncated. Present only when a delimiter is specified. * Null if request fails. */ public String nextMarker=null; /** * A List of ListEntry objects representing the objects in the given bucket. * Null if the request fails. */ public List entries=null; /** * A List of CommonPrefixEntry objects representing the common prefixes of the * keys that matched up to the delimiter. Null if the request fails. */ public List commonPrefixEntries=null; public ListBucketResponse(HttpURLConnection connection) throws IOException { super(connection); if(connection.getResponseCode() < 400) { try { XMLReader xr=Utils.createXMLReader(); ListBucketHandler handler=new ListBucketHandler(); xr.setContentHandler(handler); xr.setErrorHandler(handler); xr.parse(new InputSource(connection.getInputStream())); this.name=handler.getName(); this.prefix=handler.getPrefix(); this.marker=handler.getMarker(); this.delimiter=handler.getDelimiter(); this.maxKeys=handler.getMaxKeys(); this.isTruncated=handler.getIsTruncated(); this.nextMarker=handler.getNextMarker(); this.entries=handler.getKeyEntries(); this.commonPrefixEntries=handler.getCommonPrefixEntries(); } catch(SAXException e) { throw new RuntimeException("Unexpected error parsing ListBucket xml", e); } } } static class ListBucketHandler extends DefaultHandler { private String name=null; private String prefix=null; private String marker=null; private String delimiter=null; private int maxKeys=0; private boolean isTruncated=false; private String nextMarker=null; private boolean isEchoedPrefix=false; private List keyEntries=null; private ListEntry keyEntry=null; private List commonPrefixEntries=null; private CommonPrefixEntry commonPrefixEntry=null; private StringBuffer currText=null; private SimpleDateFormat iso8601Parser=null; public ListBucketHandler() { super(); keyEntries=new ArrayList(); commonPrefixEntries=new ArrayList(); this.iso8601Parser=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); this.iso8601Parser.setTimeZone(new SimpleTimeZone(0, "GMT")); this.currText=new StringBuffer(); } public void startDocument() { this.isEchoedPrefix=true; } public void endDocument() { // ignore } public void startElement(String uri, String name, String qName, Attributes attrs) { if(name.equals("Contents")) { this.keyEntry=new ListEntry(); } else if(name.equals("Owner")) { this.keyEntry.owner=new Owner(); } else if(name.equals("CommonPrefixes")) { this.commonPrefixEntry=new CommonPrefixEntry(); } } public void endElement(String uri, String name, String qName) { if(name.equals("Name")) { this.name=this.currText.toString(); } // this prefix is the one we echo back from the request else if(name.equals("Prefix") && this.isEchoedPrefix) { this.prefix=this.currText.toString(); this.isEchoedPrefix=false; } else if(name.equals("Marker")) { this.marker=this.currText.toString(); } else if(name.equals("MaxKeys")) { this.maxKeys=Integer.parseInt(this.currText.toString()); } else if(name.equals("Delimiter")) { this.delimiter=this.currText.toString(); } else if(name.equals("IsTruncated")) { this.isTruncated=Boolean.valueOf(this.currText.toString()); } else if(name.equals("NextMarker")) { this.nextMarker=this.currText.toString(); } else if(name.equals("Contents")) { this.keyEntries.add(this.keyEntry); } else if(name.equals("Key")) { this.keyEntry.key=this.currText.toString(); } else if(name.equals("LastModified")) { try { this.keyEntry.lastModified=this.iso8601Parser.parse(this.currText.toString()); } catch(ParseException e) { throw new RuntimeException("Unexpected date format in list bucket output", e); } } else if(name.equals("ETag")) { this.keyEntry.eTag=this.currText.toString(); } else if(name.equals("Size")) { this.keyEntry.size=Long.parseLong(this.currText.toString()); } else if(name.equals("StorageClass")) { this.keyEntry.storageClass=this.currText.toString(); } else if(name.equals("ID")) { this.keyEntry.owner.id=this.currText.toString(); } else if(name.equals("DisplayName")) { this.keyEntry.owner.displayName=this.currText.toString(); } else if(name.equals("CommonPrefixes")) { this.commonPrefixEntries.add(this.commonPrefixEntry); } // this is the common prefix for keys that match up to the delimiter else if(name.equals("Prefix")) { this.commonPrefixEntry.prefix=this.currText.toString(); } if(this.currText.length() != 0) this.currText=new StringBuffer(); } public void characters(char ch[], int start, int length) { this.currText.append(ch, start, length); } public String getName() { return this.name; } public String getPrefix() { return this.prefix; } public String getMarker() { return this.marker; } public String getDelimiter() { return this.delimiter; } public int getMaxKeys() { return this.maxKeys; } public boolean getIsTruncated() { return this.isTruncated; } public String getNextMarker() { return this.nextMarker; } public List getKeyEntries() { return this.keyEntries; } public List getCommonPrefixEntries() { return this.commonPrefixEntries; } } } static class CommonPrefixEntry { /** * The prefix common to the delimited keys it represents */ public String prefix; } static class ListAllMyBucketsResponse extends Response { /** * A list of Bucket objects, one for each of this account's buckets. Will be null if * the request fails. */ public List entries; public ListAllMyBucketsResponse(HttpURLConnection connection) throws IOException { super(connection); if(connection.getResponseCode() < 400) { try { XMLReader xr=Utils.createXMLReader(); ; ListAllMyBucketsHandler handler=new ListAllMyBucketsHandler(); xr.setContentHandler(handler); xr.setErrorHandler(handler); xr.parse(new InputSource(connection.getInputStream())); this.entries=handler.getEntries(); } catch(SAXException e) { throw new RuntimeException("Unexpected error parsing ListAllMyBuckets xml", e); } } } static class ListAllMyBucketsHandler extends DefaultHandler { private List entries=null; private Bucket currBucket=null; private StringBuffer currText=null; private SimpleDateFormat iso8601Parser=null; public ListAllMyBucketsHandler() { super(); entries=new ArrayList(); this.iso8601Parser=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); this.iso8601Parser.setTimeZone(new SimpleTimeZone(0, "GMT")); this.currText=new StringBuffer(); } public void startDocument() { // ignore } public void endDocument() { // ignore } public void startElement(String uri, String name, String qName, Attributes attrs) { if(name.equals("Bucket")) { this.currBucket=new Bucket(); } } public void endElement(String uri, String name, String qName) { if(name.equals("Bucket")) { this.entries.add(this.currBucket); } else if(name.equals("Name")) { this.currBucket.name=this.currText.toString(); } else if(name.equals("CreationDate")) { try { this.currBucket.creationDate=this.iso8601Parser.parse(this.currText.toString()); } catch(ParseException e) { throw new RuntimeException("Unexpected date format in list bucket output", e); } } this.currText=new StringBuffer(); } public void characters(char ch[], int start, int length) { this.currText.append(ch, start, length); } public List getEntries() { return this.entries; } } } static class S3Object { public byte[] data; /** * A Map from String to List of Strings representing the object's metadata */ public Map metadata; public S3Object(byte[] data, Map metadata) { this.data=data; this.metadata=metadata; } } abstract static class CallingFormat { protected static CallingFormat pathCallingFormat=new PathCallingFormat(); protected static CallingFormat subdomainCallingFormat=new SubdomainCallingFormat(); protected static CallingFormat vanityCallingFormat=new VanityCallingFormat(); public abstract boolean supportsLocatedBuckets(); public abstract String getEndpoint(String server, int port, String bucket); public abstract String getPathBase(String bucket, String key); public abstract URL getURL(boolean isSecure, String server, int port, String bucket, String key, Map pathArgs) throws MalformedURLException; public static CallingFormat getPathCallingFormat() { return pathCallingFormat; } public static CallingFormat getSubdomainCallingFormat() { return subdomainCallingFormat; } public static CallingFormat getVanityCallingFormat() { return vanityCallingFormat; } private static class PathCallingFormat extends CallingFormat { public boolean supportsLocatedBuckets() { return false; } public String getPathBase(String bucket, String key) { return isBucketSpecified(bucket)? "/" + bucket + "/" + key : "/"; } public String getEndpoint(String server, int port, String bucket) { return server + ":" + port; } public URL getURL(boolean isSecure, String server, int port, String bucket, String key, Map pathArgs) throws MalformedURLException { String pathBase=isBucketSpecified(bucket)? "/" + bucket + "/" + key : "/"; String pathArguments=Utils.convertPathArgsHashToString(pathArgs); return new URL(isSecure? "https" : "http", server, port, pathBase + pathArguments); } private static boolean isBucketSpecified(String bucket) { return bucket != null && bucket.length() != 0; } } private static class SubdomainCallingFormat extends CallingFormat { public boolean supportsLocatedBuckets() { return true; } public String getServer(String server, String bucket) { return bucket + "." + server; } public String getEndpoint(String server, int port, String bucket) { return getServer(server, bucket) + ":" + port; } public String getPathBase(String bucket, String key) { return "/" + key; } public URL getURL(boolean isSecure, String server, int port, String bucket, String key, Map pathArgs) throws MalformedURLException { if(bucket == null || bucket.length() == 0) { //The bucket is null, this is listAllBuckets request String pathArguments=Utils.convertPathArgsHashToString(pathArgs); return new URL(isSecure? "https" : "http", server, port, "/" + pathArguments); } else { String serverToUse=getServer(server, bucket); String pathBase=getPathBase(bucket, key); String pathArguments=Utils.convertPathArgsHashToString(pathArgs); return new URL(isSecure? "https" : "http", serverToUse, port, pathBase + pathArguments); } } } private static class VanityCallingFormat extends SubdomainCallingFormat { public String getServer(String server, String bucket) { return bucket; } } } static class Utils { static final String METADATA_PREFIX="x-amz-meta-"; static final String AMAZON_HEADER_PREFIX="x-amz-"; static final String ALTERNATIVE_DATE_HEADER="x-amz-date"; public static final String DEFAULT_HOST="s3.amazonaws.com"; public static final int SECURE_PORT=443; public static final int INSECURE_PORT=80; /** * HMAC/SHA1 Algorithm per RFC 2104. */ private static final String HMAC_SHA1_ALGORITHM="HmacSHA1"; static String makeCanonicalString(String method, String bucket, String key, Map pathArgs, Map headers) { return makeCanonicalString(method, bucket, key, pathArgs, headers, null); } /** * Calculate the canonical string. When expires is non-null, it will be * used instead of the Date header. */ static String makeCanonicalString(String method, String bucketName, String key, Map pathArgs, Map headers, String expires) { StringBuilder buf=new StringBuilder(); buf.append(method + "\n"); // Add all interesting headers to a list, then sort them. "Interesting" // is defined as Content-MD5, Content-Type, Date, and x-amz- SortedMap interestingHeaders=new TreeMap(); if(headers != null) { for(Iterator i=headers.keySet().iterator(); i.hasNext();) { String hashKey=(String)i.next(); if(hashKey == null) continue; String lk=hashKey.toLowerCase(); // Ignore any headers that are not particularly interesting. if(lk.equals("content-type") || lk.equals("content-md5") || lk.equals("date") || lk.startsWith(AMAZON_HEADER_PREFIX)) { List s=(List)headers.get(hashKey); interestingHeaders.put(lk, concatenateList(s)); } } } if(interestingHeaders.containsKey(ALTERNATIVE_DATE_HEADER)) { interestingHeaders.put("date", ""); } // if the expires is non-null, use that for the date field. this // trumps the x-amz-date behavior. if(expires != null) { interestingHeaders.put("date", expires); } // these headers require that we still put a new line in after them, // even if they don't exist. if(!interestingHeaders.containsKey("content-type")) { interestingHeaders.put("content-type", ""); } if(!interestingHeaders.containsKey("content-md5")) { interestingHeaders.put("content-md5", ""); } // Finally, add all the interesting headers (i.e.: all that startwith x-amz- ;-)) for(Iterator i=interestingHeaders.keySet().iterator(); i.hasNext();) { String headerKey=(String)i.next(); if(headerKey.startsWith(AMAZON_HEADER_PREFIX)) { buf.append(headerKey).append(':').append(interestingHeaders.get(headerKey)); } else { buf.append(interestingHeaders.get(headerKey)); } buf.append("\n"); } // build the path using the bucket and key if(bucketName != null && bucketName.length() != 0) { buf.append("/" + bucketName); } // append the key (it might be an empty string) // append a slash regardless buf.append("/"); if(key != null) { buf.append(key); } // if there is an acl, logging or torrent parameter // add them to the string if(pathArgs != null) { if(pathArgs.containsKey("acl")) { buf.append("?acl"); } else if(pathArgs.containsKey("torrent")) { buf.append("?torrent"); } else if(pathArgs.containsKey("logging")) { buf.append("?logging"); } else if(pathArgs.containsKey("location")) { buf.append("?location"); } } return buf.toString(); } /** * Calculate the HMAC/SHA1 on a string. * @return Signature * @throws java.security.NoSuchAlgorithmException * If the algorithm does not exist. Unlikely * @throws java.security.InvalidKeyException * If the key is invalid. */ static String encode(String awsSecretAccessKey, String canonicalString, boolean urlencode) { // The following HMAC/SHA1 code for the signature is taken from the // AWS Platform's implementation of RFC2104 (amazon.webservices.common.Signature) // // Acquire an HMAC/SHA1 from the raw key bytes. SecretKeySpec signingKey= new SecretKeySpec(awsSecretAccessKey.getBytes(), HMAC_SHA1_ALGORITHM); // Acquire the MAC instance and initialize with the signing key. Mac mac=null; try { mac=Mac.getInstance(HMAC_SHA1_ALGORITHM); } catch(NoSuchAlgorithmException e) { // should not happen throw new RuntimeException("Could not find sha1 algorithm", e); } try { mac.init(signingKey); } catch(InvalidKeyException e) { // also should not happen throw new RuntimeException("Could not initialize the MAC algorithm", e); } // Compute the HMAC on the digest, and set it. String b64=Base64.encodeBytes(mac.doFinal(canonicalString.getBytes())); if(urlencode) { return urlencode(b64); } else { return b64; } } static Map paramsForListOptions(String prefix, String marker, Integer maxKeys) { return paramsForListOptions(prefix, marker, maxKeys, null); } static Map paramsForListOptions(String prefix, String marker, Integer maxKeys, String delimiter) { Map argParams=new HashMap(); // these three params must be url encoded if(prefix != null) argParams.put("prefix", urlencode(prefix)); if(marker != null) argParams.put("marker", urlencode(marker)); if(delimiter != null) argParams.put("delimiter", urlencode(delimiter)); if(maxKeys != null) argParams.put("max-keys", Integer.toString(maxKeys.intValue())); return argParams; } /** * Converts the Path Arguments from a map to String which can be used in url construction * @param pathArgs a map of arguments * @return a string representation of pathArgs */ public static String convertPathArgsHashToString(Map pathArgs) { StringBuilder pathArgsString=new StringBuilder(); String argumentValue; boolean firstRun=true; if(pathArgs != null) { for(Iterator argumentIterator=pathArgs.keySet().iterator(); argumentIterator.hasNext();) { String argument=(String)argumentIterator.next(); if(firstRun) { firstRun=false; pathArgsString.append("?"); } else { pathArgsString.append("&"); } argumentValue=(String)pathArgs.get(argument); pathArgsString.append(argument); if(argumentValue != null) { pathArgsString.append("="); pathArgsString.append(argumentValue); } } } return pathArgsString.toString(); } static String urlencode(String unencoded) { try { return URLEncoder.encode(unencoded, "UTF-8"); } catch(UnsupportedEncodingException e) { // should never happen throw new RuntimeException("Could not url encode to UTF-8", e); } } static XMLReader createXMLReader() { try { return XMLReaderFactory.createXMLReader(); } catch(SAXException e) { // oops, lets try doing this (needed in 1.4) System.setProperty("org.xml.sax.driver", "org.apache.crimson.parser.XMLReaderImpl"); } try { // try once more return XMLReaderFactory.createXMLReader(); } catch(SAXException e) { throw new RuntimeException("Couldn't initialize a sax driver for the XMLReader"); } } /** * Concatenates a bunch of header values, seperating them with a comma. * @param values List of header values. * @return String of all headers, with commas. */ private static String concatenateList(List values) { StringBuilder buf=new StringBuilder(); for(int i=0, size=values.size(); i < size; ++i) { buf.append(((String)values.get(i)).replaceAll("\n", "").trim()); if(i != (size - 1)) { buf.append(","); } } return buf.toString(); } /** * Validate bucket-name */ static boolean validateBucketName(String bucketName, CallingFormat callingFormat) { if(callingFormat == CallingFormat.getPathCallingFormat()) { final int MIN_BUCKET_LENGTH=3; final int MAX_BUCKET_LENGTH=255; final String BUCKET_NAME_REGEX="^[0-9A-Za-z\\.\\-_]*$"; return null != bucketName && bucketName.length() >= MIN_BUCKET_LENGTH && bucketName.length() <= MAX_BUCKET_LENGTH && bucketName.matches(BUCKET_NAME_REGEX); } else { return isValidSubdomainBucketName(bucketName); } } static boolean isValidSubdomainBucketName(String bucketName) { final int MIN_BUCKET_LENGTH=3; final int MAX_BUCKET_LENGTH=63; // don't allow names that look like 127.0.0.1 final String IPv4_REGEX="^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$"; // dns sub-name restrictions final String BUCKET_NAME_REGEX="^[a-z0-9]([a-z0-9\\-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9\\-]*[a-z0-9])?)*$"; // If there wasn't a location-constraint, then the current actual // restriction is just that no 'part' of the name (i.e. sequence // of characters between any 2 '.'s has to be 63) but the recommendation // is to keep the entire bucket name under 63. return null != bucketName && bucketName.length() >= MIN_BUCKET_LENGTH && bucketName.length() <= MAX_BUCKET_LENGTH && !bucketName.matches(IPv4_REGEX) && bucketName.matches(BUCKET_NAME_REGEX); } static CallingFormat getCallingFormatForBucket(CallingFormat desiredFormat, String bucketName) { CallingFormat callingFormat=desiredFormat; if(callingFormat == CallingFormat.getSubdomainCallingFormat() && !Utils.isValidSubdomainBucketName(bucketName)) { callingFormat=CallingFormat.getPathCallingFormat(); } return callingFormat; } } // // NOTE: The following source code is the iHarder.net public domain // Base64 library and is provided here as a convenience. For updates, // problems, questions, etc. regarding this code, please visit: // http://iharder.sourceforge.net/current/java/base64/ // /** * Encodes and decodes to and from Base64 notation. *

    *

    * Change Log: *

    *
      *
    • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added * some convenience methods for reading and writing to and from files.
    • *
    • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems * with other encodings (like EBCDIC).
    • *
    • v2.0.1 - Fixed an error when decoding a single byte, that is, when the * encoded data was a single byte.
    • *
    • v2.0 - I got rid of methods that used booleans to set options. * Now everything is more consolidated and cleaner. The code now detects * when data that's being decoded is gzip-compressed and will decompress it * automatically. Generally things are cleaner. You'll probably have to * change some method calls that you were making to support the new * options format (ints that you "OR" together).
    • *
    • v1.5.1 - Fixed bug when decompressing and decoding to a * byte[] using decode( String s, boolean gzipCompressed ). * Added the ability to "suspend" encoding in the Output Stream so * you can turn on and off the encoding if you need to embed base64 * data in an otherwise "normal" stream (like an XML file).
    • *
    • v1.5 - Output stream pases on flush() command but doesn't do anything itself. * This helps when using GZIP streams. * Added the ability to GZip-compress objects before encoding them.
    • *
    • v1.4 - Added helper methods to read/write files.
    • *
    • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
    • *
    • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream * where last buffer being read, if not completely full, was not returned.
    • *
    • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
    • *
    • v1.3.3 - Fixed I/O streams which were totally messed up.
    • *
    *

    *

    * I am placing this code in the Public Domain. Do with it as you will. * This software comes with no guarantees or warranties but with * plenty of well-wishing instead! * Please visit http://iharder.net/base64 * periodically to check for updates or to contribute improvements. *

    * @author Robert Harder * @author rob@iharder.net * @version 2.1 */ static class Base64 { /* ******** P U B L I C F I E L D S ******** */ /** * No options specified. Value is zero. */ public final static int NO_OPTIONS=0; /** * Specify encoding. */ public final static int ENCODE=1; /** * Specify decoding. */ public final static int DECODE=0; /** * Specify that data should be gzip-compressed. */ public final static int GZIP=2; /** * Don't break lines when encoding (violates strict Base64 specification) */ public final static int DONT_BREAK_LINES=8; /* ******** P R I V A T E F I E L D S ******** */ /** * Maximum line length (76) of Base64 output. */ private final static int MAX_LINE_LENGTH=76; /** * The equals sign (=) as a byte. */ private final static byte EQUALS_SIGN=(byte)'='; /** * The new line character (\n) as a byte. */ private final static byte NEW_LINE=(byte)'\n'; /** * Preferred encoding. */ private final static String PREFERRED_ENCODING="UTF-8"; /** * The 64 valid Base64 values. */ private static final byte[] ALPHABET; private static final byte[] _NATIVE_ALPHABET= /* May be something funny like EBCDIC */ { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' }; /** Determine which ALPHABET to use. */ static { byte[] __bytes; try { __bytes="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes(PREFERRED_ENCODING); } // end try catch(java.io.UnsupportedEncodingException use) { __bytes=_NATIVE_ALPHABET; // Fall back to native encoding } // end catch ALPHABET=__bytes; } // end static /** * Translates a Base64 value to either its 6-bit reconstruction value * or a negative number indicating some other meaning. */ private final static byte[] DECODABET= { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 -5, -5, // Whitespace: Tab and Linefeed -9, -9, // Decimal 11 - 12 -5, // Whitespace: Carriage Return -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 -9, -9, -9, -9, -9, // Decimal 27 - 31 -5, // Whitespace: Space -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 62, // Plus sign at decimal 43 -9, -9, -9, // Decimal 44 - 46 63, // Slash at decimal 47 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine -9, -9, -9, // Decimal 58 - 60 -1, // Equals sign at decimal 61 -9, -9, -9, // Decimal 62 - 64 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' -9, -9, -9, -9 // Decimal 123 - 126 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; // I think I end up not using the BAD_ENCODING indicator. //private final static byte BAD_ENCODING = -9; // Indicates error in encoding private final static byte WHITE_SPACE_ENC=-5; // Indicates white space in encoding private final static byte EQUALS_SIGN_ENC=-1; // Indicates equals sign in encoding /** * Defeats instantiation. */ private Base64() { } /* ******** E N C O D I N G M E T H O D S ******** */ /** * Encodes up to the first three bytes of array threeBytes * and returns a four-byte array in Base64 notation. * The actual number of significant bytes in your array is * given by numSigBytes. * The array threeBytes needs only be as big as * numSigBytes. * Code can reuse a byte array by passing a four-byte array as b4. * @param b4 A reusable byte array to reduce array instantiation * @param threeBytes the array to convert * @param numSigBytes the number of significant bytes in your array * @return four byte array in Base64 notation. * @since 1.5.1 */ private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes) { encode3to4(threeBytes, 0, numSigBytes, b4, 0); return b4; } // end encode3to4 /** * Encodes up to three bytes of the array source * and writes the resulting four Base64 bytes to destination. * The source and destination arrays can be manipulated * anywhere along their length by specifying * srcOffset and destOffset. * This method does not check to make sure your arrays * are large enough to accomodate srcOffset + 3 for * the source array or destOffset + 4 for * the destination array. * The actual number of significant bytes in your array is * given by numSigBytes. * @param source the array to convert * @param srcOffset the index where conversion begins * @param numSigBytes the number of significant bytes in your array * @param destination the array to hold the conversion * @param destOffset the index where output will be put * @return the destination array * @since 1.3 */ private static byte[] encode3to4( byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) { // 1 2 3 // 01234567890123456789012345678901 Bit position // --------000000001111111122222222 Array position from threeBytes // --------| || || || | Six bit groups to index ALPHABET // >>18 >>12 >> 6 >> 0 Right shift necessary // 0x3f 0x3f 0x3f Additional AND // Create buffer with zero-padding if there are only one or two // significant bytes passed in the array. // We have to shift left 24 in order to flush out the 1's that appear // when Java treats a value as negative that is cast from a byte to an int. int inBuff=(numSigBytes > 0? ((source[srcOffset] << 24) >>> 8) : 0) | (numSigBytes > 1? ((source[srcOffset + 1] << 24) >>> 16) : 0) | (numSigBytes > 2? ((source[srcOffset + 2] << 24) >>> 24) : 0); switch(numSigBytes) { case 3: destination[destOffset]=ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1]=ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2]=ALPHABET[(inBuff >>> 6) & 0x3f]; destination[destOffset + 3]=ALPHABET[(inBuff) & 0x3f]; return destination; case 2: destination[destOffset]=ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1]=ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2]=ALPHABET[(inBuff >>> 6) & 0x3f]; destination[destOffset + 3]=EQUALS_SIGN; return destination; case 1: destination[destOffset]=ALPHABET[(inBuff >>> 18)]; destination[destOffset + 1]=ALPHABET[(inBuff >>> 12) & 0x3f]; destination[destOffset + 2]=EQUALS_SIGN; destination[destOffset + 3]=EQUALS_SIGN; return destination; default: return destination; } // end switch } // end encode3to4 /** * Serializes an object and returns the Base64-encoded * version of that serialized object. If the object * cannot be serialized or there is another error, * the method will return null. * The object is not GZip-compressed before being encoded. * @param serializableObject The object to encode * @return The Base64-encoded object * @since 1.4 */ public static String encodeObject(java.io.Serializable serializableObject) { return encodeObject(serializableObject, NO_OPTIONS); } // end encodeObject /** * Serializes an object and returns the Base64-encoded * version of that serialized object. If the object * cannot be serialized or there is another error, * the method will return null. *

    * Valid options:

             *   GZIP: gzip-compresses object before encoding it.
             *   DONT_BREAK_LINES: don't break lines at 76 characters
             *     Note: Technically, this makes your encoding non-compliant.
             * 
    *

    * Example: encodeObject( myObj, Base64.GZIP ) or *

    * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) * @param serializableObject The object to encode * @param options Specified options * @return The Base64-encoded object * @see Base64#GZIP * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public static String encodeObject(java.io.Serializable serializableObject, int options) { // Streams java.io.ByteArrayOutputStream baos=null; java.io.OutputStream b64os=null; java.io.ObjectOutputStream oos=null; java.util.zip.GZIPOutputStream gzos=null; // Isolate options int gzip=(options & GZIP); int dontBreakLines=(options & DONT_BREAK_LINES); try { // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream baos=new java.io.ByteArrayOutputStream(); b64os=new Base64.OutputStream(baos, ENCODE | dontBreakLines); // GZip? if(gzip == GZIP) { gzos=new java.util.zip.GZIPOutputStream(b64os); oos=new java.io.ObjectOutputStream(gzos); } // end if: gzip else oos=new java.io.ObjectOutputStream(b64os); oos.writeObject(serializableObject); } // end try catch(java.io.IOException e) { e.printStackTrace(); return null; } // end catch finally { try { oos.close(); } catch(Exception e) { } try { gzos.close(); } catch(Exception e) { } try { b64os.close(); } catch(Exception e) { } try { baos.close(); } catch(Exception e) { } } // end finally // Return value according to relevant encoding. try { return new String(baos.toByteArray(), PREFERRED_ENCODING); } // end try catch(java.io.UnsupportedEncodingException uue) { return new String(baos.toByteArray()); } // end catch } // end encode /** * Encodes a byte array into Base64 notation. * Does not GZip-compress data. * @param source The data to convert * @since 1.4 */ public static String encodeBytes(byte[] source) { return encodeBytes(source, 0, source.length, NO_OPTIONS); } // end encodeBytes /** * Encodes a byte array into Base64 notation. *

    * Valid options:

             *   GZIP: gzip-compresses object before encoding it.
             *   DONT_BREAK_LINES: don't break lines at 76 characters
             *     Note: Technically, this makes your encoding non-compliant.
             * 
    *

    * Example: encodeBytes( myData, Base64.GZIP ) or *

    * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) * @param source The data to convert * @param options Specified options * @see Base64#GZIP * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public static String encodeBytes(byte[] source, int options) { return encodeBytes(source, 0, source.length, options); } // end encodeBytes /** * Encodes a byte array into Base64 notation. * Does not GZip-compress data. * @param source The data to convert * @param off Offset in array where conversion should begin * @param len Length of data to convert * @since 1.4 */ public static String encodeBytes(byte[] source, int off, int len) { return encodeBytes(source, off, len, NO_OPTIONS); } // end encodeBytes /** * Encodes a byte array into Base64 notation. *

    * Valid options:

             *   GZIP: gzip-compresses object before encoding it.
             *   DONT_BREAK_LINES: don't break lines at 76 characters
             *     Note: Technically, this makes your encoding non-compliant.
             * 
    *

    * Example: encodeBytes( myData, Base64.GZIP ) or *

    * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) * @param source The data to convert * @param off Offset in array where conversion should begin * @param len Length of data to convert * @param options Specified options * @see Base64#GZIP * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public static String encodeBytes(byte[] source, int off, int len, int options) { // Isolate options int dontBreakLines=(options & DONT_BREAK_LINES); int gzip=(options & GZIP); // Compress? if(gzip == GZIP) { java.io.ByteArrayOutputStream baos=null; java.util.zip.GZIPOutputStream gzos=null; Base64.OutputStream b64os=null; try { // GZip -> Base64 -> ByteArray baos=new java.io.ByteArrayOutputStream(); b64os=new Base64.OutputStream(baos, ENCODE | dontBreakLines); gzos=new java.util.zip.GZIPOutputStream(b64os); gzos.write(source, off, len); gzos.close(); } // end try catch(java.io.IOException e) { e.printStackTrace(); return null; } // end catch finally { try { gzos.close(); } catch(Exception e) { } try { b64os.close(); } catch(Exception e) { } try { baos.close(); } catch(Exception e) { } } // end finally // Return value according to relevant encoding. try { return new String(baos.toByteArray(), PREFERRED_ENCODING); } // end try catch(java.io.UnsupportedEncodingException uue) { return new String(baos.toByteArray()); } // end catch } // end if: compress // Else, don't compress. Better not to use streams at all then. else { // Convert option to boolean in way that code likes it. boolean breakLines=dontBreakLines == 0; int len43=len * 4 / 3; byte[] outBuff=new byte[(len43) // Main 4:3 + ((len % 3) > 0? 4 : 0) // Account for padding + (breakLines? (len43 / MAX_LINE_LENGTH) : 0)]; // New lines int d=0; int e=0; int len2=len - 2; int lineLength=0; for(; d < len2; d+=3, e+=4) { encode3to4(source, d + off, 3, outBuff, e); lineLength+=4; if(breakLines && lineLength == MAX_LINE_LENGTH) { outBuff[e + 4]=NEW_LINE; e++; lineLength=0; } // end if: end of line } // en dfor: each piece of array if(d < len) { encode3to4(source, d + off, len - d, outBuff, e); e+=4; } // end if: some padding needed // Return value according to relevant encoding. try { return new String(outBuff, 0, e, PREFERRED_ENCODING); } // end try catch(java.io.UnsupportedEncodingException uue) { return new String(outBuff, 0, e); } // end catch } // end else: don't compress } // end encodeBytes /* ******** D E C O D I N G M E T H O D S ******** */ /** * Decodes four bytes from array source * and writes the resulting bytes (up to three of them) * to destination. * The source and destination arrays can be manipulated * anywhere along their length by specifying * srcOffset and destOffset. * This method does not check to make sure your arrays * are large enough to accomodate srcOffset + 4 for * the source array or destOffset + 3 for * the destination array. * This method returns the actual number of bytes that * were converted from the Base64 encoding. * @param source the array to convert * @param srcOffset the index where conversion begins * @param destination the array to hold the conversion * @param destOffset the index where output will be put * @return the number of decoded bytes converted * @since 1.3 */ private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) { // Example: Dk== if(source[srcOffset + 2] == EQUALS_SIGN) { // Two ways to do the same thing. Don't know which way I like best. //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); int outBuff=((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12); destination[destOffset]=(byte)(outBuff >>> 16); return 1; } // Example: DkL= else if(source[srcOffset + 3] == EQUALS_SIGN) { // Two ways to do the same thing. Don't know which way I like best. //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); int outBuff=((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6); destination[destOffset]=(byte)(outBuff >>> 16); destination[destOffset + 1]=(byte)(outBuff >>> 8); return 2; } // Example: DkLE else { try { // Two ways to do the same thing. Don't know which way I like best. //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); int outBuff=((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF)); destination[destOffset]=(byte)(outBuff >> 16); destination[destOffset + 1]=(byte)(outBuff >> 8); destination[destOffset + 2]=(byte)(outBuff); return 3; } catch(Exception e) { System.out.println(valueOf(source[srcOffset]) + ": " + (DECODABET[source[srcOffset]])); System.out.println(valueOf(source[srcOffset + 1]) + ": " + (DECODABET[source[srcOffset + 1]])); System.out.println(valueOf(source[srcOffset + 2]) + ": " + (DECODABET[source[srcOffset + 2]])); System.out.println(String.valueOf(source[srcOffset + 3]) + ": " + (DECODABET[source[srcOffset + 3]])); return -1; } //e nd catch } } // end decodeToBytes /** * Very low-level access to decoding ASCII characters in * the form of a byte array. Does not support automatically * gunzipping or any other "fancy" features. * @param source The Base64 encoded data * @param off The offset of where to begin decoding * @param len The length of characters to decode * @return decoded data * @since 1.3 */ public static byte[] decode(byte[] source, int off, int len) { int len34=len * 3 / 4; byte[] outBuff=new byte[len34]; // Upper limit on size of output int outBuffPosn=0; byte[] b4=new byte[4]; int b4Posn=0; int i=0; byte sbiCrop=0; byte sbiDecode=0; for(i=off; i < off + len; i++) { sbiCrop=(byte)(source[i] & 0x7f); // Only the low seven bits sbiDecode=DECODABET[sbiCrop]; if(sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better { if(sbiDecode >= EQUALS_SIGN_ENC) { b4[b4Posn++]=sbiCrop; if(b4Posn > 3) { outBuffPosn+=decode4to3(b4, 0, outBuff, outBuffPosn); b4Posn=0; // If that was the equals sign, break out of 'for' loop if(sbiCrop == EQUALS_SIGN) break; } // end if: quartet built } // end if: equals sign or better } // end if: white space, equals sign or better else { System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)"); return null; } // end else: } // each input character byte[] out=new byte[outBuffPosn]; System.arraycopy(outBuff, 0, out, 0, outBuffPosn); return out; } // end decode /** * Decodes data from Base64 notation, automatically * detecting gzip-compressed data and decompressing it. * @param s the string to decode * @return the decoded data * @since 1.4 */ public static byte[] decode(String s) { byte[] bytes; try { bytes=s.getBytes(PREFERRED_ENCODING); } // end try catch(java.io.UnsupportedEncodingException uee) { bytes=s.getBytes(); } // end catch // // Decode bytes=decode(bytes, 0, bytes.length); // Check to see if it's gzip-compressed // GZIP Magic Two-Byte Number: 0x8b1f (35615) if(bytes != null && bytes.length >= 4) { int head=((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); if(java.util.zip.GZIPInputStream.GZIP_MAGIC == head) { java.io.ByteArrayInputStream bais=null; java.util.zip.GZIPInputStream gzis=null; java.io.ByteArrayOutputStream baos=null; byte[] buffer=new byte[2048]; int length=0; try { baos=new java.io.ByteArrayOutputStream(); bais=new java.io.ByteArrayInputStream(bytes); gzis=new java.util.zip.GZIPInputStream(bais); while((length=gzis.read(buffer)) >= 0) { baos.write(buffer, 0, length); } // end while: reading input // No error? Get new bytes. bytes=baos.toByteArray(); } // end try catch(java.io.IOException e) { // Just return originally-decoded bytes } // end catch finally { try { baos.close(); } catch(Exception e) { } try { gzis.close(); } catch(Exception e) { } try { bais.close(); } catch(Exception e) { } } // end finally } // end if: gzipped } // end if: bytes.length >= 2 return bytes; } // end decode /** * Attempts to decode Base64 data and deserialize a Java * Object within. Returns null if there was an error. * @param encodedObject The Base64 data to decode * @return The decoded and deserialized object * @since 1.5 */ public static Object decodeToObject(String encodedObject) { // Decode and gunzip if necessary byte[] objBytes=decode(encodedObject); java.io.ByteArrayInputStream bais=null; java.io.ObjectInputStream ois=null; Object obj=null; try { bais=new java.io.ByteArrayInputStream(objBytes); ois=new java.io.ObjectInputStream(bais); obj=ois.readObject(); } // end try catch(java.io.IOException e) { e.printStackTrace(); obj=null; } // end catch catch(java.lang.ClassNotFoundException e) { e.printStackTrace(); obj=null; } // end catch finally { try { if(bais != null) bais.close(); } catch(Exception e) { } try { if(ois != null) ois.close(); } catch(Exception e) { } } // end finally return obj; } // end decodeObject /** * Convenience method for encoding data to a file. * @param dataToEncode byte array of data to encode in base64 form * @param filename Filename for saving encoded data * @return true if successful, false otherwise * @since 2.1 */ public static boolean encodeToFile(byte[] dataToEncode, String filename) { boolean success=false; Base64.OutputStream bos=null; try { bos=new Base64.OutputStream( new java.io.FileOutputStream(filename), Base64.ENCODE); bos.write(dataToEncode); success=true; } // end try catch(java.io.IOException e) { success=false; } // end catch: IOException finally { try { if(bos != null) bos.close(); } catch(Exception e) { } } // end finally return success; } // end encodeToFile /** * Convenience method for decoding data to a file. * @param dataToDecode Base64-encoded data as a string * @param filename Filename for saving decoded data * @return true if successful, false otherwise * @since 2.1 */ public static boolean decodeToFile(String dataToDecode, String filename) { boolean success=false; Base64.OutputStream bos=null; try { bos=new Base64.OutputStream( new java.io.FileOutputStream(filename), Base64.DECODE); bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); success=true; } // end try catch(java.io.IOException e) { success=false; } // end catch: IOException finally { try { if(bos != null) bos.close(); } catch(Exception e) { } } // end finally return success; } // end decodeToFile /** * Convenience method for reading a base64-encoded * file and decoding it. * @param filename Filename for reading encoded data * @return decoded byte array or null if unsuccessful * @since 2.1 */ public static byte[] decodeFromFile(String filename) { byte[] decodedData=null; Base64.InputStream bis=null; try { // Set up some useful variables java.io.File file=new java.io.File(filename); byte[] buffer=null; int length=0; int numBytes=0; // Check for size of file if(file.length() > Integer.MAX_VALUE) { System.err.println("File is too big for this convenience method (" + file.length() + " bytes)."); return null; } // end if: file too big for int index buffer=new byte[(int)file.length()]; // Open a stream bis=new Base64.InputStream( new java.io.BufferedInputStream( new java.io.FileInputStream(file)), Base64.DECODE); // Read until done while((numBytes=bis.read(buffer, length, 4096)) >= 0) length+=numBytes; // Save in a variable to return decodedData=new byte[length]; System.arraycopy(buffer, 0, decodedData, 0, length); } // end try catch(java.io.IOException e) { System.err.println("Error decoding from file " + filename); } // end catch: IOException finally { try { if(bis != null) bis.close(); } catch(Exception e) { } } // end finally return decodedData; } // end decodeFromFile /** * Convenience method for reading a binary file * and base64-encoding it. * @param filename Filename for reading binary data * @return base64-encoded string or null if unsuccessful * @since 2.1 */ public static String encodeFromFile(String filename) { String encodedData=null; Base64.InputStream bis=null; try { // Set up some useful variables java.io.File file=new java.io.File(filename); byte[] buffer=new byte[(int)(file.length() * 1.4)]; int length=0; int numBytes=0; // Open a stream bis=new Base64.InputStream( new java.io.BufferedInputStream( new java.io.FileInputStream(file)), Base64.ENCODE); // Read until done while((numBytes=bis.read(buffer, length, 4096)) >= 0) length+=numBytes; // Save in a variable to return encodedData=new String(buffer, 0, length, Base64.PREFERRED_ENCODING); } // end try catch(java.io.IOException e) { System.err.println("Error encoding from file " + filename); } // end catch: IOException finally { try { if(bis != null) bis.close(); } catch(Exception e) { } } // end finally return encodedData; } // end encodeFromFile /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ /** * A {@link Base64.InputStream} will read data from another * java.io.InputStream, given in the constructor, * and encode/decode to/from Base64 notation on the fly. * @see Base64 * @since 1.3 */ public static class InputStream extends java.io.FilterInputStream { private boolean encode; // Encoding or decoding private int position; // Current position in the buffer private byte[] buffer; // Small buffer holding converted data private int bufferLength; // Length of buffer (3 or 4) private int numSigBytes; // Number of meaningful bytes in the buffer private int lineLength; private boolean breakLines; // Break lines at less than 80 characters /** * Constructs a {@link Base64.InputStream} in DECODE mode. * @param in the java.io.InputStream from which to read data. * @since 1.3 */ public InputStream(java.io.InputStream in) { this(in, DECODE); } // end constructor /** * Constructs a {@link Base64.InputStream} in * either ENCODE or DECODE mode. *

    * Valid options:

                 *   ENCODE or DECODE: Encode or Decode as data is read.
                 *   DONT_BREAK_LINES: don't break lines at 76 characters
                 *     (only meaningful when encoding)
                 *     Note: Technically, this makes your encoding non-compliant.
                 * 
    *

    * Example: new Base64.InputStream( in, Base64.DECODE ) * @param in the java.io.InputStream from which to read data. * @param options Specified options * @see Base64#ENCODE * @see Base64#DECODE * @see Base64#DONT_BREAK_LINES * @since 2.0 */ public InputStream(java.io.InputStream in, int options) { super(in); this.breakLines=(options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode=(options & ENCODE) == ENCODE; this.bufferLength=encode? 4 : 3; this.buffer=new byte[bufferLength]; this.position=-1; this.lineLength=0; } // end constructor /** * Reads enough of the input stream to convert * to/from Base64 and returns the next byte. * @return next byte * @since 1.3 */ public int read() throws java.io.IOException { // Do we need to get data? if(position < 0) { if(encode) { byte[] b3=new byte[3]; int numBinaryBytes=0; for(int i=0; i < 3; i++) { try { int b=in.read(); // If end of stream, b is -1. if(b >= 0) { b3[i]=(byte)b; numBinaryBytes++; } // end if: not end of stream } // end try: read catch(java.io.IOException e) { // Only a problem if we got no data at all. if(i == 0) throw e; } // end catch } // end for: each needed input byte if(numBinaryBytes > 0) { encode3to4(b3, 0, numBinaryBytes, buffer, 0); position=0; numSigBytes=4; } // end if: got data else { return -1; } // end else } // end if: encoding // Else decoding else { byte[] b4=new byte[4]; int i=0; for(i=0; i < 4; i++) { // Read four "meaningful" bytes: int b=0; do { b=in.read(); } while(b >= 0 && DECODABET[b & 0x7f] <= WHITE_SPACE_ENC); if(b < 0) break; // Reads a -1 if end of stream b4[i]=(byte)b; } // end for: each needed input byte if(i == 4) { numSigBytes=decode4to3(b4, 0, buffer, 0); position=0; } // end if: got four characters else if(i == 0) { return -1; } // end else if: also padded correctly else { // Must have broken out from above. throw new java.io.IOException("Improperly padded Base64 input."); } // end } // end else: decode } // end else: get data // Got data? if(position >= 0) { // End of relevant data? if( /*!encode &&*/ position >= numSigBytes) return -1; if(encode && breakLines && lineLength >= MAX_LINE_LENGTH) { lineLength=0; return '\n'; } // end if else { lineLength++; // This isn't important when decoding // but throwing an extra "if" seems // just as wasteful. int b=buffer[position++]; if(position >= bufferLength) position=-1; return b & 0xFF; // This is how you "cast" a byte that's // intended to be unsigned. } // end else } // end if: position >= 0 // Else error else { // When JDK1.4 is more accepted, use an assertion here. throw new java.io.IOException("Error in Base64 code reading stream."); } // end else } // end read /** * Calls {@link #read()} repeatedly until the end of stream * is reached or len bytes are read. * Returns number of bytes read into array or -1 if * end of stream is encountered. * @param dest array to hold values * @param off offset for array * @param len max number of bytes to read into array * @return bytes read into array or -1 if end of stream is encountered. * @since 1.3 */ public int read(byte[] dest, int off, int len) throws java.io.IOException { int i; int b; for(i=0; i < len; i++) { b=read(); //if( b < 0 && i == 0 ) // return -1; if(b >= 0) dest[off + i]=(byte)b; else if(i == 0) return -1; else break; // Out of 'for' loop } // end for: each byte read return i; } // end read } // end inner class InputStream /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ /** * A {@link Base64.OutputStream} will write data to another * java.io.OutputStream, given in the constructor, * and encode/decode to/from Base64 notation on the fly. * @see Base64 * @since 1.3 */ public static class OutputStream extends java.io.FilterOutputStream { private boolean encode; private int position; private byte[] buffer; private int bufferLength; private int lineLength; private boolean breakLines; private byte[] b4; // Scratch used in a few places private boolean suspendEncoding; /** * Constructs a {@link Base64.OutputStream} in ENCODE mode. * @param out the java.io.OutputStream to which data will be written. * @since 1.3 */ public OutputStream(java.io.OutputStream out) { this(out, ENCODE); } // end constructor /** * Constructs a {@link Base64.OutputStream} in * either ENCODE or DECODE mode. *

    * Valid options:

                 *   ENCODE or DECODE: Encode or Decode as data is read.
                 *   DONT_BREAK_LINES: don't break lines at 76 characters
                 *     (only meaningful when encoding)
                 *     Note: Technically, this makes your encoding non-compliant.
                 * 
    *

    * Example: new Base64.OutputStream( out, Base64.ENCODE ) * @param out the java.io.OutputStream to which data will be written. * @param options Specified options. * @see Base64#ENCODE * @see Base64#DECODE * @see Base64#DONT_BREAK_LINES * @since 1.3 */ public OutputStream(java.io.OutputStream out, int options) { super(out); this.breakLines=(options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode=(options & ENCODE) == ENCODE; this.bufferLength=encode? 3 : 4; this.buffer=new byte[bufferLength]; this.position=0; this.lineLength=0; this.suspendEncoding=false; this.b4=new byte[4]; } // end constructor /** * Writes the byte to the output stream after * converting to/from Base64 notation. * When encoding, bytes are buffered three * at a time before the output stream actually * gets a write() call. * When decoding, bytes are buffered four * at a time. * @param theByte the byte to write * @since 1.3 */ public void write(int theByte) throws java.io.IOException { // Encoding suspended? if(suspendEncoding) { super.out.write(theByte); return; } // end if: supsended // Encode? if(encode) { buffer[position++]=(byte)theByte; if(position >= bufferLength) // Enough to encode. { out.write(encode3to4(b4, buffer, bufferLength)); lineLength+=4; if(breakLines && lineLength >= MAX_LINE_LENGTH) { out.write(NEW_LINE); lineLength=0; } // end if: end of line position=0; } // end if: enough to output } // end if: encoding // Else, Decoding else { // Meaningful Base64 character? if(DECODABET[theByte & 0x7f] > WHITE_SPACE_ENC) { buffer[position++]=(byte)theByte; if(position >= bufferLength) // Enough to output. { int len=Base64.decode4to3(buffer, 0, b4, 0); out.write(b4, 0, len); //out.write( Base64.decode4to3( buffer ) ); position=0; } // end if: enough to output } // end if: meaningful base64 character else if(DECODABET[theByte & 0x7f] != WHITE_SPACE_ENC) { throw new java.io.IOException("Invalid character in Base64 data."); } // end else: not white space either } // end else: decoding } // end write /** * Calls {@link #write(int)} repeatedly until len * bytes are written. * @param theBytes array from which to read bytes * @param off offset for array * @param len max number of bytes to read into array * @since 1.3 */ public void write(byte[] theBytes, int off, int len) throws java.io.IOException { // Encoding suspended? if(suspendEncoding) { super.out.write(theBytes, off, len); return; } // end if: supsended for(int i=0; i < len; i++) { write(theBytes[off + i]); } // end for: each byte written } // end write /** * Method added by PHIL. [Thanks, PHIL. -Rob] * This pads the buffer without closing the stream. */ public void flushBase64() throws java.io.IOException { if(position > 0) { if(encode) { out.write(encode3to4(b4, buffer, position)); position=0; } // end if: encoding else { throw new java.io.IOException("Base64 input not properly padded."); } // end else: decoding } // end if: buffer partially full } // end flush /** * Flushes and closes (I think, in the superclass) the stream. * @since 1.3 */ public void close() throws java.io.IOException { // 1. Ensure that pending characters are written flushBase64(); // 2. Actually close the stream // Base class both flushes and closes. super.close(); buffer=null; out=null; } // end close /** * Suspends encoding of the stream. * May be helpful if you need to embed a piece of * base640-encoded data in a stream. * @since 1.5.1 */ public void suspendEncoding() throws java.io.IOException { flushBase64(); this.suspendEncoding=true; } // end suspendEncoding /** * Resumes encoding of the stream. * May be helpful if you need to embed a piece of * base640-encoded data in a stream. * @since 1.5.1 */ public void resumeEncoding() { this.suspendEncoding=false; } // end resumeEncoding } // end inner class OutputStream } // end class Base64 } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/DELAY.java0000644000175000017500000000466511366547366025145 0ustar twernertwerner// $Id: DELAY.java,v 1.10 2007/01/12 14:19:35 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.util.Properties; /** * Delays incoming/outgoing messages by a random number of milliseconds (range between 0 and n * where n is determined by the user). Incoming messages can be delayed independently from * outgoing messages (or not delayed at all).

    * This protocol should be inserted directly above the bottommost protocol (e.g. UDP). */ public class DELAY extends Protocol { int in_delay=0, out_delay=0; /** * All protocol names have to be unique ! */ public String getName() { return "DELAY"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("in_delay"); if(str != null) { in_delay=Integer.parseInt(str); props.remove("in_delay"); } str=props.getProperty("out_delay"); if(str != null) { out_delay=Integer.parseInt(str); props.remove("out_delay"); } if(!props.isEmpty()) { log.error("DELAY.setProperties(): these properties are not recognized: " + props); return false; } return true; } public Object up(Event evt) { int delay=in_delay > 0 ? computeDelay(in_delay) : 0; switch(evt.getType()) { case Event.MSG: // Only delay messages, not other events ! if(log.isInfoEnabled()) log.info("delaying incoming message for " + delay + " milliseconds"); Util.sleep(delay); break; } return up_prot.up(evt); // Pass up to the layer above us } public Object down(Event evt) { int delay=out_delay > 0 ? computeDelay(out_delay) : 0; switch(evt.getType()) { case Event.MSG: // Only delay messages, not other events ! if(log.isInfoEnabled()) log.info("delaying outgoing message for " + delay + " milliseconds"); Util.sleep(delay); break; } return down_prot.down(evt); // Pass on to the layer below us } /** * Compute a random number between 0 and n */ static int computeDelay(int n) { return (int)((Math.random() * 1000000) % n); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/SHUFFLE.java0000644000175000017500000000635311366547366025377 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.Protocol; import java.util.*; /** * This layer shuffles upcoming messages, put it just above your bottom layer. * If you system sends less than 2 messages per sec you can notice a latency due * to this layer. * * @author Gianluca Collot * */ public class SHUFFLE extends Protocol implements Runnable { String name="SHUFFLE"; final List messages; Thread messagesHandler; public SHUFFLE() { messages = Collections.synchronizedList(new ArrayList()); } public String getName() { return name; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("name"); if(str != null) { name=str; props.remove("name"); } if(!props.isEmpty()) { log.error("DUMMY.setProperties(): these properties are not recognized: " + props); return false; } return true; } /** * Adds upcoming messages to the messages List<\code> where the messagesHandler<\code> * retrieves them. */ public Object up(Event evt) { Message msg; switch (evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); // Do something with the event, e.g. extract the message and remove a header. // Optionally pass up messages.add(msg); return null; } return up_prot.up(evt); // Pass up to the layer above us } /** * Starts the messagesHandler<\code> */ public void start() throws Exception { messagesHandler = new Thread(this,"MessagesHandler"); messagesHandler.setDaemon(true); messagesHandler.start(); } /** * Stops the messagesHandler */ public void stop() { Thread tmp = messagesHandler; messagesHandler = null; try { tmp.join(); } catch (Exception ex) {ex.printStackTrace();} } /** * Removes a random chosen message from the messages List<\code> if there * are less than 10 messages in the List it waits some time to ensure to chose from * a set of messages > 1. */ public void run() { Message msg; while (messagesHandler != null) { if (!messages.isEmpty()) { msg = (Message) messages.remove(rnd(messages.size())); up_prot.up(new Event(Event.MSG,msg)); } if (messages.size() < 5) { try { Thread.sleep(300); /** @todo make this time user configurable */ } catch (Exception ex) { ex.printStackTrace(); } } }// while // PassUp remaining messages Iterator iter = messages.iterator(); while (iter.hasNext()) { msg = (Message) iter.next(); up_prot.up(new Event(Event.MSG,msg)); } } // random integer between 0 and n-1 int rnd(int n) { return (int)(Math.random()*n); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/AUTH.java0000644000175000017500000001740611366547366025045 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.auth.AuthToken; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.JoinRsp; import org.jgroups.stack.Protocol; import java.util.Properties; /** * The AUTH protocol adds a layer of authentication to JGroups * @author Chris Mills */ public class AUTH extends Protocol{ static final String NAME = "AUTH"; /** * used on the coordinator to authentication joining member requests against */ private AuthToken serverSideToken = null; public AUTH(){ } public boolean setProperties(Properties props) { String authClassString = props.getProperty("auth_class"); if(authClassString != null){ props.remove("auth_class"); try{ Object obj = Class.forName(authClassString).newInstance(); serverSideToken = (AuthToken) obj; serverSideToken.setAuth(this); serverSideToken.setValue(props); }catch(Exception e){ if(log.isFatalEnabled()){ log.fatal("Failed to create server side token (" + authClassString + ")"); log.fatal(e); } return false; } } if(!props.isEmpty()) { //this should never happen as everything is read in to the AuthToken instance if(log.isErrorEnabled()){ log.error("AUTH.setProperties(): the following properties are not recognized: " + props); } return false; } return true; } public final String getName() { return AUTH.NAME; } /** * Used to create a failed JOIN_RSP message to pass back down the stack * @param joiner The Address of the requesting member * @param message The failure message to send back to the joiner * @return An Event containing a GmsHeader with a JoinRsp object */ private Event createFailureEvent(Address joiner, String message){ Message msg = new Message(joiner, null, null); if(log.isDebugEnabled()){ log.debug("Creating JoinRsp with failure message - " + message); } JoinRsp joinRes = new JoinRsp(message); //need to specify the error message on the JoinRsp object once it's been changed GMS.GmsHeader gmsHeader = new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP, joinRes); msg.putHeader(GMS.name, gmsHeader); if(log.isDebugEnabled()){ log.debug("GMSHeader created for failure JOIN_RSP"); } return new Event(Event.MSG, msg); } /** * An event was received from the layer below. Usually the current layer will want to examine * the event type and - depending on its type - perform some computation * (e.g. removing headers from a MSG event type, or updating the internal membership list * when receiving a VIEW_CHANGE event). * Finally the event is either a) discarded, or b) an event is sent down * the stack using down_prot.down() or c) the event (or another event) is sent up * the stack using up_prot.up(). */ public Object up(Event evt) { GMS.GmsHeader hdr = isJoinMessage(evt); if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ || hdr.getType() == GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER)){ if(log.isDebugEnabled()){ log.debug("AUTH got up event"); } //we found a join message - now try and get the AUTH Header Message msg = (Message)evt.getArg(); if((msg.getHeader(AUTH.NAME) != null) && (msg.getHeader(AUTH.NAME) instanceof AuthHeader)){ AuthHeader authHeader = (AuthHeader)msg.getHeader(AUTH.NAME); if(authHeader != null){ //Now we have the AUTH Header we need to validate it if(this.serverSideToken.authenticate(authHeader.getToken(), msg)){ //valid token if(log.isDebugEnabled()){ log.debug("AUTH passing up event"); } up_prot.up(evt); }else{ //invalid token if(log.isWarnEnabled()){ log.warn("AUTH failed to validate AuthHeader token"); } sendRejectionMessage(msg.getSrc(), createFailureEvent(msg.getSrc(), "Authentication failed")); } }else{ //Invalid AUTH Header - need to send failure message if(log.isWarnEnabled()){ log.warn("AUTH failed to get valid AuthHeader from Message"); } sendRejectionMessage(msg.getSrc(), createFailureEvent(msg.getSrc(), "Failed to find valid AuthHeader in Message")); } }else{ if(log.isDebugEnabled()){ log.debug("No AUTH Header Found"); } //should be a failure sendRejectionMessage(msg.getSrc(), createFailureEvent(msg.getSrc(), "Failed to find an AuthHeader in Message")); } } else { return up_prot.up(evt); } return null; } private void sendRejectionMessage(Address dest, Event join_rsp) { if(dest == null) { log.error("destination is null, cannot send JOIN rejection message to null destination"); return; } down_prot.down(join_rsp); } /** * An event is to be sent down the stack. The layer may want to examine its type and perform * some action on it, depending on the event's type. If the event is a message MSG, then * the layer may need to add a header to it (or do nothing at all) before sending it down * the stack using down_prot.down(). In case of a GET_ADDRESS event (which tries to * retrieve the stack's address from one of the bottom layers), the layer may need to send * a new response event back up the stack using up_prot.up(). */ public Object down(Event evt) { GMS.GmsHeader hdr = isJoinMessage(evt); if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ || hdr.getType() == GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER)){ if(log.isDebugEnabled()){ log.debug("AUTH got down event"); } //we found a join request message - now add an AUTH Header Message msg = (Message)evt.getArg(); AuthHeader authHeader = new AuthHeader(); authHeader.setToken(this.serverSideToken); msg.putHeader(AUTH.NAME, authHeader); if(log.isDebugEnabled()){ log.debug("AUTH passing down event"); } } if((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_RSP)){ if(log.isDebugEnabled()){ log.debug(hdr.toString()); } } return down_prot.down(evt); } /** * Used to check if the message type is a Gms message * @param evt The event object passed in to AUTH * @return A GmsHeader object or null if the event contains a message of a different type */ private static GMS.GmsHeader isJoinMessage(Event evt){ Message msg; switch(evt.getType()){ case Event.MSG: msg = (Message)evt.getArg(); Object obj = msg.getHeader("GMS"); if(obj == null || !(obj instanceof GMS.GmsHeader)){ return null; } return (GMS.GmsHeader)obj; } return null; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/package.html0000644000175000017500000000024311366547366025711 0ustar twernertwerner Provides implementations of transport protocols which are responsible for sending and receiving messages to/from the network. libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FD.java0000644000175000017500000006135511366547366024577 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import java.io.*; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Failure detection based on simple heartbeat protocol. Regularly polls members for * liveness. Multicasts SUSPECT messages when a member is not reachable. The simple * algorithms works as follows: the membership is known and ordered. Each HB protocol * periodically sends an 'are-you-alive' message to its *neighbor*. A neighbor is the next in * rank in the membership list, which is recomputed upon a view change. When a response hasn't * been received for n milliseconds and m tries, the corresponding member is suspected (and * eventually excluded if faulty).

    * FD starts when it detects (in a view change notification) that there are at least * 2 members in the group. It stops running when the membership drops below 2.

    * When a message is received from the monitored neighbor member, it causes the pinger thread to * 'skip' sending the next are-you-alive message. Thus, traffic is reduced.

    * When we receive a ping from a member that's not in the membership list, we shun it by sending it a * NOT_MEMBER message. That member will then leave the group (and possibly rejoin). This is only done if * shun is true. * @author Bela Ban * @version $Id: FD.java,v 1.58.2.4 2009/09/10 10:53:39 belaban Exp $ */ public class FD extends Protocol { Address local_addr=null; long timeout=3000; // number of millisecs to wait for an are-you-alive msg long last_ack=System.currentTimeMillis(); int num_tries=0; int max_tries=2; // number of times to send a are-you-alive msg (tot time= max_tries*timeout) protected final Lock lock=new ReentrantLock(); @GuardedBy("lock") Address ping_dest=null; @GuardedBy("lock") final List

    members=new ArrayList
    (); /** Members from which we select ping_dest. may be subset of {@link #members} */ @GuardedBy("lock") final List
    pingable_mbrs=new ArrayList
    (); // number of pings from suspected mbrs @GuardedBy("lock") final Map invalid_pingers=new HashMap(7); boolean shun=true; TimeScheduler timer=null; @GuardedBy("lock") private Future monitor_future=null; // task that performs the actual monitoring for failure detection protected int num_heartbeats=0; protected int num_suspect_events=0; /** Transmits SUSPECT message until view change or UNSUSPECT is received */ protected final Broadcaster bcast_task=new Broadcaster(); final static String name="FD"; final BoundedList
    suspect_history=new BoundedList
    (20); public String getName() {return name;} public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} public String getMembers() {return members != null? members.toString() : "null";} public String getPingableMembers() {return pingable_mbrs != null? pingable_mbrs.toString() : "null";} public String getPingDest() {return ping_dest != null? ping_dest.toString() : "null";} public int getNumberOfHeartbeatsSent() {return num_heartbeats;} public int getNumSuspectEventsGenerated() {return num_suspect_events;} public long getTimeout() {return timeout;} public void setTimeout(long timeout) {this.timeout=timeout;} public int getMaxTries() {return max_tries;} public void setMaxTries(int max_tries) {this.max_tries=max_tries;} public int getCurrentNumTries() {return num_tries;} public boolean isShun() {return shun;} public void setShun(boolean flag) {this.shun=flag;} public String printSuspectHistory() { StringBuilder sb=new StringBuilder(); for(Address addr: suspect_history) { sb.append(new Date()).append(": ").append(addr).append("\n"); } return sb.toString(); } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("timeout"); if(str != null) { timeout=Long.parseLong(str); props.remove("timeout"); } str=props.getProperty("max_tries"); // before suspecting a member if(str != null) { max_tries=Integer.parseInt(str); props.remove("max_tries"); } str=props.getProperty("shun"); if(str != null) { shun=Boolean.valueOf(str).booleanValue(); props.remove("shun"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void resetStats() { num_heartbeats=num_suspect_events=0; suspect_history.clear(); } public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); } public void stop() { lock.lock(); try { stopMonitor(); } finally { lock.unlock(); } } private Address getPingDest(List
    mbrs) { Address tmp, retval=null; if(mbrs == null || mbrs.size() < 2 || local_addr == null) return null; for(int i=0; i < mbrs.size(); i++) { tmp=mbrs.get(i); if(local_addr.equals(tmp)) { if(i + 1 >= mbrs.size()) retval=mbrs.get(0); else retval=mbrs.get(i + 1); break; } } return retval; } /** Requires lock to held by caller */ @GuardedBy("lock") private void startMonitor() { if(monitor_future == null || monitor_future.isDone()) { last_ack=System.currentTimeMillis(); // start from scratch monitor_future=timer.scheduleWithFixedDelay(new Monitor(), timeout, timeout, TimeUnit.MILLISECONDS); num_tries=0; } } /** Requires lock to be held by caller */ @GuardedBy("lock") private void stopMonitor() { if(monitor_future != null) { monitor_future.cancel(true); monitor_future=null; } } /** Restarts the monitor if the ping destination has changed. If not, this is a no-op. * Requires lock to be held by the caller */ @GuardedBy("lock") private void restartMonitor() { Address tmp_dest=getPingDest(pingable_mbrs); boolean restart_monitor=tmp_dest == null || ping_dest == null || // tmp_dest != null && ping_dest == null !ping_dest.equals(tmp_dest); // tmp_dest != null && ping_dest != null if(restart_monitor) { ping_dest=tmp_dest; stopMonitor(); if(ping_dest != null) { try { startMonitor(); } catch(Exception ex) { if(log.isWarnEnabled()) log.warn("exception when calling startMonitor(): " + ex); } } } } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: Message msg=(Message)evt.getArg(); FdHeader hdr=(FdHeader)msg.getHeader(name); if(hdr == null) { updateTimestamp(msg.getSrc()); break; // message did not originate from FD layer, just pass up } switch(hdr.type) { case FdHeader.HEARTBEAT: // heartbeat request; send heartbeat ack Address hb_sender=msg.getSrc(); if(log.isTraceEnabled()) log.trace("received are-you-alive from " + hb_sender + ", sending response"); sendHeartbeatResponse(hb_sender); // 2. Shun the sender of a HEARTBEAT message if that sender is not a member. This will cause // the sender to leave the group (and possibly rejoin it later) if(shun) shunInvalidHeartbeatSender(hb_sender); break; // don't pass up ! case FdHeader.HEARTBEAT_ACK: // heartbeat ack updateTimestamp(hdr.from); break; case FdHeader.SUSPECT: if(hdr.mbrs != null) { if(log.isTraceEnabled()) log.trace("[SUSPECT] suspect hdr is " + hdr); for(int i=0; i < hdr.mbrs.size(); i++) { Address m=hdr.mbrs.elementAt(i); if(local_addr != null && m.equals(local_addr)) { if(log.isWarnEnabled()) log.warn("I was suspected by " + msg.getSrc() + "; ignoring the SUSPECT " + "message and sending back a HEARTBEAT_ACK"); sendHeartbeatResponse(msg.getSrc()); continue; } else { lock.lock(); try { pingable_mbrs.remove(m); restartMonitor(); } finally { lock.unlock(); } } up_prot.up(new Event(Event.SUSPECT, m)); down_prot.down(new Event(Event.SUSPECT, m)); } } break; case FdHeader.NOT_MEMBER: if(shun) { if(log.isDebugEnabled()) log.debug("[NOT_MEMBER] I'm being shunned; exiting"); up_prot.up(new Event(Event.EXIT)); } break; } return null; } return up_prot.up(evt); // pass up to the layer above us } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: down_prot.down(evt); lock.lock(); try { View v=(View)evt.getArg(); members.clear(); members.addAll(v.getMembers()); bcast_task.adjustSuspectedMembers(members); pingable_mbrs.clear(); pingable_mbrs.addAll(members); restartMonitor(); } finally { lock.unlock(); } return null; case Event.UNSUSPECT: unsuspect((Address)evt.getArg()); return down_prot.down(evt); } return down_prot.down(evt); } private void sendHeartbeatResponse(Address dest) { Message hb_ack=new Message(dest, null, null); hb_ack.setFlag(Message.OOB); FdHeader tmp_hdr=new FdHeader(FdHeader.HEARTBEAT_ACK); tmp_hdr.from=local_addr; hb_ack.putHeader(name, tmp_hdr); down_prot.down(new Event(Event.MSG, hb_ack)); } @GuardedBy("lock") private void unsuspect(Address mbr) { lock.lock(); try { bcast_task.removeSuspectedMember(mbr); pingable_mbrs.clear(); pingable_mbrs.addAll(members); pingable_mbrs.removeAll(bcast_task.getSuspectedMembers()); restartMonitor(); } finally { lock.unlock(); } } @GuardedBy("lock") private void updateTimestamp(Address sender) { if(ping_dest != null && sender != null && ping_dest.equals(sender)) { long tmp=System.currentTimeMillis(); lock.lock(); try { last_ack=tmp; num_tries=0; } finally { lock.unlock(); } } } /** * If sender is not a member, send a NOT_MEMBER to sender (after n pings received) */ private void shunInvalidHeartbeatSender(Address hb_sender) { int num_pings=0; Message shun_msg=null; lock.lock(); try { if(hb_sender != null && members != null && !members.contains(hb_sender)) { if(invalid_pingers.containsKey(hb_sender)) { num_pings=invalid_pingers.get(hb_sender).intValue(); if(num_pings >= max_tries) { if(log.isDebugEnabled()) log.debug(hb_sender + " is not in " + members + " ! Shunning it"); shun_msg=new Message(hb_sender, null, null); shun_msg.setFlag(Message.OOB); shun_msg.putHeader(name, new FdHeader(FdHeader.NOT_MEMBER)); invalid_pingers.remove(hb_sender); } else { num_pings++; invalid_pingers.put(hb_sender, new Integer(num_pings)); } } else { num_pings++; invalid_pingers.put(hb_sender, new Integer(num_pings)); } } } finally { lock.unlock(); } if(shun_msg != null) down_prot.down(new Event(Event.MSG, shun_msg)); } public static class FdHeader extends Header implements Streamable { public static final byte HEARTBEAT=0; public static final byte HEARTBEAT_ACK=1; public static final byte SUSPECT=2; public static final byte NOT_MEMBER=3; // received as response by pinged mbr when we are not a member byte type=HEARTBEAT; Vector
    mbrs=null; Address from=null; // member who detected that suspected_mbr has failed private static final long serialVersionUID=-6387039473828820899L; public FdHeader() { } // used for externalization public FdHeader(byte type) { this.type=type; } public FdHeader(byte type, Vector
    mbrs, Address from) { this(type); this.mbrs=mbrs; this.from=from; } public String toString() { switch(type) { case HEARTBEAT: return "heartbeat"; case HEARTBEAT_ACK: return "heartbeat ack"; case SUSPECT: return "SUSPECT (suspected_mbrs=" + mbrs + ", from=" + from + ")"; case NOT_MEMBER: return "NOT_MEMBER"; default: return "unknown type (" + type + ")"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); if(mbrs == null) out.writeBoolean(false); else { out.writeBoolean(true); out.writeInt(mbrs.size()); for(Iterator it=mbrs.iterator(); it.hasNext();) { Address addr=(Address)it.next(); Marshaller.write(addr, out); } } Marshaller.write(from, out); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); boolean mbrs_not_null=in.readBoolean(); if(mbrs_not_null) { int len=in.readInt(); mbrs=new Vector
    (11); for(int i=0; i < len; i++) { Address addr=(Address)Marshaller.read(in); mbrs.add(addr); } } from=(Address)Marshaller.read(in); } public int size() { int retval=Global.BYTE_SIZE; // type retval+=Util.size(mbrs); retval+=Util.size(from); return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); Util.writeAddresses(mbrs, out); Util.writeAddress(from, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); mbrs=(Vector
    )Util.readAddresses(in, Vector.class); from=Util.readAddress(in); } } protected class Monitor implements Runnable { public void run() { Message hb_req; long not_heard_from; // time in msecs we haven't heard from ping_dest Address dest=null; lock.lock(); try { if(ping_dest == null) { if(log.isWarnEnabled()) log.warn("ping_dest is null: members=" + members + ", pingable_mbrs=" + pingable_mbrs + ", local_addr=" + local_addr); return; } else dest=ping_dest; } finally { lock.unlock(); } // 1. send heartbeat request hb_req=new Message(dest, null, null); hb_req.setFlag(Message.OOB); hb_req.putHeader(name, new FdHeader(FdHeader.HEARTBEAT)); // send heartbeat request if(log.isDebugEnabled()) log.debug("sending are-you-alive msg to " + dest + " (own address=" + local_addr + ')'); down_prot.down(new Event(Event.MSG, hb_req)); num_heartbeats++; // 2. If the time of the last heartbeat is > timeout and max_tries heartbeat messages have not been // received, then broadcast a SUSPECT message. Will be handled by coordinator, which may install // a new view not_heard_from=System.currentTimeMillis() - last_ack; // quick & dirty fix: increase timeout by 500msecs to allow for latency (bela June 27 2003) if(not_heard_from > timeout + 500) { // no heartbeat ack for more than timeout msecs if(num_tries >= max_tries) { if(log.isDebugEnabled()) log.debug("[" + local_addr + "]: received no heartbeat ack from " + dest + " for " + (num_tries +1) + " times (" + ((num_tries+1) * timeout) + " milliseconds), suspecting it"); // broadcast a SUSPECT message to all members - loop until // unsuspect or view change is received bcast_task.addSuspectedMember(dest); num_tries=0; if(stats) { num_suspect_events++; suspect_history.add(dest); } } else { if(log.isDebugEnabled()) log.debug("heartbeat missing from " + dest + " (number=" + num_tries + ')'); num_tries++; } } } } /** * Task that periodically broadcasts a list of suspected members to the group. Goal is not to lose * a SUSPECT message: since these are bcast unreliably, they might get dropped. The BroadcastTask makes * sure they are retransmitted until a view has been received which doesn't contain the suspected members * any longer. Then the task terminates. */ protected final class Broadcaster { final Vector
    suspected_mbrs=new Vector
    (7); final Lock bcast_lock=new ReentrantLock(); @GuardedBy("bcast_lock") Future bcast_future=null; @GuardedBy("bcast_lock") BroadcastTask task; Vector getSuspectedMembers() { return suspected_mbrs; } /** * Starts a new task, or - if already running - adds the argument to the running task. * @param suspect */ private void startBroadcastTask(Address suspect) { bcast_lock.lock(); try { if(bcast_future == null || bcast_future.isDone()) { task=new BroadcastTask(suspected_mbrs); task.addSuspectedMember(suspect); bcast_future=timer.scheduleWithFixedDelay(task, 0, // run immediately the first time timeout, // then every timeout milliseconds, until cancelled TimeUnit.MILLISECONDS); if(log.isTraceEnabled()) log.trace("BroadcastTask started"); } else { task.addSuspectedMember(suspect); } } finally { bcast_lock.unlock(); } } private void stopBroadcastTask() { bcast_lock.lock(); try { if(bcast_future != null) { bcast_future.cancel(true); bcast_future=null; task=null; } } finally { bcast_lock.unlock(); } } /** Adds a suspected member. Starts the task if not yet running */ protected void addSuspectedMember(Address mbr) { if(mbr == null) return; if(!members.contains(mbr)) return; synchronized(suspected_mbrs) { if(!suspected_mbrs.contains(mbr)) { suspected_mbrs.addElement(mbr); startBroadcastTask(mbr); } } } void removeSuspectedMember(Address suspected_mbr) { if(suspected_mbr == null) return; if(log.isDebugEnabled()) log.debug("member is " + suspected_mbr); synchronized(suspected_mbrs) { suspected_mbrs.removeElement(suspected_mbr); if(suspected_mbrs.isEmpty()) stopBroadcastTask(); } } /** Removes all elements from suspected_mbrs that are not in the new membership */ void adjustSuspectedMembers(List new_mbrship) { if(new_mbrship == null || new_mbrship.isEmpty()) return; synchronized(suspected_mbrs) { suspected_mbrs.retainAll(new_mbrship); if(suspected_mbrs.isEmpty()) stopBroadcastTask(); } } } protected final class BroadcastTask implements Runnable { private final Vector
    suspected_members=new Vector
    (); BroadcastTask(Vector
    suspected_members) { this.suspected_members.addAll(suspected_members); } public void stop() { suspected_members.clear(); if(log.isTraceEnabled()) log.trace("BroadcastTask stopped"); } public void run() { Message suspect_msg; FD.FdHeader hdr; synchronized(suspected_members) { if(suspected_members.isEmpty()) { stop(); return; } hdr=new FdHeader(FdHeader.SUSPECT); hdr.mbrs=new Vector
    (suspected_members); hdr.from=local_addr; } suspect_msg=new Message(); // mcast SUSPECT to all members suspect_msg.setFlag(Message.OOB); suspect_msg.putHeader(name, hdr); if(log.isDebugEnabled()) log.debug("broadcasting SUSPECT message [suspected_mbrs=" + suspected_members + "] to group"); down_prot.down(new Event(Event.MSG, suspect_msg)); } public void addSuspectedMember(Address suspect) { if(suspect != null && !suspected_members.contains(suspect)) { suspected_members.add(suspect); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TUNNEL.java0000644000175000017500000002465511366547366025315 0ustar twernertwerner// $Id: TUNNEL.java,v 1.45.2.5 2008/11/28 17:52:51 vlada Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.RouterStub; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.IOException; import java.net.SocketException; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Replacement for UDP. Instead of sending packets via UDP, a TCP connection is * opened to a Router (using the RouterStub client-side stub), the IP * address/port of which was given using channel properties * router_host and router_port. All outgoing * traffic is sent via this TCP socket to the Router which distributes it to all * connected TUNNELs in this group. Incoming traffic received from Router will * simply be passed up the stack. * *

    * A TUNNEL layer can be used to penetrate a firewall, most firewalls allow * creating TCP connections to the outside world, however, they do not permit * outside hosts to initiate a TCP connection to a host inside the firewall. * Therefore, the connection created by the inside host is reused by Router to * send traffic from an outside host to a host inside the firewall. * * @author Bela Ban */ public class TUNNEL extends TP { private String router_host = null; private int router_port = 0; private RouterStub stub; long reconnect_interval = 5000; /* * flag indicating if tunnel was destroyed intentionally (disconnect, channel destroy etc) */ private volatile boolean intentionallyTornDown = false; /** time to wait in ms between reconnect attempts */ @GuardedBy("reconnectorLock") private Future reconnectorFuture = null; private final Lock reconnectorLock = new ReentrantLock(); public TUNNEL(){} public String toString() { return "Protocol TUNNEL(local_addr=" + local_addr + ')'; } public String getRouterHost() { return router_host; } public void setRouterHost(String router_host) { this.router_host=router_host; } public int getRouterPort() { return router_port; } public void setRouterPort(int router_port) { this.router_port=router_port; } public long getReconnectInterval() { return reconnect_interval; } public void setReconnectInterval(long reconnect_interval) { this.reconnect_interval=reconnect_interval; } /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return "TUNNEL"; } public void init() throws Exception { super.init(); if(timer == null) throw new Exception("TUNNEL.init(): timer cannot be retrieved from protocol stack"); } public void start() throws Exception { // loopback turned on is mandatory loopback = true; intentionallyTornDown = false; stub = new RouterStub(router_host, router_port, bind_addr); stub.setConnectionListener(new StubConnectionListener()); local_addr = stub.getLocalAddress(); if(additional_data != null && local_addr instanceof IpAddress) ((IpAddress) local_addr).setAdditionalData(additional_data); super.start(); } public void stop() { teardownTunnel(); super.stop(); } /** Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str = props.getProperty("router_host"); if(str != null){ router_host = str; props.remove("router_host"); } str = props.getProperty("router_port"); if(str != null){ router_port = Integer.parseInt(str); props.remove("router_port"); } if(log.isDebugEnabled()){ log.debug("router_host=" + router_host + ";router_port=" + router_port); } if(router_host == null || router_port == 0){ if(log.isErrorEnabled()){ log.error("both router_host and router_port have to be set !"); return false; } } str = props.getProperty("reconnect_interval"); if(str != null){ reconnect_interval = Long.parseLong(str); props.remove("reconnect_interval"); } if(!props.isEmpty()){ StringBuilder sb = new StringBuilder(); for(Enumeration e = props.propertyNames();e.hasMoreElements();){ sb.append(e.nextElement().toString()); if(e.hasMoreElements()){ sb.append(", "); } } if(log.isErrorEnabled()) log.error("The following properties are not recognized: " + sb); return false; } return true; } /** Tears the TCP connection to the router down */ void teardownTunnel() { intentionallyTornDown = true; stopReconnecting(); stub.disconnect(); } public Object handleDownEvent(Event evt) { Object retEvent = super.handleDownEvent(evt); switch(evt.getType()){ case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: try{ stub.connect(channel_name); }catch(Exception e){ if(log.isErrorEnabled()) log.error("failed connecting to GossipRouter at " + router_host + ":" + router_port); startReconnecting(); } break; case Event.DISCONNECT: teardownTunnel(); break; } return retEvent; } private void startReconnecting() { reconnectorLock.lock(); try{ if(reconnectorFuture == null || reconnectorFuture.isDone()){ final Runnable reconnector = new Runnable() { public void run() { try{ if(!intentionallyTornDown){ if(log.isDebugEnabled()){ log.debug("Reconnecting " + getLocalAddress() + " to router at " + router_host + ":" + router_port); } stub.connect(channel_name); } }catch(Exception ex){ if(log.isTraceEnabled()) log.trace("failed reconnecting", ex); } } }; reconnectorFuture = timer.scheduleWithFixedDelay(reconnector, 0, reconnect_interval, TimeUnit.MILLISECONDS); } }finally{ reconnectorLock.unlock(); } } private void stopReconnecting() { reconnectorLock.lock(); try{ if(reconnectorFuture != null){ reconnectorFuture.cancel(true); reconnectorFuture = null; } }finally{ reconnectorLock.unlock(); } } private class StubConnectionListener implements RouterStub.ConnectionListener { private volatile int currentState = RouterStub.STATUS_DISCONNECTED; public void connectionStatusChange(int newState) { if(newState == RouterStub.STATUS_DISCONNECTED){ startReconnecting(); }else if(currentState != RouterStub.STATUS_CONNECTED && newState == RouterStub.STATUS_CONNECTED){ stopReconnecting(); Thread t = global_thread_factory.newThread(new TunnelReceiver(), "TUNNEL receiver"); t.setDaemon(true); t.start(); } currentState = newState; } } private class TunnelReceiver implements Runnable { public void run() { while(stub.isConnected()){ Address dest = null; int len; byte[] data = null; DataInputStream input = null; try{ input = stub.getInputStream(); dest = Util.readAddress(input); len = input.readInt(); if(len > 0){ data = new byte[len]; input.readFully(data, 0, len); receive(dest, null/*src will be read from data*/, data, 0, len); } }catch(SocketException se){ // if(log.isWarnEnabled()) log.warn("failure in TUNNEL // receiver thread", se); }catch(IOException ioe){ // if(log.isWarnEnabled()) log.warn("failure in TUNNEL // receiver thread", ioe); }catch(Exception e){ if(log.isWarnEnabled()) log.warn("failure in TUNNEL receiver thread", e); } } } } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { stub.sendToAllMembers(data, offset, length); } public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { stub.sendToSingleMember(dest, data, offset, length); } public String getInfo() { if(stub != null) return stub.toString(); else return "RouterStub not yet initialized"; } public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { msg.setDest(dest); } public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { msg.setDest(dest); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FRAG.java0000644000175000017500000005105411366547366025020 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.stack.Protocol; import org.jgroups.util.ExposedByteArrayOutputStream; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.*; /** * Fragmentation layer. Fragments messages larger than FRAG_SIZE into smaller packets. * Reassembles fragmented packets into bigger ones. The fragmentation number is prepended * to the messages as a header (and removed at the receiving side).

    * Each fragment is identified by (a) the sender (part of the message to which the header is appended), * (b) the fragmentation ID (which is unique per FRAG layer (monotonically increasing) and (c) the * fragement ID which ranges from 0 to number_of_fragments-1.

    * Requirement: lossless delivery (e.g. NAK, ACK). No requirement on ordering. Works for both unicast and * multicast messages. * @author Bela Ban * @author Filip Hanik * @version $Id: FRAG.java,v 1.39.2.2 2008/07/22 14:08:59 belaban Exp $ */ public class FRAG extends Protocol { private int frag_size=8192; // conservative value private int max_retained_buffer=70000; /*the fragmentation list contains a fragmentation table per sender *this way it becomes easier to clean up if a sender (member) leaves or crashes */ private final FragmentationList fragment_list=new FragmentationList(); private int curr_id=1; private final ExposedByteArrayOutputStream bos=new ExposedByteArrayOutputStream(1024); private final Vector

    members=new Vector
    (11); private final static String name="FRAG"; long num_sent_msgs=0; long num_sent_frags=0; long num_received_msgs=0; long num_received_frags=0; public String getName() { return name; } public int getFragSize() {return frag_size;} public void setFragSize(int s) {frag_size=s;} public long getNumberOfSentMessages() {return num_sent_msgs;} public long getNumberOfSentFragments() {return num_sent_frags;} public long getNumberOfReceivedMessages() {return num_received_msgs;} public long getNumberOfReceivedFragments() {return num_received_frags;} /** * Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("frag_size"); if(str != null) { frag_size=Integer.parseInt(str); props.remove("frag_size"); } str=props.getProperty("max_retained_buffer"); if(str != null) { max_retained_buffer=Integer.parseInt(str); props.remove("max_retained_buffer"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { super.init(); Map info=new HashMap(1); info.put("frag_size", frag_size); up_prot.up(new Event(Event.INFO, info)); down_prot.down(new Event(Event.INFO, info)); } public void resetStats() { super.resetStats(); num_sent_msgs=num_sent_frags=num_received_msgs=num_received_frags=0; } /** * Fragment a packet if larger than frag_size (add a header). Otherwise just pass down. Only * add a header if framentation is needed ! */ public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); long size=msg.size(); num_sent_msgs++; if(size > frag_size) { if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("message size is "); sb.append(size).append(", will fragment (frag_size=").append(frag_size).append(')'); log.trace(sb.toString()); } fragment(msg); // Fragment and pass down return null; } break; case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; case Event.CONFIG: Object ret=down_prot.down(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); return ret; } return down_prot.down(evt); // Pass on to the layer below us } /** * If event is a message, if it is fragmented, re-assemble fragments into big message and pass up the stack. */ public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); FragHeader hdr=(FragHeader)msg.getHeader(name); if(hdr != null) { // needs to be defragmented unfragment(msg, hdr); // Unfragment and possibly pass up return null; } else { num_received_msgs++; } break; case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; case Event.CONFIG: Object ret=up_prot.up(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); return ret; } return up_prot.up(evt); // Pass up to the layer above us by default } private void handleViewChange(View view) { Vector new_mbrs=view.getMembers(), left_mbrs; Address mbr; left_mbrs=Util.determineLeftMembers(members, new_mbrs); members.clear(); members.addAll(new_mbrs); for(int i=0; i < left_mbrs.size(); i++) { mbr=(Address)left_mbrs.elementAt(i); //the new view doesn't contain the sender, he must have left, //hence we will clear all his fragmentation tables fragment_list.remove(mbr); if(log.isTraceEnabled()) log.trace("[VIEW_CHANGE] removed " + mbr + " from fragmentation table"); } } /** * Send all fragments as separate messages (with same ID !). * Example: *
         * Given the generated ID is 2344, number of fragments=3, message {dst,src,buf}
         * would be fragmented into:
         * 

    * [2344,3,0]{dst,src,buf1}, * [2344,3,1]{dst,src,buf2} and * [2344,3,2]{dst,src,buf3} *

    */ private void fragment(Message msg) { DataOutputStream out=null; byte[] buffer; byte[] fragments[]; Event evt; FragHeader hdr; Message frag_msg; Address dest=msg.getDest(), src=msg.getSrc(); long id=curr_id++; // used as seqnos int num_frags; try { // Write message into a byte buffer and fragment it // Synchronization around bos is needed for concurrent access (http://jira.jboss.com/jira/browse/JGRP-215) synchronized(bos) { bos.reset(this.max_retained_buffer); out=new DataOutputStream(bos); msg.writeTo(out); out.flush(); buffer=bos.getRawBuffer(); fragments=Util.fragmentBuffer(buffer, frag_size, bos.size()); } num_frags=fragments.length; num_sent_frags+=num_frags; if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("fragmenting packet to ").append(dest != null ? dest.toString() : ""); sb.append(" (size=").append(buffer.length).append(") into ").append(num_frags); sb.append(" fragment(s) [frag_size=").append(frag_size).append(']'); log.trace(sb.toString()); } for(int i=0; i < num_frags; i++) { frag_msg=new Message(dest, src, fragments[i]); hdr=new FragHeader(id, i, num_frags); frag_msg.putHeader(name, hdr); evt=new Event(Event.MSG, frag_msg); down_prot.down(evt); } } catch(Exception e) { log.error("exception occurred trying to fragment message", e); } finally { Util.close(out); } } /** * 1. Get all the fragment buffers * 2. When all are received -> Assemble them into one big buffer * 3. Read headers and byte buffer from big buffer * 4. Set headers and buffer in msg * 5. Pass msg up the stack */ private void unfragment(Message msg, FragHeader hdr) { FragmentationTable frag_table; Address sender=msg.getSrc(); Message assembled_msg; byte[] m; ByteArrayInputStream bis; DataInputStream in=null; frag_table=fragment_list.get(sender); if(frag_table == null) { frag_table=new FragmentationTable(sender); try { fragment_list.add(sender, frag_table); } catch(IllegalArgumentException x) { // the entry has already been added, probably in parallel from another thread frag_table=fragment_list.get(sender); } } num_received_frags++; m=frag_table.add(hdr.id, hdr.frag_id, hdr.num_frags, msg.getBuffer()); if(m != null) { try { bis=new ByteArrayInputStream(m); in=new DataInputStream(bis); assembled_msg=new Message(false); assembled_msg.readFrom(in); if(log.isTraceEnabled()) log.trace("assembled_msg is " + assembled_msg); assembled_msg.setSrc(sender); // needed ? YES, because fragments have a null src !! num_received_msgs++; up_prot.up(new Event(Event.MSG, assembled_msg)); } catch(Exception e) { log.error("failed unfragmenting a message", e); } finally { Util.close(in); } } } void handleConfigEvent(Map map) { if(map == null) return; if(map.containsKey("frag_size")) { frag_size=((Integer)map.get("frag_size")).intValue(); if(log.isDebugEnabled()) log.debug("setting frag_size=" + frag_size); } } /** * A fragmentation list keeps a list of fragmentation tables * sorted by an Address ( the sender ). * This way, if the sender disappears or leaves the group half way * sending the content, we can simply remove this members fragmentation * table and clean up the memory of the receiver. * We do not have to do the same for the sender, since the sender doesn't keep a fragmentation table */ static class FragmentationList { /* initialize the hashtable to hold all the fragmentation tables * 11 is the best growth capacity to start with
    * HashMap */ private final HashMap frag_tables=new HashMap(11); /** * Adds a fragmentation table for this particular sender * If this sender already has a fragmentation table, an IllegalArgumentException * will be thrown. * @param sender - the address of the sender, cannot be null * @param table - the fragmentation table of this sender, cannot be null * @throws IllegalArgumentException if an entry for this sender already exist */ public void add(Address sender, FragmentationTable table) throws IllegalArgumentException { FragmentationTable healthCheck; synchronized(frag_tables) { healthCheck=(FragmentationTable)frag_tables.get(sender); if(healthCheck == null) { frag_tables.put(sender, table); } else { throw new IllegalArgumentException("Sender <" + sender + "> already exists in the fragementation list"); } } } /** * returns a fragmentation table for this sender * returns null if the sender doesn't have a fragmentation table * @return the fragmentation table for this sender, or null if no table exist */ public FragmentationTable get(Address sender) { synchronized(frag_tables) { return (FragmentationTable)frag_tables.get(sender); } } /** * returns true if this sender already holds a * fragmentation for this sender, false otherwise * @param sender - the sender, cannot be null * @return true if this sender already has a fragmentation table */ public boolean containsSender(Address sender) { synchronized(frag_tables) { return frag_tables.containsKey(sender); } } /** * removes the fragmentation table from the list. * after this operation, the fragementation list will no longer * hold a reference to this sender's fragmentation table * @param sender - the sender who's fragmentation table you wish to remove, cannot be null * @return true if the table was removed, false if the sender doesn't have an entry */ public boolean remove(Address sender) { synchronized(frag_tables) { boolean result=containsSender(sender); frag_tables.remove(sender); return result; } } /** * returns a list of all the senders that have fragmentation tables opened. * @return an array of all the senders in the fragmentation list */ public Address[] getSenders() { Address[] result; int index=0; synchronized(frag_tables) { result=new Address[frag_tables.size()]; for(Iterator it=frag_tables.keySet().iterator(); it.hasNext();) { result[index++]=(Address)it.next(); } } return result; } public String toString() { Map.Entry entry; StringBuilder buf=new StringBuilder("Fragmentation list contains "); synchronized(frag_tables) { buf.append(frag_tables.size()).append(" tables\n"); for(Iterator it=frag_tables.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); buf.append(entry.getKey()).append(": " ).append(entry.getValue()).append("\n"); } } return buf.toString(); } } /** * Keeps track of the fragments that are received. * Reassembles fragements into entire messages when all fragments have been received. * The fragmentation holds a an array of byte arrays for a unique sender * The first dimension of the array is the order of the fragmentation, in case the arrive out of order */ static class FragmentationTable { private final Address sender; /* the hashtable that holds the fragmentation entries for this sender*/ private final Hashtable h=new Hashtable(11); // keys: frag_ids, vals: Entrys FragmentationTable(Address sender) { this.sender=sender; } /** * inner class represents an entry for a message * each entry holds an array of byte arrays sorted * once all the byte buffer entries have been filled * the fragmentation is considered complete. */ static class Entry { //the total number of fragment in this message int tot_frags=0; // each fragment is a byte buffer byte[] fragments[]=null; //the number of fragments we have received int number_of_frags_recvd=0; // the message ID long msg_id=-1; /** * Creates a new entry * * @param tot_frags the number of fragments to expect for this message */ Entry(long msg_id, int tot_frags) { this.msg_id=msg_id; this.tot_frags=tot_frags; fragments=new byte[tot_frags][]; for(int i=0; i < tot_frags; i++) { fragments[i]=null; } } /** * adds on fragmentation buffer to the message * * @param frag_id the number of the fragment being added 0..(tot_num_of_frags - 1) * @param frag the byte buffer containing the data for this fragmentation, should not be null */ public void set(int frag_id, byte[] frag) { fragments[frag_id]=frag; number_of_frags_recvd++; } /** * returns true if this fragmentation is complete * ie, all fragmentations have been received for this buffer */ public boolean isComplete() { /*first make the simple check*/ if(number_of_frags_recvd < tot_frags) { return false; } /*then double check just in case*/ for(int i=0; i < fragments.length; i++) { if(fragments[i] == null) return false; } /*all fragmentations have been received*/ return true; } /** * Assembles all the fragmentations into one buffer * this method does not check if the fragmentation is complete * * @return the complete message in one buffer */ public byte[] assembleBuffer() { return Util.defragmentBuffer(fragments); } /** * debug only */ public String toString() { StringBuilder ret=new StringBuilder(); ret.append("[tot_frags=").append(tot_frags).append(", number_of_frags_recvd=").append(number_of_frags_recvd).append(']'); return ret.toString(); } public int hashCode() { return super.hashCode(); } } /** * Creates a new entry if not yet present. Adds the fragment. * If all fragements for a given message have been received, * an entire message is reassembled and returned. * Otherwise null is returned. * * @param id - the message ID, unique for a sender * @param frag_id the index of this fragmentation (0..tot_frags-1) * @param tot_frags the total number of fragmentations expected * @param fragment - the byte buffer for this fragment */ public synchronized byte[] add(long id, int frag_id, int tot_frags, byte[] fragment) { /*initialize the return value to default not complete */ byte[] retval=null; Entry e=(Entry)h.get(new Long(id)); if(e == null) { // Create new entry if not yet present e=new Entry(id, tot_frags); h.put(new Long(id), e); } e.set(frag_id, fragment); if(e.isComplete()) { retval=e.assembleBuffer(); h.remove(new Long(id)); } return retval; } public void reset() { } public String toString() { StringBuilder buf=new StringBuilder("Fragmentation Table Sender:").append(sender).append("\n\t"); java.util.Enumeration e=this.h.elements(); while(e.hasMoreElements()) { Entry entry=(Entry)e.nextElement(); int count=0; for(int i=0; i < entry.fragments.length; i++) { if(entry.fragments[i] != null) { count++; } } buf.append("Message ID:").append(entry.msg_id).append("\n\t"); buf.append("Total Frags:").append(entry.tot_frags).append("\n\t"); buf.append("Frags Received:").append(count).append("\n\n"); } return buf.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/COMPRESS.java0000644000175000017500000002035111366547366025530 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import java.io.*; import java.util.Properties; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ArrayBlockingQueue; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; /** * Compresses the payload of a message. Goal is to reduce the number of messages sent across the wire. * Should ideally be layered somewhere above a fragmentation protocol (e.g. FRAG). * @author Bela Ban * @version $Id: COMPRESS.java,v 1.19.4.2 2009/07/01 17:35:04 dereed Exp $ */ public class COMPRESS extends Protocol { BlockingQueue deflater_pool=null; BlockingQueue inflater_pool=null; /** Values are from 0-9 (0=no compression, 9=best compression) */ int compression_level=Deflater.BEST_COMPRESSION; // this is 9 /** Minimal payload size of a message (in bytes) for compression to kick in */ long min_size=500; /** Number of inflaters/deflaters, for concurrency, increase this to the max number of concurrent requests possible */ int pool_size=2; final static String name="COMPRESS"; public String getName() { return name; } public void init() throws Exception { deflater_pool=new ArrayBlockingQueue(pool_size); for(int i=0; i < pool_size; i++) { deflater_pool.add(new Deflater(compression_level)); } inflater_pool=new ArrayBlockingQueue(pool_size); for(int i=0; i < pool_size; i++) { inflater_pool.add(new Inflater()); } } public void destroy() { for(Deflater deflater: deflater_pool) deflater.end(); for(Inflater inflater: inflater_pool) inflater.end(); } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("compression_level"); if(str != null) { compression_level=Integer.parseInt(str); props.remove("compression_level"); } str=props.getProperty("min_size"); if(str != null) { min_size=Long.parseLong(str); props.remove("min_size"); } str=props.getProperty("pool_size"); if(str != null) { pool_size=Integer.parseInt(str); if(pool_size <= 0) { log.warn("pool_size must be > 0, setting it to 1"); pool_size=1; } props.remove("pool_size"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } /** * We compress the payload if it is larger than min_size. In this case we add a header containing * the original size before compression. Otherwise we add no header.
    * Note that we compress either the entire buffer (if offset/length are not used), or a subset (if offset/length * are used) * @param evt */ public Object down(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); int length=msg.getLength(); // takes offset/length (if set) into account if(length >= min_size) { byte[] payload=msg.getRawBuffer(); // here we get the ref so we can avoid copying byte[] compressed_payload=new byte[length]; int compressed_size; Deflater deflater=null; try { deflater=deflater_pool.take(); deflater.reset(); deflater.setInput(payload, msg.getOffset(), length); deflater.finish(); deflater.deflate(compressed_payload); compressed_size=deflater.getTotalOut(); if ( compressed_size < length ) { // JGRP-1000 byte[] new_payload=new byte[compressed_size]; System.arraycopy(compressed_payload, 0, new_payload, 0, compressed_size); msg.setBuffer(new_payload); msg.putHeader(name, new CompressHeader(length)); if(log.isTraceEnabled()) log.trace("compressed payload from " + length + " bytes to " + compressed_size + " bytes"); } else { if(log.isTraceEnabled()) log.trace("Skipping compression since the compressed message is larger than the original"); } } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again throw new RuntimeException(e); } finally { if(deflater != null) deflater_pool.offer(deflater); } } } return down_prot.down(evt); } /** * If there is no header, we pass the message up. Otherwise we uncompress the payload to its original size. * @param evt */ public Object up(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); CompressHeader hdr=(CompressHeader)msg.getHeader(name); if(hdr != null) { byte[] compressed_payload=msg.getRawBuffer(); if(compressed_payload != null && compressed_payload.length > 0) { int original_size=hdr.original_size; byte[] uncompressed_payload=new byte[original_size]; Inflater inflater=null; try { inflater=inflater_pool.take(); inflater.reset(); inflater.setInput(compressed_payload, msg.getOffset(), msg.getLength()); try { inflater.inflate(uncompressed_payload); if(log.isTraceEnabled()) log.trace("uncompressed " + compressed_payload.length + " bytes to " + original_size + " bytes"); // we need to copy: https://jira.jboss.org/jira/browse/JGRP-867 Message copy=msg.copy(false); copy.setBuffer(uncompressed_payload); return up_prot.up(new Event(Event.MSG, copy)); // msg.setBuffer(uncompressed_payload); } catch(DataFormatException e) { if(log.isErrorEnabled()) log.error("exception on uncompression", e); } } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set the interrupt bit again, so caller can handle it } finally { if(inflater != null) inflater_pool.offer(inflater); } } } } return up_prot.up(evt); } public static class CompressHeader extends Header implements Streamable { int original_size=0; public CompressHeader() { super(); } public CompressHeader(int s) { original_size=s; } public int size() { return Global.INT_SIZE; } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(original_size); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { original_size=in.readInt(); } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(original_size); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { original_size=in.readInt(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/SIZE.java0000644000175000017500000001122711366547366025051 0ustar twernertwerner// $Id: SIZE.java,v 1.21 2007/04/27 07:59:19 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.util.Util; import org.jgroups.stack.Protocol; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.util.Properties; import java.util.Vector; /** * Protocol which prints out the real size of a message. To do this, the message * is serialized into a byte buffer and its size read. Don't use this layer in * a production stack since the costs are high (just for debugging). * * @author Bela Ban June 13 2001 */ public class SIZE extends Protocol { final Vector members=new Vector(); boolean print_msg=false; boolean raw_buffer=false; // just print size of message buffer /** Min size in bytes above which msgs should be printed */ long min_size=0; final ByteArrayOutputStream out_stream=new ByteArrayOutputStream(65535); /** * All protocol names have to be unique ! */ public String getName() { return "SIZE"; } public void init() { } /** * Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) {super.setProperties(props); String str; str=props.getProperty("print_msg"); if(str != null) { print_msg=Boolean.valueOf(str).booleanValue(); props.remove("print_msg"); } str=props.getProperty("raw_buffer"); if(str != null) { raw_buffer=Boolean.valueOf(str).booleanValue(); props.remove("raw_buffer"); } str=props.getProperty("min_size"); if(str != null) { min_size=Integer.parseInt(str); props.remove("min_size"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public Object up(Event evt) { Message msg; int payload_size=0, serialized_size; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); payload_size=msg.getLength(); if(raw_buffer) { if(log.isTraceEnabled()) log.trace("size of message buffer is " + payload_size + ", " + numHeaders(msg) + " headers"); } else { serialized_size=sizeOf(msg); if(serialized_size > min_size) { if(log.isTraceEnabled()) log.trace("size of serialized message is " + serialized_size + ", " + numHeaders(msg) + " headers"); } } if(print_msg) { if(log.isTraceEnabled()) log.trace("headers are " + msg.printHeaders() + ", payload size=" + payload_size); } break; } return up_prot.up(evt); // pass up to the layer above us } public Object down(Event evt) { Message msg; int payload_size=0, serialized_size; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); payload_size=msg.getLength(); if(raw_buffer) { if(log.isTraceEnabled()) log.trace("size of message buffer is " + payload_size + ", " + numHeaders(msg) + " headers"); } else { serialized_size=sizeOf(msg); if(serialized_size > min_size) { if(log.isTraceEnabled()) log.trace("size of serialized message is " + serialized_size + ", " + numHeaders(msg) + " headers"); } } if(print_msg) { if(log.isTraceEnabled()) log.trace("headers are " + msg.printHeaders() + ", payload size=" + payload_size); } break; } return down_prot.down(evt); // Pass on to the layer below us } int sizeOf(Message msg) { DataOutputStream out=null; synchronized(out_stream) { try { out_stream.reset(); out=new DataOutputStream(out_stream); msg.writeTo(out); out.flush(); return out_stream.size(); } catch(Exception e) { return 0; } finally { Util.close(out); } } } int numHeaders(Message msg) { if(msg == null) return 0; return msg.getNumHeaders(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TP.java0000644000175000017500000025453411366547366024634 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.*; import org.jgroups.util.Queue; import org.jgroups.util.ThreadFactory; import java.io.DataInputStream; import java.io.IOException; import java.io.DataOutputStream; import java.io.InterruptedIOException; import java.net.*; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; /** * Generic transport - specific implementations should extend this abstract class. * Features which are provided to the subclasses include *
      *
    • version checking *
    • marshalling and unmarshalling *
    • message bundling (handling single messages, and message lists) *
    • incoming packet handler *
    • loopback *
    * A subclass has to override *
      *
    • {@link #sendToAllMembers(byte[], int, int)} *
    • {@link #sendToSingleMember(org.jgroups.Address, byte[], int, int)} *
    • {@link #init()} *
    • {@link #start()}: subclasses must call super.start() after they initialize themselves * (e.g., created their sockets). *
    • {@link #stop()}: subclasses must call super.stop() after they deinitialized themselves *
    • {@link #destroy()} *
    * The create() or start() method has to create a local address.
    of interfaces to receive multicasts on. The multicast receive socket will listen * on all of these interfaces. This is a comma-separated list of IP addresses or interface names. E.g. * "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded; we only bind to an interface once. * If this property is set, it override receive_on_all_interfaces. */ List receive_interfaces=null; /** If true, the transport should use all available interfaces to send multicast messages. This means * the same multicast message is sent N times, so use with care */ boolean send_on_all_interfaces=false; /** List of interfaces to send multicasts on. The multicast send socket will send the * same multicast message on all of these interfaces. This is a comma-separated list of IP addresses or * interface names. E.g. "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded. * If this property is set, it override send_on_all_interfaces. */ List send_interfaces=null; /** The port to which the transport binds. 0 means to bind to any (ephemeral) port */ int bind_port=0; int port_range=1; // 27-6-2003 bgooren, Only try one port by default /** The members of this group (updated when a member joins or leaves) */ final protected HashSet
    members=new HashSet
    (11); protected View view=null; final ExposedByteArrayInputStream in_stream=new ExposedByteArrayInputStream(new byte[]{'0'}); final DataInputStream dis=new DataInputStream(in_stream); /** If true, messages sent to self are treated specially: unicast messages are * looped back immediately, multicast messages get a local copy first and - * when the real copy arrives - it will be discarded. Useful for Window * media (non)sense */ boolean loopback=true; /** Discard packets with a different version. Usually minor version differences are okay. Setting this property * to true means that we expect the exact same version on all incoming packets */ protected boolean discard_incompatible_packets=false; /** Sometimes receivers are overloaded (they have to handle de-serialization etc). * Packet handler is a separate thread taking care of de-serialization, receiver * thread(s) simply put packet in queue and return immediately. Setting this to * true adds one more thread */ boolean use_incoming_packet_handler=true; /** Used by packet handler to store incoming DatagramPackets */ Queue incoming_packet_queue=null; /** Dequeues DatagramPackets from packet_queue, unmarshalls them and * calls handleIncomingUdpPacket() */ IncomingPacketHandler incoming_packet_handler=null; /** Used by packet handler to store incoming Messages */ Queue incoming_msg_queue=null; IncomingMessageHandler incoming_msg_handler; boolean use_concurrent_stack=true; ThreadGroup pool_thread_group=new ThreadGroup(Util.getGlobalThreadGroup(), "Thread Pools"); /** * Names the current thread. Valid values are "pcl": * p: include the previous (original) name, e.g. "Incoming thread-1", "UDP ucast receiver" * c: include the cluster name, e.g. "MyCluster" * l: include the local address of the current member, e.g. "192.168.5.1:5678" */ protected String thread_naming_pattern="cl"; public String getThreadNamingPattern() {return thread_naming_pattern;} /** Keeps track of connects and disconnects, in order to start and stop threads */ int connect_count=0; /** Number of times init() was called. Incremented on init(), decremented on destroy() */ int init_count=0; private final ReentrantLock connectLock = new ReentrantLock(); /** ================================== OOB thread pool ============================== */ /** The thread pool which handles OOB messages */ Executor oob_thread_pool; /** Factory which is used by oob_thread_pool */ ThreadFactory oob_thread_factory=null; boolean oob_thread_pool_enabled=true; int oob_thread_pool_min_threads=2; int oob_thread_pool_max_threads=10; /** Number of milliseconds after which an idle thread is removed */ long oob_thread_pool_keep_alive_time=30000; long num_oob_msgs_received=0; /** Used if oob_thread_pool is a ThreadPoolExecutor and oob_thread_pool_queue_enabled is true */ BlockingQueue oob_thread_pool_queue=null; /** Whether of not to use a queue with ThreadPoolExecutor (ignored with direct executor) */ boolean oob_thread_pool_queue_enabled=true; /** max number of elements in queue (bounded) */ int oob_thread_pool_queue_max_size=500; /** Possible values are "Abort", "Discard", "DiscardOldest" and "Run". These values might change once we switch to * JDK 5's java.util.concurrent package */ String oob_thread_pool_rejection_policy="Run"; public Executor getOOBThreadPool() { return oob_thread_pool; } public void setOOBThreadPool(Executor oob_thread_pool) { if(this.oob_thread_pool != null) { shutdownThreadPool(this.oob_thread_pool); } this.oob_thread_pool=oob_thread_pool; } public ThreadFactory getOOBThreadPoolThreadFactory() { return oob_thread_factory; } public void setOOBThreadPoolThreadFactory(ThreadFactory factory) { oob_thread_factory=factory; if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setThreadFactory(factory); } /** ================================== Regular thread pool ============================== */ /** The thread pool which handles unmarshalling, version checks and dispatching of regular messages */ Executor thread_pool; /** Factory which is used by oob_thread_pool */ ThreadFactory default_thread_factory=null; boolean thread_pool_enabled=true; int thread_pool_min_threads=2; int thread_pool_max_threads=10; /** Number of milliseconds after which an idle thread is removed */ long thread_pool_keep_alive_time=30000; long num_incoming_msgs_received=0; /** Used if thread_pool is a ThreadPoolExecutor and thread_pool_queue_enabled is true */ BlockingQueue thread_pool_queue=null; /** Whether of not to use a queue with ThreadPoolExecutor (ignored with directE decutor) */ boolean thread_pool_queue_enabled=true; /** max number of elements in queue (bounded) */ int thread_pool_queue_max_size=500; /** Possible values are "Abort", "Discard", "DiscardOldest" and "Run". These values might change once we switch to * JDK 5's java.util.concurrent package */ String thread_pool_rejection_policy="Run"; public Executor getDefaultThreadPool() { return thread_pool; } public void setDefaultThreadPool(Executor thread_pool) { if(this.thread_pool != null) shutdownThreadPool(this.thread_pool); this.thread_pool=thread_pool; } public ThreadFactory getDefaultThreadPoolThreadFactory() { return default_thread_factory; } public void setDefaultThreadPoolThreadFactory(ThreadFactory factory) { default_thread_factory=factory; if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setThreadFactory(factory); } /** ================================== Timer thread pool ================================= */ protected TimeScheduler timer=null; protected ThreadFactory timer_thread_factory; /** Max number of threads to be used by the timer thread pool */ int num_timer_threads=4; public ThreadFactory getTimerThreadFactory() { return timer_thread_factory; } public void setTimerThreadFactory(ThreadFactory factory) { timer_thread_factory=factory; timer.setThreadFactory(factory); } public TimeScheduler getTimer() {return timer;} /** =================================Default thread factory ================================== */ /** Used by all threads created by JGroups outside of the thread pools */ protected ThreadFactory global_thread_factory=null; public ThreadFactory getThreadFactory() { return global_thread_factory; } public void setThreadFactory(ThreadFactory factory) { global_thread_factory=factory; } /** ============================= End of default thread factory ============================== */ /** If set it will be added to local_addr. Used to implement * for example transport independent addresses */ byte[] additional_data=null; /** Maximum number of bytes for messages to be queued until they are sent. This value needs to be smaller than the largest datagram packet size in case of UDP */ int max_bundle_size=64000; /** Max number of milliseconds until queued messages are sent. Messages are sent when max_bundle_size or * max_bundle_timeout has been exceeded (whichever occurs faster) */ long max_bundle_timeout=20; /** Enable bundling of smaller messages into bigger ones */ boolean enable_bundling=false; /** Enable bundling for unicast messages. Ignored if enable_bundling is off */ boolean enable_unicast_bundling=true; private Bundler bundler=null; private DiagnosticsHandler diag_handler=null; private final List preregistered_probe_handlers=new LinkedList(); boolean enable_diagnostics=true; String diagnostics_addr="224.0.75.75"; int diagnostics_port=7500; /** If this transport is shared, identifies all the transport instances which are to be shared */ String singleton_name=null; /** If singleton_name is enabled, this map is used to de-multiplex incoming messages according to their * cluster names (attached to the message by the transport anyway). The values are the next protocols above * the transports. */ private final ConcurrentMap up_prots=new ConcurrentHashMap(); TpHeader header; final String name=getName(); protected PortsManager pm=null; protected String persistent_ports_file=null; protected long pm_expiry_time=30000L; protected boolean persistent_ports=false; static final byte LIST = 1; // we have a list of messages rather than a single message when set static final byte MULTICAST = 2; // message is a multicast (versus a unicast) message when set static final byte OOB = 4; // message has OOB flag set (Message.OOB) long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; static NumberFormat f; private static final int INITIAL_BUFSIZE=4095; static { f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } /** * Creates the TP protocol, and initializes the * state variables, does however not start any sockets or threads. */ protected TP() { } /** * debug only */ public String toString() { return name + "(local address: " + local_addr + ')'; } public void resetStats() { num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=0; num_oob_msgs_received=num_incoming_msgs_received=0; } public void registerProbeHandler(ProbeHandler handler) { if(diag_handler != null) diag_handler.registerProbeHandler(handler); else preregistered_probe_handlers.add(handler); } public void unregisterProbeHandler(ProbeHandler handler) { if(diag_handler != null) diag_handler.unregisterProbeHandler(handler); } public long getNumMessagesSent() {return num_msgs_sent;} public long getNumMessagesReceived() {return num_msgs_received;} public long getNumBytesSent() {return num_bytes_sent;} public long getNumBytesReceived() {return num_bytes_received;} public String getBindAddress() {return bind_addr != null? bind_addr.toString() : "null";} public void setBindAddress(String bind_addr) throws UnknownHostException { this.bind_addr=InetAddress.getByName(bind_addr); } public int getBindPort() {return bind_port;} public void setBindPort(int port) {this.bind_port=port;} /** @deprecated Use {@link #isReceiveOnAllInterfaces()} instead */ public boolean getBindToAllInterfaces() {return receive_on_all_interfaces;} public void setBindToAllInterfaces(boolean flag) {this.receive_on_all_interfaces=flag;} public boolean isReceiveOnAllInterfaces() {return receive_on_all_interfaces;} public java.util.List getReceiveInterfaces() {return receive_interfaces;} public boolean isSendOnAllInterfaces() {return send_on_all_interfaces;} public java.util.List getSendInterfaces() {return send_interfaces;} public boolean isDiscardIncompatiblePackets() {return discard_incompatible_packets;} public void setDiscardIncompatiblePackets(boolean flag) {discard_incompatible_packets=flag;} public boolean isEnableBundling() {return enable_bundling;} public void setEnableBundling(boolean flag) {enable_bundling=flag;} public boolean isEnable_unicast_bundling() { return enable_unicast_bundling; } public void setEnable_unicast_bundling(boolean enable_unicast_bundling) { this.enable_unicast_bundling=enable_unicast_bundling; } public int getMaxBundleSize() {return max_bundle_size;} public void setMaxBundleSize(int size) {max_bundle_size=size;} public long getMaxBundleTimeout() {return max_bundle_timeout;} public void setMaxBundleTimeout(long timeout) {max_bundle_timeout=timeout;} public Address getLocalAddress() {return local_addr;} public String getChannelName() {return channel_name;} public boolean isLoopback() {return loopback;} public void setLoopback(boolean b) {loopback=b;} public boolean isUseIncomingPacketHandler() {return use_incoming_packet_handler;} public boolean isDefaulThreadPoolEnabled() { return thread_pool_enabled; } public boolean isOOBThreadPoolEnabled() { return oob_thread_pool_enabled; } public ConcurrentMap getUpProtocols() { return up_prots; } public int getOOBMinPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getCorePoolSize() : 0; } public void setOOBMinPoolSize(int size) { if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setCorePoolSize(size); } public int getOOBMaxPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getMaximumPoolSize() : 0; } public void setOOBMaxPoolSize(int size) { if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setMaximumPoolSize(size); } public int getOOBPoolSize() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getPoolSize() : 0; } public long getOOBKeepAliveTime() { return oob_thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)oob_thread_pool).getKeepAliveTime(TimeUnit.MILLISECONDS) : 0; } public void setOOBKeepAliveTime(long time) { if(oob_thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)oob_thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getOOBMessages() { return num_oob_msgs_received; } public int getOOBQueueSize() { return oob_thread_pool_queue.size(); } public int getOOBMaxQueueSize() { return oob_thread_pool_queue_max_size; } public int getIncomingMinPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getCorePoolSize() : 0; } public void setIncomingMinPoolSize(int size) { if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setCorePoolSize(size); } public int getIncomingMaxPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getMaximumPoolSize() : 0; } public void setIncomingMaxPoolSize(int size) { if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setMaximumPoolSize(size); } public int getIncomingPoolSize() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getPoolSize() : 0; } public long getIncomingKeepAliveTime() { return thread_pool instanceof ThreadPoolExecutor? ((ThreadPoolExecutor)thread_pool).getKeepAliveTime(TimeUnit.MILLISECONDS) : 0; } public void setIncomingKeepAliveTime(long time) { if(thread_pool instanceof ThreadPoolExecutor) ((ThreadPoolExecutor)thread_pool).setKeepAliveTime(time, TimeUnit.MILLISECONDS); } public long getIncomingMessages() { return num_incoming_msgs_received; } public int getIncomingQueueSize() { return thread_pool_queue.size(); } public int getIncomingMaxQueueSize() { return thread_pool_queue_max_size; } public Map dumpStats() { Map retval=super.dumpStats(); if(retval == null) retval=new HashMap(); retval.put("num_msgs_sent", new Long(num_msgs_sent)); retval.put("num_msgs_received", new Long(num_msgs_received)); retval.put("num_bytes_sent", new Long(num_bytes_sent)); retval.put("num_bytes_received", new Long(num_bytes_received)); return retval; } /** * Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N * messages, one for each member * @param data The data to be sent. This is not a copy, so don't modify it * @param offset * @param length * @throws Exception */ public abstract void sendToAllMembers(byte[] data, int offset, int length) throws Exception; /** * Send to all members in the group. UDP would use an IP multicast message, whereas TCP would send N * messages, one for each member * @param dest Must be a non-null unicast address * @param data The data to be sent. This is not a copy, so don't modify it * @param offset * @param length * @throws Exception */ public abstract void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception; public abstract String getInfo(); public abstract void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast); public abstract void postUnmarshallingList(Message msg, Address dest, boolean multicast); private StringBuilder _getInfo(Channel ch) { StringBuilder sb=new StringBuilder(); sb.append(ch.getLocalAddress()).append(" (").append(ch.getClusterName()).append(") ").append("\n"); sb.append("local_addr=").append(ch.getLocalAddress()).append("\n"); sb.append("group_name=").append(ch.getClusterName()).append("\n"); sb.append("version=").append(Version.description).append(", cvs=\"").append(Version.cvs).append("\"\n"); sb.append("view: ").append(ch.getView()).append('\n'); sb.append(getInfo()); return sb; } private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request, ProtocolStack stack) { try { StringTokenizer tok=new StringTokenizer(request); String req=tok.nextToken(); StringBuilder info=new StringBuilder("n/a"); if(req.trim().toLowerCase().startsWith("query")) { ArrayList l=new ArrayList(tok.countTokens()); while(tok.hasMoreTokens()) l.add(tok.nextToken().trim().toLowerCase()); info=_getInfo(stack.getChannel()); if(l.contains("jmx")) { Channel ch=stack.getChannel(); if(ch != null) { Map m=ch.dumpStats(); StringBuilder sb=new StringBuilder(); sb.append("stats:\n"); for(Iterator> it=m.entrySet().iterator(); it.hasNext();) { sb.append(it.next()).append("\n"); } info.append(sb); } } if(l.contains("props")) { String p=stack.printProtocolSpecAsXML(); info.append("\nprops:\n").append(p); } if(l.contains("info")) { Map tmp=stack.getChannel().getInfo(); info.append("INFO:\n"); for(Map.Entry entry: tmp.entrySet()) { info.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } } } byte[] diag_rsp=info.toString().getBytes(); if(log.isDebugEnabled()) log.debug("sending diag response to " + sender); sendResponse(sock, sender, diag_rsp); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender, t); } } private static void sendResponse(DatagramSocket sock, SocketAddress sender, byte[] buf) throws IOException { DatagramPacket p=new DatagramPacket(buf, 0, buf.length, sender); sock.send(p); } /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ public void init() throws Exception { if(init_count++ >= 1) { return; } super.init(); // Create the default thread factory global_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); // Create the timer and the associated thread factory - depends on singleton_name // timer_thread_factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); timer_thread_factory=new LazyThreadFactory(Util.getGlobalThreadGroup(), "Timer", true, true); if(singleton_name != null && singleton_name.trim().length() > 0) { timer_thread_factory.setIncludeClusterName(false); } default_thread_factory=new DefaultThreadFactory(pool_thread_group, "Incoming", false, true); oob_thread_factory=new DefaultThreadFactory(pool_thread_group, "OOB", false, true); setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); timer=new TimeScheduler(timer_thread_factory, num_timer_threads); verifyRejectionPolicy(oob_thread_pool_rejection_policy); verifyRejectionPolicy(thread_pool_rejection_policy); // ========================================== OOB thread pool ============================== if(oob_thread_pool_enabled) { if(oob_thread_pool_queue_enabled) oob_thread_pool_queue=new LinkedBlockingQueue(oob_thread_pool_queue_max_size); else oob_thread_pool_queue=new SynchronousQueue(); oob_thread_pool=createThreadPool(oob_thread_pool_min_threads, oob_thread_pool_max_threads, oob_thread_pool_keep_alive_time, oob_thread_pool_rejection_policy, oob_thread_pool_queue, oob_thread_factory); } else { // otherwise use the caller's thread to unmarshal the byte buffer into a message oob_thread_pool=new DirectExecutor(); } // ====================================== Regular thread pool =========================== if(thread_pool_enabled) { if(thread_pool_queue_enabled) thread_pool_queue=new LinkedBlockingQueue(thread_pool_queue_max_size); else thread_pool_queue=new SynchronousQueue(); thread_pool=createThreadPool(thread_pool_min_threads, thread_pool_max_threads, thread_pool_keep_alive_time, thread_pool_rejection_policy, thread_pool_queue, default_thread_factory); } else { // otherwise use the caller's thread to unmarshal the byte buffer into a message thread_pool=new DirectExecutor(); } if(persistent_ports){ pm = new PortsManager(pm_expiry_time,persistent_ports_file); } if(bind_addr != null) { Map m=new HashMap(1); m.put("bind_addr", bind_addr); up(new Event(Event.CONFIG, m)); } } public void destroy() { if(init_count == 0) return; init_count=Math.max(init_count -1, 0); if(init_count == 0) { super.destroy(); if(timer != null) { try { timer.stop(); } catch(InterruptedException e) { log.error("failed stopping the timer", e); } } // 3. Stop the thread pools if(oob_thread_pool instanceof ThreadPoolExecutor) { shutdownThreadPool(oob_thread_pool); oob_thread_pool=null; } if(thread_pool instanceof ThreadPoolExecutor) { shutdownThreadPool(thread_pool); thread_pool=null; } } } /** * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */ public void start() throws Exception { if(timer == null) throw new Exception("timer is null"); if(enable_diagnostics) { diag_handler=new DiagnosticsHandler(); diag_handler.start(); for(ProbeHandler handler: preregistered_probe_handlers) diag_handler.registerProbeHandler(handler); preregistered_probe_handlers.clear(); } if(use_incoming_packet_handler && !use_concurrent_stack) { incoming_packet_queue=new Queue(); incoming_packet_handler=new IncomingPacketHandler(); incoming_packet_handler.start(); } if(loopback && !use_concurrent_stack) { incoming_msg_queue=new Queue(); incoming_msg_handler=new IncomingMessageHandler(); incoming_msg_handler.start(); } if(enable_bundling) { bundler=new Bundler(); } setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); sendUpLocalAddressEvent(); } public void stop() { if(diag_handler != null) { diag_handler.stop(); diag_handler=null; } // 1. Stop the incoming packet handler thread if(incoming_packet_handler != null) incoming_packet_handler.stop(); // 2. Stop the incoming message handler if(incoming_msg_handler != null) incoming_msg_handler.stop(); preregistered_probe_handlers.clear(); } protected void handleConnect() throws Exception { connect_count++; } protected void handleDisconnect() { connect_count=Math.max(0, connect_count -1); } public String getSingletonName() { return singleton_name; } /** * Setup the Protocol instance according to the configuration string * @return true if no other properties are left. * false if the properties still have data in them, ie , * properties are left over and not handled by the protocol stack */ public boolean setProperties(Properties props) { super.setProperties(props); String str; try { bind_addr=Util.getBindAddress(props); } catch(IOException unknown) { throw new IllegalArgumentException("failed determining bind address", unknown); } str=props.getProperty("use_local_host"); if(str != null) { use_local_host=Boolean.parseBoolean(str); props.remove("use_local_host"); } str=props.getProperty("bind_to_all_interfaces"); if(str != null) { receive_on_all_interfaces=Boolean.parseBoolean(str); props.remove("bind_to_all_interfaces"); log.warn("bind_to_all_interfaces has been deprecated; use receive_on_all_interfaces instead"); } str=props.getProperty("receive_on_all_interfaces"); if(str != null) { receive_on_all_interfaces=Boolean.parseBoolean(str); props.remove("receive_on_all_interfaces"); } str=props.getProperty("receive_interfaces"); if(str != null) { try { receive_interfaces=Util.parseInterfaceList(str); props.remove("receive_interfaces"); } catch(Exception e) { log.error("error determining interfaces (" + str + ")", e); return false; } } str=props.getProperty("send_on_all_interfaces"); if(str != null) { send_on_all_interfaces=Boolean.parseBoolean(str); props.remove("send_on_all_interfaces"); } str=props.getProperty("send_interfaces"); if(str != null) { try { send_interfaces=Util.parseInterfaceList(str); props.remove("send_interfaces"); } catch(Exception e) { log.error("error determining interfaces (" + str + ")", e); return false; } } str=props.getProperty("bind_port"); if(str != null) { bind_port=Integer.parseInt(str); props.remove("bind_port"); } str=props.getProperty("port_range"); if(str != null) { port_range=Integer.parseInt(str); props.remove("port_range"); } str=props.getProperty("persistent_ports_file"); if(str != null) { persistent_ports_file=str; props.remove("persistent_ports_file"); } str=props.getProperty("ports_expiry_time"); if(str != null) { pm_expiry_time=Integer.parseInt(str); if(pm != null) pm.setExpiryTime(pm_expiry_time); props.remove("ports_expiry_time"); } str=props.getProperty("persistent_ports"); if(str != null) { if(Boolean.valueOf(str).booleanValue()) pm=new PortsManager(pm_expiry_time, persistent_ports_file); props.remove("persistent_ports"); } str=props.getProperty("loopback"); if(str != null) { loopback=Boolean.valueOf(str).booleanValue(); props.remove("loopback"); } str=props.getProperty("discard_incompatible_packets"); if(str != null) { discard_incompatible_packets=Boolean.valueOf(str).booleanValue(); props.remove("discard_incompatible_packets"); } // this is deprecated, just left for compatibility (use use_incoming_packet_handler) str=props.getProperty("use_packet_handler"); if(str != null) { use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_packet_handler"); if(log.isWarnEnabled()) log.warn("'use_packet_handler' is deprecated; use 'use_incoming_packet_handler' instead"); } str=props.getProperty("use_incoming_packet_handler"); if(str != null) { use_incoming_packet_handler=Boolean.valueOf(str).booleanValue(); props.remove("use_incoming_packet_handler"); } str=props.getProperty("use_concurrent_stack"); if(str != null) { use_concurrent_stack=Boolean.valueOf(str).booleanValue(); props.remove("use_concurrent_stack"); } str=props.getProperty("thread_naming_pattern"); if(str != null) { thread_naming_pattern=str; props.remove("thread_naming_pattern"); } // ======================================= OOB thread pool ========================================= str=props.getProperty("oob_thread_pool.enabled"); if(str != null) { oob_thread_pool_enabled=Boolean.valueOf(str).booleanValue(); props.remove("oob_thread_pool.enabled"); } str=props.getProperty("oob_thread_pool.min_threads"); if(str != null) { oob_thread_pool_min_threads=Integer.valueOf(str).intValue(); props.remove("oob_thread_pool.min_threads"); } str=props.getProperty("oob_thread_pool.max_threads"); if(str != null) { oob_thread_pool_max_threads=Integer.valueOf(str).intValue(); props.remove("oob_thread_pool.max_threads"); } str=props.getProperty("oob_thread_pool.keep_alive_time"); if(str != null) { oob_thread_pool_keep_alive_time=Long.valueOf(str).longValue(); props.remove("oob_thread_pool.keep_alive_time"); } str=props.getProperty("oob_thread_pool.queue_enabled"); if(str != null) { oob_thread_pool_queue_enabled=Boolean.valueOf(str).booleanValue(); props.remove("oob_thread_pool.queue_enabled"); } str=props.getProperty("oob_thread_pool.queue_max_size"); if(str != null) { oob_thread_pool_queue_max_size=Integer.valueOf(str).intValue(); props.remove("oob_thread_pool.queue_max_size"); } str=props.getProperty("oob_thread_pool.rejection_policy"); if(str != null) { str=str.toLowerCase().trim(); oob_thread_pool_rejection_policy=str; if(!(str.equals("run") || str.equals("abort")|| str.equals("discard")|| str.equals("discardoldest"))) { log.error("rejection policy of " + str + " is unknown"); return false; } props.remove("oob_thread_pool.rejection_policy"); } // ======================================= Regular thread pool ========================================= str=props.getProperty("thread_pool.enabled"); if(str != null) { thread_pool_enabled=Boolean.valueOf(str).booleanValue(); props.remove("thread_pool.enabled"); } str=props.getProperty("thread_pool.min_threads"); if(str != null) { thread_pool_min_threads=Integer.valueOf(str).intValue(); props.remove("thread_pool.min_threads"); } str=props.getProperty("thread_pool.max_threads"); if(str != null) { thread_pool_max_threads=Integer.valueOf(str).intValue(); props.remove("thread_pool.max_threads"); } str=props.getProperty("thread_pool.keep_alive_time"); if(str != null) { thread_pool_keep_alive_time=Long.valueOf(str).longValue(); props.remove("thread_pool.keep_alive_time"); } str=props.getProperty("thread_pool.queue_enabled"); if(str != null) { thread_pool_queue_enabled=Boolean.valueOf(str).booleanValue(); props.remove("thread_pool.queue_enabled"); } str=props.getProperty("thread_pool.queue_max_size"); if(str != null) { thread_pool_queue_max_size=Integer.valueOf(str).intValue(); props.remove("thread_pool.queue_max_size"); } str=props.getProperty("thread_pool.rejection_policy"); if(str != null) { str=str.toLowerCase().trim(); thread_pool_rejection_policy=str; if(!(str.equals("run") || str.equals("abort")|| str.equals("discard")|| str.equals("discardoldest"))) { log.error("rejection policy of " + str + " is unknown"); return false; } props.remove("thread_pool.rejection_policy"); } str=props.getProperty("use_outgoing_packet_handler"); if(str != null) { log.warn("Attribute \"use_outgoing_packet_handler\" has been deprecated and is ignored"); props.remove("use_outgoing_packet_handler"); } str=props.getProperty("outgoing_queue_max_size"); if(str != null) { log.warn("Attribute \"use_outgoing_queue_max_size\" has been deprecated and is ignored"); props.remove("outgoing_queue_max_size"); } str=props.getProperty("max_bundle_size"); if(str != null) { int bundle_size=Integer.parseInt(str); if(bundle_size <= 0) { if(log.isErrorEnabled()) log.error("max_bundle_size (" + bundle_size + ") is <= 0"); return false; } max_bundle_size=bundle_size; props.remove("max_bundle_size"); } str=props.getProperty("max_bundle_timeout"); if(str != null) { max_bundle_timeout=Long.parseLong(str); if(max_bundle_timeout <= 0) { if(log.isErrorEnabled()) log.error("max_bundle_timeout of " + max_bundle_timeout + " is invalid"); return false; } props.remove("max_bundle_timeout"); } str=props.getProperty("enable_bundling"); if(str != null) { enable_bundling=Boolean.valueOf(str).booleanValue(); props.remove("enable_bundling"); } str=props.getProperty("enable_unicast_bundling"); if(str != null) { enable_unicast_bundling=Boolean.valueOf(str).booleanValue(); props.remove("enable_unicast_bundling"); } str=props.getProperty("enable_diagnostics"); if(str != null) { enable_diagnostics=Boolean.valueOf(str).booleanValue(); props.remove("enable_diagnostics"); } str=props.getProperty("diagnostics_addr"); if(str != null) { diagnostics_addr=str; props.remove("diagnostics_addr"); } str=props.getProperty("diagnostics_port"); if(str != null) { diagnostics_port=Integer.parseInt(str); props.remove("diagnostics_port"); } str=props.getProperty("timer.max_threads"); if(str != null) { num_timer_threads=Integer.parseInt(str); props.remove("timer.max_threads"); log.warn("timer.max_threads is deprecated; use timer.num_threads instead"); } str=props.getProperty("timer.num_threads"); if(str != null) { num_timer_threads=Integer.parseInt(str); props.remove("timer.num_threads"); } str=props.getProperty(Global.SINGLETON_NAME); if(str != null) { singleton_name=str; props.remove(Global.SINGLETON_NAME); } return true; } /** * handle the UP event. * @param evt - the event being send from the stack */ public Object up(Event evt) { switch(evt.getType()) { case Event.CONFIG: if(singleton_name != null) passToAllUpProtocols(evt); else up_prot.up(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); return null; } if(singleton_name != null) { passToAllUpProtocols(evt); return null; } else return up_prot.up(evt); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling down()). */ public Object down(Event evt) { if(evt.getType() != Event.MSG) { // unless it is a message handle it and respond return handleDownEvent(evt); } Message msg=(Message)evt.getArg(); if(header != null) { // added patch by Roland Kurmann (March 20 2003) // msg.putHeader(name, new TpHeader(channel_name)); msg.putHeaderIfAbsent(name, header); } setSourceAddress(msg); // very important !! listToBuffer() will fail with a null src address !! if(log.isTraceEnabled()) { log.trace("sending msg to " + msg.getDest() + ", src=" + msg.getSrc() + ", headers are " + msg.printHeaders()); } // Don't send if destination is local address. Instead, switch dst and src and put in up_queue. // If multicast message, loopback a copy directly to us (but still multicast). Once we receive this, // we will discard our own multicast message Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); if(loopback && (multicast || dest.equals(local_addr))) { // we *have* to make a copy, or else up_prot.up() might remove headers from msg which will then *not* // be available for marshalling further down (when sending the message) final Message copy=msg.copy(); if(log.isTraceEnabled()) log.trace(new StringBuilder("looping back message ").append(copy)); // up_prot.up(new Event(Event.MSG, copy)); // changed to fix http://jira.jboss.com/jira/browse/JGRP-506 Executor pool=msg.isFlagSet(Message.OOB)? oob_thread_pool : thread_pool; pool.execute(new Runnable() { public void run() { passMessageUp(copy, false); } }); if(!multicast) return null; } try { send(msg, dest, multicast); } catch(InterruptedIOException iex) { ; } catch(InterruptedException interruptedEx) { Thread.currentThread().interrupt(); // let someone else handle the interrupt } catch(Throwable e) { if(log.isErrorEnabled()) { String dst=msg.getDest() == null? "null" : msg.getDest().toString(); log.error("failed sending message to " + dst + " (" + msg.size() + " bytes)", e); } } return null; } /*--------------------------- End of Protocol interface -------------------------- */ /* ------------------------------ Private Methods -------------------------------- */ /** * If the sender is null, set our own address. We cannot just go ahead and set the address * anyway, as we might be sending a message on behalf of someone else ! E.gin case of * retransmission, when the original sender has crashed, or in a FLUSH protocol when we * have to return all unstable messages with the FLUSH_OK response. */ private void setSourceAddress(Message msg) { if(msg.getSrc() == null) msg.setSrc(local_addr); } private void passMessageUp(Message msg, boolean perform_cluster_name_matching) { TpHeader hdr=(TpHeader)msg.getHeader(name); // replaced removeHeader() with getHeader() if(hdr == null) { if(channel_name == null) { Event evt=new Event(Event.MSG, msg); if(singleton_name != null) { passMessageToAll(evt); } else { up_prot.up(evt); } } else { if(log.isErrorEnabled()) log.error(new StringBuilder("message does not have a transport header, msg is ").append(msg). append(", headers are ").append(msg.printHeaders()).append(", will be discarded")); } return; } String ch_name=hdr.channel_name; if(singleton_name != null) { Protocol tmp_prot=up_prots.get(ch_name); if(tmp_prot != null) { Event evt=new Event(Event.MSG, msg); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("message is ").append(msg).append(", headers are ").append(msg.printHeaders()); log.trace(sb); } tmp_prot.up(evt); } else { // we discard messages for a group we don't have. If we had a scenario with channel C1 and A,B on it, // and channel C2 and only A on it (asymmetric setup), then C2 would always log warnings that B was // not found (Jan 25 2008 (bela)) // if(log.isWarnEnabled()) // log.warn(new StringBuilder("discarded message from group \"").append(ch_name). // append("\" (our groups are ").append(up_prots.keySet()).append("). Sender was ").append(msg.getSrc())); } } else { // Discard if message's group name is not the same as our group name if(perform_cluster_name_matching && channel_name != null && !channel_name.equals(ch_name)) { if(log.isWarnEnabled()) log.warn(new StringBuilder("discarded message from different group \"").append(ch_name). append("\" (our group is \"").append(channel_name).append("\"). Sender was ").append(msg.getSrc())); } else { Event evt=new Event(Event.MSG, msg); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("message is ").append(msg).append(", headers are ").append(msg.printHeaders()); log.trace(sb); } up_prot.up(evt); } } } private void passMessageToAll(Event evt) { for(Protocol tmp_prot: up_prots.values()) { try { tmp_prot.up(evt); } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failure passing message up: message is " + evt.getArg(), ex); } } } /** * Subclasses must call this method when a unicast or multicast message has been received. * Declared final so subclasses cannot override this method. * * @param dest * @param sender * @param data * @param offset * @param length */ protected final void receive(Address dest, Address sender, byte[] data, int offset, int length) { if(data == null) return; if(log.isTraceEnabled()){ boolean mcast=dest == null || dest.isMulticastAddress(); StringBuilder sb=new StringBuilder("received ("); sb.append(mcast? "mcast) " : "ucast) ").append(length).append(" bytes from ").append(sender); log.trace(sb); } try { // determine whether OOB or not by looking at first byte of 'data' boolean oob=false; byte oob_flag=data[Global.SHORT_SIZE]; // we need to skip the first 2 bytes (version) if((oob_flag & OOB) == OOB) oob=true; if(use_concurrent_stack) { if(oob) { num_oob_msgs_received++; dispatchToThreadPool(oob_thread_pool, dest, sender, data, offset, length); } else { num_incoming_msgs_received++; dispatchToThreadPool(thread_pool, dest, sender, data, offset, length); } } else { if(use_incoming_packet_handler) { byte[] tmp=new byte[length]; System.arraycopy(data, offset, tmp, 0, length); incoming_packet_queue.add(new IncomingPacket(dest, sender, tmp, 0, length)); } else handleIncomingPacket(dest, sender, data, offset, length); } } catch(Throwable t) { if(log.isErrorEnabled()) log.error(new StringBuilder("failed handling data from ").append(sender), t); } } private void dispatchToThreadPool(Executor pool, Address dest, Address sender, byte[] data, int offset, int length) { if(pool instanceof DirectExecutor) { // we don't make a copy of the buffer if we execute on this thread pool.execute(new IncomingPacket(dest, sender, data, offset, length)); } else { byte[] tmp=new byte[length]; System.arraycopy(data, offset, tmp, 0, length); pool.execute(new IncomingPacket(dest, sender, tmp, 0, length)); } } /** * Processes a packet read from either the multicast or unicast socket. Needs to be synchronized because * mcast or unicast socket reads can be concurrent. * Correction (bela April 19 2005): we access no instance variables, all vars are allocated on the stack, so * this method should be reentrant: removed 'synchronized' keyword */ private void handleIncomingPacket(Address dest, Address sender, byte[] data, int offset, int length) { Message msg=null; short version=0; boolean is_message_list, multicast; byte flags; List msgs; try { synchronized(in_stream) { in_stream.setData(data, offset, length); try { version=dis.readShort(); } catch(IOException ex) { if(discard_incompatible_packets) return; throw ex; } if(Version.isBinaryCompatible(version) == false) { if(log.isWarnEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("packet from ").append(sender).append(" has different version (").append(Version.print(version)); sb.append(") from ours (").append(Version.printVersion()).append("). "); if(discard_incompatible_packets) sb.append("Packet is discarded"); else sb.append("This may cause problems"); log.warn(sb); } if(discard_incompatible_packets) return; } flags=dis.readByte(); is_message_list=(flags & LIST) == LIST; multicast=(flags & MULTICAST) == MULTICAST; if(is_message_list) msgs=readMessageList(dis, dest, multicast); else { msg=readMessage(dis, dest, sender, multicast); msgs=new LinkedList(); msgs.add(msg); } } Address src; for(Iterator it=msgs.iterator(); it.hasNext();) { msg=it.next(); src=msg.getSrc(); if(loopback) { if(multicast && src != null && src.equals(local_addr)) { // discard own loopback multicast packets it.remove(); } } else handleIncomingMessage(msg); } if(incoming_msg_queue != null && !msgs.isEmpty()) incoming_msg_queue.addAll(msgs); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed unmarshalling message", t); } } private void handleIncomingMessage(Message msg) { if(stats) { num_msgs_received++; num_bytes_received+=msg.getLength(); } passMessageUp(msg, true); } /** Internal method to serialize and send a message. This method is not reentrant */ private void send(Message msg, Address dest, boolean multicast) throws Exception { // bundle only regular messages; send OOB messages directly if(enable_bundling && !msg.isFlagSet(Message.OOB)) { if(!enable_unicast_bundling && !multicast) { ; // don't bundle unicast msgs if enable_unicast_bundling is off (http://jira.jboss.com/jira/browse/JGRP-429) } else { bundler.send(msg, dest); return; } } ExposedByteArrayOutputStream out_stream=null; ExposedDataOutputStream dos=null; Buffer buf; out_stream=new ExposedByteArrayOutputStream(INITIAL_BUFSIZE); dos=new ExposedDataOutputStream(out_stream); writeMessage(msg, dos, multicast); buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); doSend(buf, dest, multicast); } private void doSend(Buffer buf, Address dest, boolean multicast) throws Exception { if(stats) { num_msgs_sent++; num_bytes_sent+=buf.getLength(); } if(multicast) { sendToAllMembers(buf.getBuf(), buf.getOffset(), buf.getLength()); } else { sendToSingleMember(dest, buf.getBuf(), buf.getOffset(), buf.getLength()); } } /** * This method needs to be synchronized on out_stream when it is called * @param msg * @return * @throws java.io.IOException */ private static void writeMessage(Message msg, DataOutputStream dos, boolean multicast) throws Exception { byte flags=0; dos.writeShort(Version.version); // write the version if(multicast) flags+=MULTICAST; if(msg.isFlagSet(Message.OOB)) flags+=OOB; dos.writeByte(flags); msg.writeTo(dos); } private Message readMessage(DataInputStream instream, Address dest, Address sender, boolean multicast) throws Exception { Message msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(instream); postUnmarshalling(msg, dest, sender, multicast); // allows for optimization by subclass return msg; } private static void writeMessageList(List msgs, DataOutputStream dos, boolean multicast) throws Exception { Address src; byte flags=0; int len=msgs != null? msgs.size() : 0; boolean src_written=false; dos.writeShort(Version.version); flags+=LIST; if(multicast) flags+=MULTICAST; dos.writeByte(flags); dos.writeInt(len); if(msgs != null) { for(Message msg: msgs) { src=msg.getSrc(); if(!src_written) { Util.writeAddress(src, dos); src_written=true; } msg.writeTo(dos); } } } private List readMessageList(DataInputStream instream, Address dest, boolean multicast) throws Exception { List list=new LinkedList(); int len; Message msg; Address src; len=instream.readInt(); src=Util.readAddress(instream); for(int i=0; i < len; i++) { msg=new Message(false); // don't create headers, readFrom() will do this msg.readFrom(instream); postUnmarshallingList(msg, dest, multicast); msg.setSrc(src); list.add(msg); } return list; } protected Object handleDownEvent(Event evt) { switch(evt.getType()) { case Event.TMP_VIEW: case Event.VIEW_CHANGE: synchronized(members) { view=(View)evt.getArg(); members.clear(); if(singleton_name == null) { Vector
    tmpvec=view.getMembers(); members.addAll(tmpvec); } else { for(Protocol prot: up_prots.values()) { if(prot instanceof ProtocolAdapter) { ProtocolAdapter ad=(ProtocolAdapter)prot; List
    tmp=ad.getMembers(); members.addAll(tmp); } } } } break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: channel_name=(String)evt.getArg(); header=new TpHeader(channel_name); setInAllThreadFactories(channel_name, local_addr, thread_naming_pattern); setThreadNames(); connectLock.lock(); try { handleConnect(); } catch(Exception e) { throw new RuntimeException(e); } finally { connectLock.unlock(); } return null; case Event.DISCONNECT: unsetThreadNames(); connectLock.lock(); try { handleDisconnect(); } finally { connectLock.unlock(); } break; case Event.CONFIG: if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); break; } return null; } protected void setThreadNames() { if(incoming_packet_handler != null) global_thread_factory.renameThread(IncomingPacketHandler.THREAD_NAME, incoming_packet_handler.getThread()); if(incoming_msg_handler != null) { global_thread_factory.renameThread(IncomingMessageHandler.THREAD_NAME, incoming_msg_handler.getThread()); } if(diag_handler != null) { global_thread_factory.renameThread(DiagnosticsHandler.THREAD_NAME, diag_handler.getThread()); } } protected void unsetThreadNames() { if(incoming_packet_handler != null && incoming_packet_handler.getThread() != null) incoming_packet_handler.getThread().setName(IncomingPacketHandler.THREAD_NAME); if(incoming_msg_handler != null && incoming_msg_handler.getThread() != null) incoming_msg_handler.getThread().setName(IncomingMessageHandler.THREAD_NAME); if(diag_handler != null && diag_handler.getThread() != null) diag_handler.getThread().setName(DiagnosticsHandler.THREAD_NAME); } private void setInAllThreadFactories(String cluster_name, Address local_address, String pattern) { ThreadFactory[] factories={timer_thread_factory, default_thread_factory, oob_thread_factory, global_thread_factory}; boolean is_shared_transport=singleton_name != null && singleton_name.trim().length() > 0; for(ThreadFactory factory:factories) { if(pattern != null) { factory.setPattern(pattern); if(is_shared_transport) factory.setIncludeClusterName(false); } if(cluster_name != null && !is_shared_transport) // only set cluster name if we don't have a shared transport factory.setClusterName(cluster_name); if(local_address != null) factory.setAddress(local_address.toString()); } } protected void handleConfigEvent(Map map) { if(map == null) return; if(map.containsKey("additional_data")) { additional_data=(byte[])map.get("additional_data"); if(local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(additional_data); } } protected static ExecutorService createThreadPool(int min_threads, int max_threads, long keep_alive_time, String rejection_policy, BlockingQueue queue, final ThreadFactory factory) { ThreadPoolExecutor pool=new ThreadManagerThreadPoolExecutor(min_threads, max_threads, keep_alive_time, TimeUnit.MILLISECONDS, queue); pool.setThreadFactory(factory); //default RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); if(rejection_policy != null) { if(rejection_policy.equals("abort")) handler = new ThreadPoolExecutor.AbortPolicy(); else if(rejection_policy.equals("discard")) handler = new ThreadPoolExecutor.DiscardPolicy(); else if(rejection_policy.equals("discardoldest")) handler = new ThreadPoolExecutor.DiscardOldestPolicy(); } pool.setRejectedExecutionHandler(new ShutdownRejectedExecutionHandler(handler)); return pool; } private static void shutdownThreadPool(Executor thread_pool) { if(thread_pool instanceof ExecutorService) { ExecutorService service=(ExecutorService)thread_pool; service.shutdownNow(); try { service.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } } private void verifyRejectionPolicy(String str) throws Exception{ if(!(str.equalsIgnoreCase("run") || str.equalsIgnoreCase("abort")|| str.equalsIgnoreCase("discard")|| str.equalsIgnoreCase("discardoldest"))) { log.error("rejection policy of " + str + " is unknown"); throw new Exception("Unknown rejection policy " + str); } } protected void passToAllUpProtocols(Event evt) { for(Protocol prot: up_prots.values()) { try { prot.up(evt); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed passing up event " + evt, e); } } } public void sendUpLocalAddressEvent() { if(up_prot != null) up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); else { for(Map.Entry entry: up_prots.entrySet()) { String tmp=entry.getKey(); if(tmp.startsWith(Global.DUMMY)) continue; Protocol prot=entry.getValue(); prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } } } /* ----------------------------- End of Private Methods ---------------------------------------- */ /* ----------------------------- Inner Classes ---------------------------------------- */ class IncomingPacket implements Runnable { Address dest=null; Address sender=null; byte[] buf; int offset, length; IncomingPacket(Address dest, Address sender, byte[] buf, int offset, int length) { this.dest=dest; this.sender=sender; this.buf=buf; this.offset=offset; this.length=length; } /** Code copied from handleIncomingPacket */ public void run() { short version=0; boolean is_message_list, multicast; byte flags; ExposedByteArrayInputStream in_stream=null; DataInputStream dis=null; try { in_stream=new ExposedByteArrayInputStream(buf, offset, length); dis=new DataInputStream(in_stream); try { version=dis.readShort(); } catch(IOException ex) { if(discard_incompatible_packets) return; throw ex; } if(Version.isBinaryCompatible(version) == false) { if(log.isWarnEnabled()) { StringBuilder sb=new StringBuilder(); sb.append("packet from ").append(sender).append(" has different version (").append(Version.print(version)); sb.append(") from ours (").append(Version.printVersion()).append("). "); if(discard_incompatible_packets) sb.append("Packet is discarded"); else sb.append("This may cause problems"); log.warn(sb); } if(discard_incompatible_packets) return; } flags=dis.readByte(); is_message_list=(flags & LIST) == LIST; multicast=(flags & MULTICAST) == MULTICAST; if(is_message_list) { // used if message bundling is enabled List msgs=readMessageList(dis, dest, multicast); for(Message msg: msgs) { if(msg.isFlagSet(Message.OOB)) { log.warn("bundled message should not be marked as OOB"); } handleMyMessage(msg, multicast); } } else { Message msg=readMessage(dis, dest, sender, multicast); handleMyMessage(msg, multicast); } } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed handling incoming message", t); } } private void handleMyMessage(Message msg, boolean multicast) { if(stats) { num_msgs_received++; num_bytes_received+=msg.getLength(); } Address src=msg.getSrc(); if(loopback && multicast && src != null && src.equals(local_addr)) { return; // drop message that was already looped back and delivered } passMessageUp(msg, true); } } /** * This thread fetches byte buffers from the packet_queue, converts them into messages and passes them up * to the higher layer (done in handleIncomingUdpPacket()). */ class IncomingPacketHandler implements Runnable { public static final String THREAD_NAME="IncomingPacketHandler"; Thread t=null; Thread getThread(){ return t; } void start() { if(t == null || !t.isAlive()) { t=global_thread_factory.newThread(this, THREAD_NAME); t.setDaemon(true); t.start(); } } void stop() { incoming_packet_queue.close(true); // should terminate the packet_handler thread too if(t != null) { try { t.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } } } public void run() { IncomingPacket entry; while(!incoming_packet_queue.closed() && Thread.currentThread().equals(t)) { try { entry=(IncomingPacket)incoming_packet_queue.remove(); handleIncomingPacket(entry.dest, entry.sender, entry.buf, entry.offset, entry.length); } catch(QueueClosedException closed_ex) { break; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("error processing incoming packet", ex); } } if(log.isTraceEnabled()) log.trace("incoming packet handler terminating"); } } class IncomingMessageHandler implements Runnable { public static final String THREAD_NAME = "IncomingMessageHandler"; Thread t; Thread getThread(){ return t; } public void start() { if(t == null || !t.isAlive()) { t=global_thread_factory.newThread(this, THREAD_NAME); t.setDaemon(true); t.start(); } } public void stop() { incoming_msg_queue.close(true); if(t != null) { try { t.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } } } public void run() { Message msg; while(!incoming_msg_queue.closed() && Thread.currentThread().equals(t)) { try { msg=(Message)incoming_msg_queue.remove(); handleIncomingMessage(msg); } catch(QueueClosedException closed_ex) { break; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("error processing incoming message", ex); } } if(log.isTraceEnabled()) log.trace("incoming message handler terminating"); } } private class Bundler { static final int MIN_NUMBER_OF_BUNDLING_TASKS=2; /** HashMap>. Keys are destinations, values are lists of Messages */ final Map> msgs=new HashMap>(36); @GuardedBy("lock") long count=0; // current number of bytes accumulated int num_msgs=0; @GuardedBy("lock") int num_bundling_tasks=0; long last_bundle_time; final ReentrantLock lock=new ReentrantLock(); private void send(Message msg, Address dest) throws Exception { long length=msg.size(); checkLength(length); Map> bundled_msgs=null; lock.lock(); try { if(count + length >= max_bundle_size) { bundled_msgs=removeBundledMessages(); } addMessage(msg, dest); count+=length; if(num_bundling_tasks < MIN_NUMBER_OF_BUNDLING_TASKS) { num_bundling_tasks++; timer.schedule(new BundlingTimer(), max_bundle_timeout, TimeUnit.MILLISECONDS); } } finally { lock.unlock(); } if(bundled_msgs != null) { sendBundledMessages(bundled_msgs); } } /** Run with lock acquired */ private void addMessage(Message msg, Address dest) { // no sync needed, always called with lock held if(msgs.isEmpty()) last_bundle_time=System.currentTimeMillis(); List tmp=msgs.get(dest); if(tmp == null) { tmp=new LinkedList(); msgs.put(dest, tmp); } tmp.add(msg); num_msgs++; } /** Must always be called with lock held */ private Map> removeBundledMessages() { if(msgs.isEmpty()) return null; Map> copy=new HashMap>(msgs); if(log.isTraceEnabled()) { long stop=System.currentTimeMillis(); double percentage=100.0 / max_bundle_size * count; StringBuilder sb=new StringBuilder("sending ").append(num_msgs).append(" msgs ("); num_msgs=0; sb.append(count).append(" bytes (" + f.format(percentage) + "% of max_bundle_size)"); if(last_bundle_time > 0) { sb.append(", collected in ").append(stop-last_bundle_time).append("ms) "); } sb.append(" to ").append(copy.size()).append(" destination(s)"); if(copy.size() > 1) sb.append(" (dests=").append(copy.keySet()).append(")"); log.trace(sb); } msgs.clear(); count=0; return copy; } /** * Sends all messages from the map, all messages for the same destination are bundled into 1 message. * This method may be called by timer and bundler concurrently * @param msgs */ private void sendBundledMessages(Map> msgs) { boolean multicast; Buffer buffer; Map.Entry> entry; Address dst; ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(INITIAL_BUFSIZE); ExposedDataOutputStream dos=new ExposedDataOutputStream(out_stream); boolean first=true; for(Iterator>> it=msgs.entrySet().iterator(); it.hasNext();) { entry=it.next(); List list=entry.getValue(); if(list.isEmpty()) continue; dst=entry.getKey(); multicast=dst == null || dst.isMulticastAddress(); try { if(first) { first=false; } else { out_stream.reset(); dos.reset(); } writeMessageList(list, dos, multicast); // flushes output stream when done buffer=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); doSend(buffer, dst, multicast); } catch(Throwable e) { if(log.isErrorEnabled()) log.error("exception sending msg: " + e.toString(), e.getCause()); } } } private void checkLength(long len) throws Exception { if(len > max_bundle_size) throw new Exception("message size (" + len + ") is greater than max bundling size (" + max_bundle_size + "). Set the fragmentation/bundle size in FRAG and TP correctly"); } private class BundlingTimer implements Runnable { public void run() { Map> msgs=null; boolean unlocked=false; lock.lock(); try { msgs=removeBundledMessages(); if(msgs != null) { lock.unlock(); unlocked=true; sendBundledMessages(msgs); } } finally { if(unlocked) lock.lock(); num_bundling_tasks--; lock.unlock(); } } } } public interface ProbeHandler { /** * Handles a probe. For each key that is handled, the key and its result should be in the returned map. * @param keys * @return Map. A map of keys and values. A null return value is permissible. */ Map handleProbe(String... keys); /** Returns a list of supported keys */ String[] supportedKeys(); } private class DiagnosticsHandler implements Runnable { public static final String THREAD_NAME = "DiagnosticsHandler"; private Thread thread=null; private MulticastSocket diag_sock=null; private final Set handlers=new HashSet(); DiagnosticsHandler() { } Thread getThread(){ return thread; } void registerProbeHandler(ProbeHandler handler) { if(handler != null) handlers.add(handler); } void unregisterProbeHandler(ProbeHandler handler) { if(handler != null) handlers.remove(handler); } void start() throws IOException { registerProbeHandler(new ProbeHandler() { public Map handleProbe(String... keys) { Map retval=new HashMap(2); for(String key: keys) { if(key.equals("dump")) { retval.put("dump", Util.dumpThreads()); continue; } if(key.equals("keys")) { StringBuilder sb=new StringBuilder(); for(ProbeHandler handler: handlers) { String[] tmp=handler.supportedKeys(); if(tmp != null && tmp.length > 0) { for(String s: tmp) sb.append(s).append(" "); } } retval.put("keys", sb.toString()); } } return retval; } public String[] supportedKeys() { return new String[]{"dump", "keys"}; } }); diag_sock=new MulticastSocket(diagnostics_port); java.util.List interfaces=Util.getAllAvailableInterfaces(); bindToInterfaces(interfaces, diag_sock); if(thread == null || !thread.isAlive()) { thread=global_thread_factory.newThread(this, THREAD_NAME); thread.setDaemon(true); thread.start(); } } void stop() { if(diag_sock != null) diag_sock.close(); handlers.clear(); if(thread != null){ try{ thread.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e){ Thread.currentThread().interrupt(); // set interrupt flag } } } public void run() { byte[] buf=new byte[1500]; // MTU on most LANs DatagramPacket packet; while(!diag_sock.isClosed() && Thread.currentThread().equals(thread)) { packet=new DatagramPacket(buf, 0, buf.length); try { diag_sock.receive(packet); handleDiagnosticProbe(packet.getSocketAddress(), diag_sock, new String(packet.getData(), packet.getOffset(), packet.getLength())); } catch(IOException e) { } } } private void handleDiagnosticProbe(SocketAddress sender, DatagramSocket sock, String request) { StringTokenizer tok=new StringTokenizer(request); List list=new ArrayList(10); while(tok.hasMoreTokens()) { String req=tok.nextToken().trim(); if(req.length() > 0) list.add(req); } String[] tokens=new String[list.size()]; for(int i=0; i < list.size(); i++) tokens[i]=list.get(i); for(ProbeHandler handler: handlers) { Map map=handler.handleProbe(tokens); if(map == null || map.isEmpty()) continue; if(!map.containsKey("local_addr")) map.put("local_addr", local_addr != null? local_addr.toString() : "n/a"); if(!map.containsKey("cluster")) map.put("cluster", channel_name != null? channel_name : "n/a"); StringBuilder info=new StringBuilder(); for(Map.Entry entry: map.entrySet()) { info.append(entry.getKey()).append("=").append(entry.getValue()).append("\r\n"); } byte[] diag_rsp=info.toString().getBytes(); if(log.isDebugEnabled()) log.debug("sending diag response to " + sender); try { sendResponse(sock, sender, diag_rsp); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed sending diag rsp to " + sender, t); } } } private void sendResponse(DatagramSocket sock, SocketAddress sender, byte[] buf) throws IOException { DatagramPacket p=new DatagramPacket(buf, 0, buf.length, sender); sock.send(p); } private void bindToInterfaces(java.util.List interfaces, MulticastSocket s) { SocketAddress group_addr=new InetSocketAddress(diagnostics_addr, diagnostics_port); for(Iterator it=interfaces.iterator(); it.hasNext();) { NetworkInterface i=it.next(); try { if (i.getInetAddresses().hasMoreElements()) { // fix for VM crash - suggested by JJalenak@netopia.com s.joinGroup(group_addr, i); if(log.isTraceEnabled()) log.trace("joined " + group_addr + " on " + i.getName()); } } catch(IOException e) { log.warn("failed to join " + group_addr + " on " + i.getName() + ": " + e); } } } } public static class ProtocolAdapter extends Protocol { final String cluster_name; final String transport_name; final TpHeader header; final List
    members=new CopyOnWriteArrayList
    (); final ThreadFactory factory; public ProtocolAdapter(String cluster_name, String transport_name, Protocol up, Protocol down, String pattern, Address addr) { this.cluster_name=cluster_name; this.transport_name=transport_name; this.up_prot=up; this.down_prot=down; this.header=new TpHeader(cluster_name); this.factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "", false); factory.setPattern(pattern); if(addr != null) factory.setAddress(addr.toString()); } public List
    getMembers() { return Collections.unmodifiableList(members); } public ThreadFactory getThreadFactory() { return factory; } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); msg.putHeader(transport_name, header); break; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); Vector
    tmp=view.getMembers(); members.clear(); members.addAll(tmp); break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: factory.setClusterName((String)evt.getArg()); break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: Address addr=(Address)evt.getArg(); if(addr != null) factory.setAddress(addr.toString()); break; } return up_prot.up(evt); } public String getName() { return "TP.ProtocolAdapter"; } public String toString() { return cluster_name + " (" + transport_name + ")"; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/DELAY_JOIN_REQ.java0000644000175000017500000000434411366547366026465 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.util.Date; import java.util.Properties; /** * Discards 2 JOIN-REQs then accepts 1, then discards 2 more and so on * @author Bela Ban * @version $Id: DELAY_JOIN_REQ.java,v 1.2.2.2 2007/11/20 08:37:24 belaban Exp $ */ public class DELAY_JOIN_REQ extends Protocol { private long delay=4000; public String getName() { return "DELAY_JOIN_REQ"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("delay"); if(str != null) { delay=Long.parseLong(str); props.remove("delay"); } if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } public long getDelay() { return delay; } public void setDelay(long delay) { this.delay=delay; } public Object up(final Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); final GMS.GmsHeader hdr=(GMS.GmsHeader)msg.getHeader("GMS"); if(hdr != null) { switch(hdr.getType()) { case GMS.GmsHeader.JOIN_REQ: case GMS.GmsHeader.JOIN_REQ_WITH_STATE_TRANSFER: System.out.println(new Date() + ": delaying JOIN-REQ by " + delay + " ms"); Thread thread=new Thread() { public void run() { Util.sleep(delay); System.out.println(new Date() + ": sending up delayed JOIN-REQ by " + hdr.getMember()); up_prot.up(evt); } }; thread.start(); return null; } } break; } return up_prot.up(evt); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/ENCRYPT.java0000644000175000017500000013045011366547366025423 0ustar twernertwerner// $Id: ENCRYPT.java,v 1.38.4.1 2008/03/12 07:28:27 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.QueueClosedException; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import javax.crypto.*; import javax.crypto.spec.SecretKeySpec; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.*; import java.security.cert.CertificateException; import java.security.spec.X509EncodedKeySpec; import java.util.Map; import java.util.Properties; import java.util.WeakHashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * ENCRYPT layer. Encrypt and decrypt the group communication in JGroups * * The file can be used in two ways: *
      *
    • Option 1. Configured with a secretKey in a keystore so it can be used at any * layer in JGroups without the need for a coordinator, or if you want protection against passive * monitoring but do not want the key exchange overhead and complexity. In this mode all nodes must be distributed with * the same keystore file. *
    • Option 2. Configured with algorithms and key sizes. The Encrypt Layer in this mode sould be used between the * FRAG and PBCast layers in the stack. The coordinator then chooses the * secretkey which it distributes amongst all the peers. In this form no keystore exists as the keys are * distributed using a public/private key exchange. View changes that identify a new controller will result in a new session key * being generated and then distributed to all peers. This overhead can be substantial in a an application with * a reasonable peer churn. *
    *

    *

    * Each message is identified as encrypted with a specific encryption header which identifies * the type of encrypt header and an MD5 digest that identifies the version of the key being used * to encrypt/decrypt the messages. *

    *

    *

    Option 1


    * This is the simplest option and can be used by simply inserting the Encryption layer * at any point in the JGroup stack - it will encrypt all Events of a type MSG that * have a non-null message buffer. The format of the entry in this form is:
    * <ENCRYPT key_store_name="defaultStore.keystore" store_password="changeit" alias="myKey"/>
    * An example bare-bones.xml file showing the keystore version can be found in the conf * ina file called EncryptKeyStore.xml - along with a defaultStore.keystore file.
    * In order to use the Encrypt layer in this manner it is necessary to have the secretKey already generated * in a keystore file. The directory containing the keystore file must be on the application's classpath. * You cannot create a SecretKey keystore file using the keytool application shipped with the JDK. * A java file called KeyStoreGenerator is included in the demo * package that can be used from the command line (or IDE) to generate a suitable keystore. *

    *

    *

    Option 2


    * This option is suited to an application that does not ship with a known key but instead it is generated * and distributed by the controller. The secret key is first generated by the Controller (in JGroup terms). When a view change * occurs a peer will request the secret key by sending a key request with its own public key. The controller * encrypts the secret key with this key and sends it back to the peer who then decrypts it and installs the * key as its own secret key. *
    All encryption and decryption of Messages is done using this key. When a peer receives * a view change that shows a different keyserver it will repeat this process - the view change event * also trigger the encrypt layer to queue up and down messages until the new key is installed. * The previous keys are retained so that messages sent before the view change that are queued can be decrypted * if the key is different. *
    * An example EncryptNoKeyStore.xml is included in the conf file as a guide. *

    *
    Note: the current version does not support the concept of perfect forward encryption (PFE) * which means that if a peer leaves the group the keys are re-generated preventing the departed peer from * decrypting future messages if it chooses to listen in on the group. This is not included as it really requires * a suitable authentication scheme as well to make this feature useful as there is nothing to stop the peer rejoining and receiving the new * key. A future release will address this issue. * * @author Steve Woodcock * @author Bela Ban */ public class ENCRYPT extends Protocol { Observer observer; interface Observer { void up(Event evt); void passUp(Event evt); void down(Event evt); void passDown(Event evt); } static final String DEFAULT_SYM_ALGO = "Blowfish"; // address info Address local_addr = null; // keyserver address Address keyServerAddr = null; //used to see whether we are the key server boolean keyServer = false; // encryption properties in no supplied key mode String asymProvider = null; static final String symProvider = null; String asymAlgorithm = "RSA"; String symAlgorithm = DEFAULT_SYM_ALGO; int asymInit = 512; // initial public/private key length int symInit = 56; // initial shared key length // properties for functioning in supplied key mode private boolean suppliedKey = false; private String keyStoreName; private String storePassword ="changeit"; //JDK default private String keyPassword="changeit"; //JDK default private String alias="mykey"; // JDK default // public/private Key KeyPair Kpair; // to store own's public/private Key // for client to store server's public Key PublicKey serverPubKey = null; // needed because we do simultaneous encode/decode with these ciphers - which // would be a threading issue Cipher symEncodingCipher; Cipher symDecodingCipher; // version filed for secret key private String symVersion = null; // dhared secret key to encrypt/decrypt messages SecretKey secretKey = null; // map to hold previous keys so we can decrypt some earlier messages if we need to final Map keyMap = new WeakHashMap(); // queues to buffer data while we are swapping shared key // or obtsining key for first time private boolean queue_up = true; private boolean queue_down = false; // queue to hold upcoming messages while key negotiation is happening private BlockingQueue upMessageQueue = new LinkedBlockingQueue(); // queue to hold downcoming messages while key negotiation is happening private BlockingQueue downMessageQueue = new LinkedBlockingQueue(); // decrypting cypher for secret key requests private Cipher asymCipher; /** determines whether to encrypt the entire message, or just the buffer */ private boolean encrypt_entire_message=false; public ENCRYPT() { } public String getName() { return "ENCRYPT"; } public void setObserver(Observer o) { observer=o; } /* * GetAlgorithm: Get the algorithm name from "algorithm/mode/padding" * taken m original ENCRYPT file */ private static String getAlgorithm(String s) { int index = s.indexOf("/"); if (index == -1) return s; return s.substring(0, index); } public boolean setProperties(Properties props) { String str; super.setProperties(props); // asymmetric key length str = props.getProperty("asym_init"); if (str != null) { asymInit = Integer.parseInt(str); props.remove("asym_init"); if (log.isInfoEnabled()) log.info("Asym algo bits used is " + asymInit); } // symmetric key length str = props.getProperty("sym_init"); if (str != null) { symInit = Integer.parseInt(str); props.remove("sym_init"); if (log.isInfoEnabled()) log.info("Sym algo bits used is " + symInit); } // asymmetric algorithm name str = props.getProperty("asym_algorithm"); if (str != null) { asymAlgorithm = str; props.remove("asym_algorithm"); if (log.isInfoEnabled()) log.info("Asym algo used is " + asymAlgorithm); } // symmetric algorithm name str = props.getProperty("sym_algorithm"); if (str != null) { symAlgorithm = str; props.remove("sym_algorithm"); if (log.isInfoEnabled()) log.info("Sym algo used is " + symAlgorithm); } // symmetric algorithm name str = props.getProperty("asym_provider"); if (str != null) { asymProvider = str; props.remove("asym_provider"); if (log.isInfoEnabled()) log.info("asymProvider used is " + asymProvider); } //symmetric algorithm name str = props.getProperty("key_store_name"); if (str != null) { keyStoreName = str; props.remove("key_store_name"); if (log.isInfoEnabled()) log.info("key_store_name used is " + keyStoreName); } // symmetric algorithm name str = props.getProperty("store_password"); if (str != null) { storePassword = str; props.remove("store_password"); if (log.isInfoEnabled()) log.info("store_password used is not null"); } // symmetric algorithm name str = props.getProperty("key_password"); if (str != null) { keyPassword = str; props.remove("key_password"); if (log.isInfoEnabled()) log.info("key_password used is not null"); } else if (storePassword != null) { keyPassword = storePassword; if (log.isInfoEnabled()) log.info("key_password used is same as store_password"); } // symmetric algorithm name str = props.getProperty("alias"); if (str != null) { alias = str; props.remove("alias"); if (log.isInfoEnabled()) log.info("alias used is " + alias); } str=props.getProperty("encrypt_entire_message"); if(str != null) { this.encrypt_entire_message=Boolean.valueOf(str).booleanValue(); props.remove("encrypt_entire_message"); } if (!props.isEmpty()) { if (log.isErrorEnabled()) log.error("these properties are not recognized:" + props); return false; } return true; } public void init() throws Exception { if (keyStoreName == null) { initSymKey(); initKeyPair(); } else { initConfiguredKey(); } initSymCiphers(symAlgorithm, getSecretKey()); } /** * Initialisation if a supplied key is defined in the properties. This * supplied key must be in a keystore which can be generated using the * keystoreGenerator file in demos. The keystore must be on the classpath * to find it. * * @throws KeyStoreException * @throws Exception * @throws IOException * @throws NoSuchAlgorithmException * @throws CertificateException * @throws UnrecoverableKeyException */ private void initConfiguredKey() throws Exception { InputStream inputStream = null; // must not use default keystore type - as does not support secret keys KeyStore store = KeyStore.getInstance("JCEKS"); SecretKey tempKey = null; try { // load in keystore using this thread's classloader inputStream = Thread.currentThread().getContextClassLoader() .getResourceAsStream(keyStoreName); // we can't find a keystore here - if (inputStream == null) { throw new Exception("Unable to load keystore " + keyStoreName + " ensure file is on classpath"); } // we have located a file lets load the keystore try{ store.load(inputStream, storePassword.toCharArray()); // loaded keystore - get the key tempKey = (SecretKey) store .getKey(alias, keyPassword.toCharArray()); } catch (IOException e){ throw new Exception("Unable to load keystore "+ keyStoreName + ": " + e); }catch (NoSuchAlgorithmException e){ throw new Exception("No Such algorithm "+ keyStoreName + ": " + e); }catch(CertificateException e){ throw new Exception("Certificate exception "+ keyStoreName + ": " + e); } if (tempKey == null) { throw new Exception("Unable to retrieve key '" + alias + "' from keystore " + keyStoreName); } //set the key here setSecretKey(tempKey); if (symAlgorithm.equals(DEFAULT_SYM_ALGO)) { symAlgorithm = tempKey.getAlgorithm(); } // set the fact we are using a supplied key suppliedKey = true; queue_down =false; queue_up =false; } finally { Util.close(inputStream); } } /** * Used to initialise the symmetric key if none is supplied in a keystore. * @throws Exception */ public void initSymKey() throws Exception { KeyGenerator keyGen = null; // see if we have a provider specified if (symProvider != null && symProvider.trim().length() > 0) { keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm), symProvider); } else { keyGen = KeyGenerator.getInstance(getAlgorithm(symAlgorithm)); } // generate the key using the defined init properties keyGen.init(symInit); secretKey = keyGen.generateKey(); setSecretKey(secretKey); if (log.isInfoEnabled()) log.info(" Symmetric key generated "); } /** * Initialises the Ciphers for both encryption and decryption using the * generated or supplied secret key. * * @param algorithm * @param secret * @throws Exception */ private void initSymCiphers(String algorithm, SecretKey secret) throws Exception { if (log.isInfoEnabled()) log.info(" Initializing symmetric ciphers"); symEncodingCipher = Cipher.getInstance(algorithm); symDecodingCipher = Cipher.getInstance(algorithm); symEncodingCipher.init(Cipher.ENCRYPT_MODE, secret); symDecodingCipher.init(Cipher.DECRYPT_MODE, secret); //set the version MessageDigest digest = MessageDigest.getInstance("MD5"); digest.reset(); digest.update(secret.getEncoded()); symVersion = new String(digest.digest(), "UTF-8"); if (log.isInfoEnabled()) { // log.info(" Initialized symmetric ciphers with secret key (" + symVersion.length() + " bytes) " +symVersion); StringBuilder sb=new StringBuilder(" Initialized symmetric ciphers with secret key (" + symVersion.length() + " bytes) "); char[] arr=symVersion.toCharArray(); for(int i=0; i < arr.length; i++) { char c=arr[i]; sb.append((int)c); } log.info(sb.toString()); } } /** * Generates the public/private key pair from the init params * @throws Exception */ public void initKeyPair() throws Exception { // generate keys according to the specified algorithms // generate publicKey and Private Key KeyPairGenerator KpairGen = null; if (asymProvider != null && asymProvider.trim().length() > 0) { KpairGen = KeyPairGenerator.getInstance( getAlgorithm(asymAlgorithm), asymProvider); } else { KpairGen = KeyPairGenerator .getInstance(getAlgorithm(asymAlgorithm)); } KpairGen.initialize(asymInit, new SecureRandom()); Kpair = KpairGen.generateKeyPair(); // set up the Cipher to decrypt secret key responses encrypted with our key asymCipher = Cipher.getInstance(asymAlgorithm); asymCipher.init(Cipher.DECRYPT_MODE,Kpair.getPrivate()); if (log.isInfoEnabled()) log.info(" asym algo initialized"); } /** Just remove if you don't need to reset any state */ public void reset() { } /* (non-Javadoc) * @see org.jgroups.stack.Protocol#up(org.jgroups.Event) */ public Object up(Event evt) { switch (evt.getType()) { // we need to know what our address is case Event.SET_LOCAL_ADDRESS : local_addr = (Address) evt.getArg(); if (log.isDebugEnabled()) log.debug("set local address to " + local_addr); break; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); if (log.isInfoEnabled()) log.info("handling view-change up: " + view); if (!suppliedKey){ handleViewChange(view, false); } break; case Event.TMP_VIEW: view=(View)evt.getArg(); if (log.isInfoEnabled()) log.info("handling tmp-view up: " + view); if (!suppliedKey){ // if a tmp_view then we are trying to become coordinator so // make us keyserver handleViewChange(view, true); } break; // we try and decrypt all messages case Event.MSG : try { handleUpMessage(evt); } catch (Exception e) { log.warn("exception occurred decrypting message", e); } return null; default : break; } return passItUp(evt); } public Object passItUp(Event evt) { if(observer != null) observer.passUp(evt); return up_prot != null? up_prot.up(evt) : null; } private synchronized void handleViewChange(View view, boolean makeServer){ // if view is a bit broken set me as keyserver if (view.getMembers() == null || view.getMembers().get(0) == null){ becomeKeyServer(local_addr); return; } // otherwise get keyserver from view controller Address tmpKeyServer = view.getMembers().get(0); //I am new keyserver - either first member of group or old key server is no more and // I have been voted new controller if (makeServer || (tmpKeyServer.equals(local_addr) && (keyServerAddr == null || (!tmpKeyServer.equals(keyServerAddr))))) { becomeKeyServer(tmpKeyServer); // a new keyserver has been set and it is not me }else if (keyServerAddr == null || (! tmpKeyServer.equals(keyServerAddr))){ handleNewKeyServer(tmpKeyServer); } else{ if (log.isDebugEnabled()) log.debug("Membership has changed but I do not care"); } } /** * Handles becoming server - resetting queue settings * and setting keyserver address to be local address. * * @param tmpKeyServer */ private void becomeKeyServer(Address tmpKeyServer){ keyServerAddr = tmpKeyServer; keyServer =true; if (log.isInfoEnabled()) log.info("I have become key server " + keyServerAddr); queue_down = false; queue_up = false; } /** * Sets up the peer for a new keyserver - this is * setting queueing to buffer messages until we have a new * secret key from the key server and sending a key request * to the new keyserver. * * @param newKeyServer */ private void handleNewKeyServer(Address newKeyServer){ // start queueing until we have new key // to make sure we are not sending with old key queue_up =true; queue_down = true; // set new keyserver address keyServerAddr = newKeyServer; keyServer =false; if (log.isInfoEnabled()) log.info("Sending key request"); // create a key request message sendKeyRequest(); } /** * @param evt */ private void handleUpMessage(Event evt) throws Exception { Message msg = (Message) evt.getArg(); if (msg == null) { if(log.isTraceEnabled()) log.trace("null message - passing straight up"); passItUp(evt); return; } if(msg.getLength() == 0) { passItUp(evt); return; } EncryptHeader hdr = (EncryptHeader)msg.getHeader(EncryptHeader.KEY); // try and get the encryption header if (hdr == null) { if (log.isTraceEnabled()) log.trace("dropping message as ENCRYPT header is null or has not been recognized, msg will not be passed up, " + "headers are " + msg.printHeaders()); return; } if(log.isTraceEnabled()) log.trace("header received " + hdr); // if a normal message try and decrypt it if (hdr.getType() == EncryptHeader.ENCRYPT) { // if msg buffer is empty, and we didn't encrypt the entire message, just pass up if (!hdr.encrypt_entire_msg && ((Message)evt.getArg()).getLength() == 0) { if(log.isTraceEnabled()) log.trace("passing up message as it has an empty buffer "); passItUp(evt); return; } // if queueing then pass into queue to be dealt with later if (queue_up){ if(log.isTraceEnabled()) log.trace("queueing up message as no session key established: " + evt.getArg()); upMessageQueue.put(evt); } else{ // make sure we pass up any queued messages first // could be more optimised but this can wait // we only need this if not using supplied key if (!suppliedKey) { drainUpQueue(); } // try and decrypt the message - we need to copy msg as we modify its // buffer (http://jira.jboss.com/jira/browse/JGRP-538) Message tmpMsg=decryptMessage(symDecodingCipher, msg.copy()); if (tmpMsg != null){ if(log.isTraceEnabled()) log.trace("decrypted message " + tmpMsg); passItUp(new Event(Event.MSG, tmpMsg)); } else { log.warn("Unrecognised cipher discarding message"); } } } else { // check if we had some sort of encrypt control // header if using supplied key we should not // process it if (suppliedKey) { if (log.isWarnEnabled()) { log.warn("We received an encrypt header of " + hdr.getType() + " while in configured mode"); } } else{ // see what sort of encrypt control message we // have received switch (hdr.getType()){ // if a key request case EncryptHeader.KEY_REQUEST: if (log.isInfoEnabled()) { log.info("received a key request from peer"); } //if a key request send response key back try { // extract peer's public key PublicKey tmpKey = generatePubKey(msg.getBuffer()); // send back the secret key we have sendSecretKey(getSecretKey(), tmpKey, msg.getSrc()); } catch (Exception e){ log.warn("unable to reconstitute peer's public key"); } break; case EncryptHeader.SECRETKEY: if (log.isInfoEnabled()) { log.info("received a secretkey response from keyserver"); } try { SecretKey tmp = decodeKey(msg.getBuffer()); if (tmp == null) { // unable to understand response // lets try again sendKeyRequest(); }else{ // otherwise lets set the reurned key // as the shared key setKeys(tmp, hdr.getVersion()); if (log.isInfoEnabled()) { log.info("Decoded secretkey response"); } } } catch (Exception e){ log.warn("unable to process received public key"); } break; default: log.warn("Received ignored encrypt header of "+hdr.getType()); break; } } } } /** * used to drain the up queue - synchronized so we * can call it safely despite access from potentially two threads at once * @throws QueueClosedException * @throws Exception */ private void drainUpQueue() throws Exception { //we do not synchronize here as we only have one up thread so we should never get an issue //synchronized(upLock){ Event tmp =null; while ((tmp = (Event)upMessageQueue.poll(0L, TimeUnit.MILLISECONDS)) != null){ Message msg = decryptMessage(symDecodingCipher, ((Message)tmp.getArg()).copy()); if (msg != null){ if(log.isTraceEnabled()){ log.trace("passing up message from drain " + msg); } passItUp(new Event(Event.MSG, msg)); } else{ log.warn("discarding message in queue up drain as cannot decode it"); } } } /** * Sets the keys for the app. and drains the queues - the drains could * be called att he same time as the up/down messages calling in to * the class so we may have an extra call to the drain methods but this slight expense * is better than the alternative of waiting until the next message to * trigger the drains which may never happen. * * @param key * @param version * @throws Exception */ private void setKeys(SecretKey key, String version) throws Exception{ // put the previous key into the map // if the keys are already there then they will overwrite keyMap.put(getSymVersion(), getSymDecodingCipher()); setSecretKey(key); initSymCiphers(key.getAlgorithm(),key ); setSymVersion(version); // drain the up queue log.info("setting queue up to false in setKeys"); queue_up =false; drainUpQueue(); queue_down =false; drainDownQueue(); } /** * Does the actual work for decrypting - if version does not match current cipher * then tries to use previous cipher * @param cipher * @param msg * @return * @throws Exception */ private Message decryptMessage(Cipher cipher, Message msg) throws Exception { EncryptHeader hdr = (EncryptHeader)msg.getHeader(EncryptHeader.KEY); if (!hdr.getVersion().equals(getSymVersion())){ log.warn("attempting to use stored cipher as message does not uses current encryption version "); cipher = (Cipher)keyMap.get(hdr.getVersion()); if (cipher == null) { log.warn("Unable to find a matching cipher in previous key map"); return null; } else{ if(log.isTraceEnabled()) log.trace("decrypting using previous cipher version "+ hdr.getVersion()); return _decrypt(cipher, msg, hdr.encrypt_entire_msg); } } else { // reset buffer with decrypted message return _decrypt(cipher, msg, hdr.encrypt_entire_msg); } } private static Message _decrypt(Cipher cipher, Message msg, boolean decrypt_entire_msg) throws Exception { if(!decrypt_entire_msg) { msg.setBuffer(cipher.doFinal(msg.getRawBuffer(), msg.getOffset(), msg.getLength())); return msg; } byte[] decrypted_msg=cipher.doFinal(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); Message ret=(Message)Util.streamableFromByteBuffer(Message.class, decrypted_msg); if(ret.getDest() == null) ret.setDest(msg.getDest()); if(ret.getSrc() == null) ret.setSrc(msg.getSrc()); return ret; } /** * @param secret * @param pubKey * @throws InvalidKeyException * @throws IllegalStateException * @throws IllegalBlockSizeException * @throws BadPaddingException */ private void sendSecretKey(SecretKey secret, PublicKey pubKey, Address source) throws InvalidKeyException, IllegalStateException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException { Message newMsg; if (log.isDebugEnabled()) log.debug("encoding shared key "); // create a cipher with peer's public key Cipher tmp = Cipher.getInstance(asymAlgorithm); tmp.init(Cipher.ENCRYPT_MODE, pubKey); //encrypt current secret key byte[] encryptedKey = tmp.doFinal(secret.getEncoded()); //SW logout encrypted bytes we are sending so we // can match the clients log to see if they match if (log.isDebugEnabled()) log.debug(" Generated encoded key which only client can decode:" + formatArray(encryptedKey)); newMsg = new Message(source, local_addr, encryptedKey); newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader( EncryptHeader.SECRETKEY, getSymVersion())); if (log.isDebugEnabled()) log.debug(" Sending version " + getSymVersion() + " encoded key to client"); passItDown(new Event(Event.MSG, newMsg)); } /** * @param msg * @return */ // private PublicKey handleKeyRequest(Message msg) // { // Message newMsg; // if (log.isDebugEnabled()) // log.debug("Request for key recieved"); // // //SW log the clients encoded public key so we can // // see if they match // if (log.isDebugEnabled()) // log.debug("Got peer's encoded public key:" // + formatArray(msg.getBuffer())); // // PublicKey pubKey = generatePubKey(msg.getBuffer()); // // //SW log the clients resulting public key so we can // // see if it is created correctly // if (log.isDebugEnabled()) // log.debug("Generated requestors public key" + pubKey); // // /* // * SW why do we send this as the client does not use it ? - although we // * could make use to provide some authentication later on rahter than // * just encryption send server's publicKey // */ // newMsg = new Message(msg.getSrc(), local_addr, Kpair.getPublic() // .getEncoded()); // // //SW Log out our public key in encoded format so we // // can match with the client debugging to // // see if they match // if (log.isInfoEnabled()) // log.debug("encoded key is " // + formatArray(Kpair.getPublic().getEncoded())); // // // newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader( // EncryptHeader.SERVER_PUBKEY, getSymVersion())); // // // down_prot.down(new Event(Event.MSG, newMsg)); // return pubKey; // } /** * @return Message */ private Message sendKeyRequest() { // send client's public key to server and request // server's public key Message newMsg = new Message(keyServerAddr, local_addr, Kpair.getPublic() .getEncoded()); newMsg.putHeader(EncryptHeader.KEY, new EncryptHeader( EncryptHeader.KEY_REQUEST, getSymVersion())); passItDown(new Event(Event.MSG, newMsg)); return newMsg; } /* (non-Javadoc) * @see org.jgroups.stack.Protocol#down(org.jgroups.Event) */ public Object down(Event evt) { if(observer != null) observer.down(evt); switch (evt.getType()) { case Event.MSG : try { if (queue_down){ if(log.isTraceEnabled()) log.trace("queueing down message as no session key established" + evt.getArg()); downMessageQueue.put(evt); // queue messages if we are waiting for a new key } else { // make sure the down queue is drained first to keep ordering if (!suppliedKey){ drainDownQueue(); } sendDown(evt); } } catch (Exception e) { log.warn("unable to send down event " + e); } return null; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); if (log.isInfoEnabled()) log.info("handling view-change down: " + view); if (!suppliedKey){ handleViewChange(view, false); } break; case Event.TMP_VIEW: view=(View)evt.getArg(); if (log.isInfoEnabled()) log.info("handling tmp-view down: " + view); if (!suppliedKey){ // if a tmp_view then we are trying to become coordinator so // make us keyserver handleViewChange(view, true); } break; default : break; } return down_prot.down(evt); } public Object passItDown(Event evt) { if(observer != null) observer.passDown(evt); return down_prot != null? down_prot.down(evt) : null; } /** * @throws Exception * @throws QueueClosedException */ private void drainDownQueue() throws Exception { // we do not synchronize here as we only have one down thread so we should never get an issue // first lets replay any oustanding events Event tmp =null; while((tmp = (Event)downMessageQueue.poll(0L, TimeUnit.MILLISECONDS) )!= null){ sendDown(tmp); } } /** * @param evt * @throws Exception */ private void sendDown(Event evt) throws Exception { if (evt.getType() != Event.MSG) { return; } Message msg = (Message) evt.getArg(); if(msg.getLength() == 0) { passItDown(evt); return; } EncryptHeader hdr=new EncryptHeader(EncryptHeader.ENCRYPT, getSymVersion()); hdr.encrypt_entire_msg=this.encrypt_entire_message; if(encrypt_entire_message) { byte[] serialized_msg=Util.streamableToByteBuffer(msg); byte[] encrypted_msg=encryptMessage(symEncodingCipher, serialized_msg, 0, serialized_msg.length); Message tmp=msg.copy(false); // we need to preserve headers which may already be present tmp.setBuffer(encrypted_msg); tmp.setSrc(local_addr); tmp.putHeader(EncryptHeader.KEY, hdr); passItDown(new Event(Event.MSG, tmp)); return; } // put our encrypt header on the message msg.putHeader(EncryptHeader.KEY, hdr); // copy neeeded because same message (object) may be retransmitted -> no double encryption Message msgEncrypted = msg.copy(false); msgEncrypted.setBuffer(encryptMessage(symEncodingCipher, msg.getRawBuffer(), msg.getOffset(), msg.getLength())); passItDown(new Event(Event.MSG, msgEncrypted)); } /** * * @param cipher * @param plain * @return * @throws Exception */ private static byte[] encryptMessage(Cipher cipher, byte[] plain, int offset, int length) throws Exception { return cipher.doFinal(plain, offset, length); } private SecretKeySpec decodeKey(byte[] encodedKey) throws Exception { // try and decode secrey key sent from keyserver byte[] keyBytes = asymCipher.doFinal(encodedKey); SecretKeySpec keySpec = null; try { keySpec = new SecretKeySpec(keyBytes, getAlgorithm(symAlgorithm)); // test reconstituted key to see if valid Cipher temp = Cipher.getInstance(symAlgorithm); temp.init(Cipher.SECRET_KEY, keySpec); } catch (Exception e) { log.fatal(e); keySpec = null; } return keySpec; } /** * used to reconstitute public key sent in byte form from peer * @param encodedKey * @return PublicKey */ private PublicKey generatePubKey(byte[] encodedKey) { PublicKey pubKey = null; try { KeyFactory KeyFac = KeyFactory.getInstance(getAlgorithm(asymAlgorithm)); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(encodedKey); pubKey = KeyFac.generatePublic(x509KeySpec); } catch (Exception e) { e.printStackTrace(); } return pubKey; } /* * simple helper method so we can see the format of the byte arrays in a * readable form could be better to use Base64 but will do for now */ private static String formatArray(byte[] array) { StringBuilder buf=new StringBuilder(); for (int i = 0; i < array.length; i++) { buf.append(Integer.toHexString(array[i])); } return buf.toString(); } /** * @return Returns the asymInit. */ protected int getAsymInit() { return asymInit; } /** * @return Returns the asymProvider. */ protected String getAsymProvider() { return asymProvider; } /** * @return Returns the desKey. */ protected SecretKey getDesKey() { return secretKey; } /** * @return Returns the kpair. */ protected KeyPair getKpair() { return Kpair; } /** * @return Returns the asymCipher. */ protected Cipher getAsymCipher() { return asymCipher; } /** * @return Returns the serverPubKey. */ protected PublicKey getServerPubKey() { return serverPubKey; } /** * @return Returns the symAlgorithm. */ protected String getSymAlgorithm() { return symAlgorithm; } /** * @return Returns the symInit. */ protected int getSymInit() { return symInit; } /** * @return Returns the symProvider. */ protected static String getSymProvider() { return symProvider; } /** * @return Returns the asymAlgorithm. */ protected String getAsymAlgorithm() { return asymAlgorithm; } /** * @return Returns the symVersion. */ private String getSymVersion() { return symVersion; } /** * @param symVersion * The symVersion to set. */ private void setSymVersion(String symVersion) { this.symVersion = symVersion; } /** * @return Returns the secretKey. */ private SecretKey getSecretKey() { return secretKey; } /** * @param secretKey The secretKey to set. */ private void setSecretKey(SecretKey secretKey) { this.secretKey = secretKey; } /** * @return Returns the keyStoreName. */ protected String getKeyStoreName() { return keyStoreName; } /** * @return Returns the symDecodingCipher. */ protected Cipher getSymDecodingCipher() { return symDecodingCipher; } /** * @return Returns the symEncodingCipher. */ protected Cipher getSymEncodingCipher() { return symEncodingCipher; } /** * @return Returns the local_addr. */ protected Address getLocal_addr() { return local_addr; } /** * @param local_addr The local_addr to set. */ protected void setLocal_addr(Address local_addr) { this.local_addr = local_addr; } /** * @return Returns the keyServerAddr. */ protected Address getKeyServerAddr() { return keyServerAddr; } /** * @param keyServerAddr The keyServerAddr to set. */ protected void setKeyServerAddr(Address keyServerAddr) { this.keyServerAddr = keyServerAddr; } public static class EncryptHeader extends org.jgroups.Header implements Streamable { short type; public static final short ENCRYPT = 0; public static final short KEY_REQUEST = 1; public static final short SERVER_PUBKEY = 2; public static final short SECRETKEY = 3; public static final short SECRETKEY_READY = 4; // adding key for Message object purpose static final String KEY = "encrypt"; String version; boolean encrypt_entire_msg=false; public EncryptHeader() { } public EncryptHeader(short type) { //this(type, 0l); this.type = type; this.version = ""; } public EncryptHeader(short type, String version) { this.type = type; this.version = version; } public void writeExternal(java.io.ObjectOutput out) throws IOException { out.writeShort(type); out.writeObject(version); } public void readExternal(java.io.ObjectInput in) throws IOException, ClassNotFoundException { type = in.readShort(); version = (String)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeShort(type); Util.writeString(version, out); out.writeBoolean(encrypt_entire_msg); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readShort(); version=Util.readString(in); encrypt_entire_msg=in.readBoolean(); } public String toString() { return "ENCRYPT [type=" + type + " version=\"" + version + "\"]"; } public int size() { int retval=Global.SHORT_SIZE + Global.BYTE_SIZE + Global.BYTE_SIZE; if(version != null) retval+=version.length() +2; return retval; } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { return obj instanceof EncryptHeader && ((((EncryptHeader)obj).getType() == type) && ((((EncryptHeader)obj) .getVersion().equals(version)))); } /** * @return Returns the type. */ protected short getType() { return type; } /** * @return Returns the version. */ protected String getVersion() { return version; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/PingRsp.java0000644000175000017500000000534711366547366025667 0ustar twernertwerner// $Id: PingRsp.java,v 1.10.4.3 2010/04/01 11:37:32 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; public class PingRsp implements Serializable, Streamable { public Address own_addr=null; public Address coord_addr=null; public boolean is_server=false; private static final long serialVersionUID=3634334590904551586L; public PingRsp() { // externalization } public PingRsp(Address own_addr, Address coord_addr, boolean is_server) { this.own_addr=own_addr; this.coord_addr=coord_addr; this.is_server=is_server; } public boolean equals(Object obj) { if(!(obj instanceof PingRsp)) return false; PingRsp other=(PingRsp)obj; return own_addr != null && other.own_addr != null && own_addr.equals(other.own_addr); } public int hashCode() { int retval=0; if(own_addr != null) retval+=own_addr.hashCode(); if(retval == 0) retval=super.hashCode(); return retval; } public boolean isCoord() { return is_server && own_addr != null && coord_addr != null && own_addr.equals(coord_addr); } public int size() { int retval=Global.BYTE_SIZE *3; // for is_server, plus 2 presence bytes if(own_addr != null) { retval+=Global.BYTE_SIZE; // 1 boolean for: IpAddress or other address ? retval+=own_addr.size(); } if(coord_addr != null) { retval+=Global.BYTE_SIZE; // 1 boolean for: IpAddress or other address ? retval+=coord_addr.size(); } return retval; } public Address getAddress() { return own_addr; } public Address getCoordAddress() { return coord_addr; } public boolean isServer() { return is_server; } public String toString() { return new StringBuilder("[own_addr=").append(own_addr).append(", coord_addr=").append(coord_addr). append(", is_server=").append(is_server).append(']').toString(); } public void writeTo(DataOutputStream outstream) throws IOException { Util.writeAddress(own_addr, outstream); Util.writeAddress(coord_addr, outstream); outstream.writeBoolean(is_server); } public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { own_addr=Util.readAddress(instream); coord_addr=Util.readAddress(instream); is_server=instream.readBoolean(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/AUTOCONF.java0000644000175000017500000001604411366547366025517 0ustar twernertwerner// $Id: AUTOCONF.java,v 1.20 2007/08/27 08:09:19 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.stack.Protocol; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * Senses the network configuration when it is initialized (in init()) and sends a CONFIG event up * and down the stack. The CONFIG event contains a hashmap, with strings as keys (e.g. "frag_size") * and Objects as values. Certain protocols can set some of their properties when receiving the CONFIG * event. *

    * This protocol should be placed above the transport protocol (e.g. UDP). It is not needed for TCP. *

    * Example: senses the network send and receive buffers, plus the max size of a message to be sent and * generates a CONFIG event containing "frag_size", "send_buf_size" and "receive_buf_size" keys. * * @author Bela Ban */ public class AUTOCONF extends Protocol { final Map config=new HashMap(); static int num_iterations=10; // to find optimal frag_size /** Number of bytes to subtract from computed fragmentation size, due to (a) headers and * (b) serialization overhead */ static int frag_overhead=1000; public String getName() { return "AUTOCONF"; } public void init() throws Exception { senseNetworkConfiguration(); if(log.isDebugEnabled()) log.debug("configuration is\n" + config); } public void start() throws Exception { if(config != null && config.size() > 0) { Event config_evt=new Event(Event.CONFIG, config); down_prot.down(config_evt); up_prot.up(config_evt); } } /** * Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("num_iterations"); if(str != null) { num_iterations=Integer.parseInt(str); props.remove("num_iterations"); } str=props.getProperty("frag_overhead"); if(str != null) { frag_overhead=Integer.parseInt(str); props.remove("frag_overhead"); } if(props.size() > 0) { log.error("AUTOCONF.setProperties(): the following properties are not recognized: " + props); return false; } return true; } /* -------------------------------------- Private metods ------------------------------------------- */ void senseNetworkConfiguration() { int max_frag_size=senseMaxFragSize(); if(max_frag_size <= 0) { if(log.isErrorEnabled()) log.error("max_frag_size is invalid: " + max_frag_size); } else config.put("frag_size", new Integer(max_frag_size)); senseMaxSendBufferSize(config); senseMaxReceiveBufferSize(config); } public static int senseMaxFragSizeStatic() { return new AUTOCONF().senseMaxFragSize(); } /** * Tries to find out the max number of bytes in a DatagramPacket we can send by sending increasingly * larger packets, until there is an exception (e.g., java.io.IOException: message too long). */ public int senseMaxFragSize() { int max_send=32000; int upper; int lower=0; int highest_failed=-1; DatagramSocket sock; byte[] buf; DatagramPacket packet; InetAddress local_addr; try { sock=new DatagramSocket(); local_addr=InetAddress.getLocalHost(); } catch(Exception ex) { if(log.isWarnEnabled()) log.warn("failed creating DatagramSocket: " + ex); return 0; } try { upper=max_send; for(int i=0; i < num_iterations && lower < upper; i++) { // iterations to approximate frag_size try { buf=new byte[upper]; // System.out.println("** upper=" + upper + " (lower=" + lower + ")"); packet=new DatagramPacket(buf, buf.length, local_addr, 9); sock.send(packet); lower=Math.max(lower, upper); upper=upper * 2; if(highest_failed > -1) upper=Math.min(highest_failed, upper); } catch(IOException io_ex) { if(highest_failed > -1) highest_failed=Math.min(highest_failed, upper); // never exceed max_upper else highest_failed=upper; upper=(upper + lower) / 2; } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("exception=" + ex); break; } } /** Reduce the frag_size a bit to prevent packets that are too large (see bug #854887) */ lower-=frag_overhead; if(log.isDebugEnabled()) log.debug("frag_size=" + lower); return lower; } finally { if(sock != null) sock.close(); } } void senseMaxSendBufferSize(Map map) { DatagramSocket sock; int max_size=4096, retval=max_size; if(map != null && map.containsKey("frag_size)")) max_size=((Integer)map.get("frag_size")).intValue(); try { sock=new DatagramSocket(); while(max_size < 1000000) { sock.setSendBufferSize(max_size); if((retval=sock.getSendBufferSize()) < max_size) return; max_size*=2; } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed getting the max send buffer size: " + ex + ". Defaulting to " + retval); } finally { map.put("send_buf_size", new Integer(retval)); } } void senseMaxReceiveBufferSize(Map map) { DatagramSocket sock; int max_size=4096, retval=max_size; try { sock=new DatagramSocket(); while(max_size < 1000000) { sock.setReceiveBufferSize(max_size); if((retval=sock.getReceiveBufferSize()) < max_size) return; max_size*=2; } } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed getting the max send buffer size: " + ex + ". Defaulting to " + retval); } finally { map.put("recv_buf_size", new Integer(retval)); } } /* ----------------------------------- End of Private metods --------------------------------------- */ public static void main(String[] args) { int frag_size=new AUTOCONF().senseMaxFragSize(); System.out.println("frag_size: " + frag_size); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FD_SOCK.java0000644000175000017500000013403211366547366025407 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.*; import org.jgroups.util.ThreadFactory; import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.*; /** * Failure detection protocol based on sockets. Failure detection is ring-based. Each member creates a * server socket and announces its address together with the server socket's address in a multicast. A * pinger thread will be started when the membership goes above 1 and will be stopped when it drops below * 2. The pinger thread connects to its neighbor on the right and waits until the socket is closed. When * the socket is closed by the monitored peer in an abnormal fashion (IOException), the neighbor will be * suspected.

    The main feature of this protocol is that no ping messages need to be exchanged between * any 2 peers, and failure detection relies entirely on TCP sockets. The advantage is that no activity * will take place between 2 peers as long as they are alive (i.e. have their server sockets open). * The disadvantage is that hung servers or crashed routers will not cause sockets to be closed, therefore * they won't be detected. * The FD_SOCK protocol will work for groups where members are on different hosts

    * The costs involved are 2 additional threads: one that * monitors the client side of the socket connection (to monitor a peer) and another one that manages the * server socket. However, those threads will be idle as long as both peers are running. * @author Bela Ban May 29 2001 * @version $Id: FD_SOCK.java,v 1.83.2.9 2009/09/29 04:36:30 belaban Exp $ */ public class FD_SOCK extends Protocol implements Runnable { long get_cache_timeout=1000; // msecs to wait for the socket cache from the coordinator long suspect_msg_interval=5000; // (BroadcastTask): mcast SUSPECT every 5000 msecs int num_tries=3; // attempts coord is solicited for socket cache until we give up final Vector

    members=new Vector
    (11); // list of group members (updated on VIEW_CHANGE) boolean srv_sock_sent=false; // has own socket been broadcast yet ? /** Used to rendezvous on GET_CACHE and GET_CACHE_RSP */ final Promise> get_cache_promise=new Promise>(); boolean got_cache_from_coord=false; // was cache already fetched ? Address local_addr=null; // our own address ServerSocket srv_sock=null; // server socket to which another member connects to monitor me InetAddress bind_addr=null; // the NIC on which the ServerSocket should listen private ServerSocketHandler srv_sock_handler=null; // accepts new connections on srv_sock IpAddress srv_sock_addr=null; // pair of server_socket:port Address ping_dest=null; // address of the member we monitor Socket ping_sock=null; // socket to the member we monitor InputStream ping_input=null; // input stream of the socket to the member we monitor @GuardedBy("this") volatile Thread pinger_thread=null; // listens on ping_sock, suspects member if socket is closed /** Cache of member addresses and their ServerSocket addresses */ final ConcurrentMap cache=new ConcurrentHashMap(11); /** Start port for server socket (uses first available port starting at start_port). A value of 0 (default) * picks a random port */ int start_port=0; final Promise ping_addr_promise=new Promise(); // to fetch the ping_addr for ping_dest final Object sock_mutex=new Object(); // for access to ping_sock, ping_input TimeScheduler timer=null; private final BroadcastTask bcast_task=new BroadcastTask(); // to transmit SUSPECT message (until view change) boolean regular_sock_close=false; // used by interruptPingerThread() when new ping_dest is computed int num_suspect_events=0; private static final int INTERRUPT =8; private static final int NORMAL_TERMINATION=9; private static final int ABNORMAL_TERMINATION=-1; private static final String name="FD_SOCK"; final BoundedList
    suspect_history=new BoundedList
    (20); /** whether to use KEEP_ALIVE on the ping socket or not */ private boolean keep_alive=true; private int sock_conn_timeout=3000; // max time in millis to wait for Socket.connect() to return private volatile boolean running=false; public String getName() { return name; } public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} public String getMembers() {return members != null? members.toString() : "null";} public String getPingableMembers() {return getMembers();} public String getPingDest() {return ping_dest != null? ping_dest.toString() : "null";} public int getNumSuspectEventsGenerated() {return num_suspect_events;} public String printSuspectHistory() { StringBuilder sb=new StringBuilder(); for(Address suspect: suspect_history) { sb.append(new Date()).append(": ").append(suspect).append("\n"); } return sb.toString(); } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("get_cache_timeout"); if(str != null) { get_cache_timeout=Long.parseLong(str); props.remove("get_cache_timeout"); } str=props.getProperty("suspect_msg_interval"); if(str != null) { suspect_msg_interval=Long.parseLong(str); props.remove("suspect_msg_interval"); } str=props.getProperty("num_tries"); if(str != null) { num_tries=Integer.parseInt(str); props.remove("num_tries"); } str=props.getProperty("start_port"); if(str != null) { start_port=Integer.parseInt(str); props.remove("start_port"); } str=props.getProperty("keep_alive"); if(str != null) { keep_alive=Boolean.parseBoolean(str); props.remove("keep_alive"); } str=props.getProperty("srv_sock_bind_addr"); if(str != null) { log.error("srv_sock_bind_addr is deprecated and will be ignored - use bind_addr instead"); props.remove("srv_sock_bind_addr"); } str=props.getProperty("sock_conn_timeout"); if(str != null) { sock_conn_timeout=Integer.parseInt(str); props.remove("sock_conn_timeout"); } try { bind_addr=Util.getBindAddress(props); } catch(UnknownHostException unknown) { log.fatal("failed getting bind_addr", unknown); return false; } catch(SocketException ex) { log.fatal("failed getting bind_addr", ex); return false; } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public String printCache() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: cache.entrySet()) { sb.append(entry.getKey()).append(" has server socket at ").append(entry.getValue()).append("\n"); } return sb.toString(); } public void init() throws Exception { srv_sock_handler=new ServerSocketHandler(); timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer is null"); } public void start() throws Exception { super.start(); startServerSocket(); running=true; } public void stop() { running=false; bcast_task.removeAll(); synchronized(this) { stopPingerThread(); } stopServerSocket(true); // graceful close } public void resetStats() { super.resetStats(); num_suspect_events=0; suspect_history.clear(); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address) evt.getArg(); break; case Event.MSG: Message msg=(Message) evt.getArg(); FdHeader hdr=(FdHeader)msg.getHeader(name); if(hdr == null) break; // message did not originate from FD_SOCK layer, just pass up switch(hdr.type) { case FdHeader.SUSPECT: if(hdr.mbrs != null) { if(log.isDebugEnabled()) log.debug("[SUSPECT] hdr=" + hdr); for(Address m: hdr.mbrs) { if(local_addr != null && m.equals(local_addr)) { if(log.isWarnEnabled()) log.warn("I was suspected by " + msg.getSrc() + "; ignoring the SUSPECT message"); continue; } up_prot.up(new Event(Event.SUSPECT, m)); down_prot.down(new Event(Event.SUSPECT, m)); } } else if(log.isWarnEnabled()) log.warn("[SUSPECT]: hdr.mbrs == null"); break; // If I have the sock for 'hdr.mbr', return it. Otherwise look it up in my cache and return it case FdHeader.WHO_HAS_SOCK: if(local_addr != null && local_addr.equals(msg.getSrc())) return null; // don't reply to WHO_HAS bcasts sent by me ! if(hdr.mbr == null) { if(log.isErrorEnabled()) log.error("hdr.mbr is null"); return null; } if(log.isTraceEnabled()) log.trace("who-has-sock " + hdr.mbr); // 1. Try my own address, maybe it's me whose socket is wanted if(local_addr != null && local_addr.equals(hdr.mbr) && srv_sock_addr != null) { sendIHaveSockMessage(msg.getSrc(), local_addr, srv_sock_addr); // unicast message to msg.getSrc() return null; } // 2. If I don't have it, maybe it is in the cache IpAddress addr=cache.get(hdr.mbr); if(addr != null) sendIHaveSockMessage(msg.getSrc(), hdr.mbr, addr); // ucast msg break; // Update the cache with the addr:sock_addr entry (if on the same host) case FdHeader.I_HAVE_SOCK: if(hdr.mbr == null || hdr.sock_addr == null) { if(log.isErrorEnabled()) log.error("[I_HAVE_SOCK]: hdr.mbr is null or hdr.sock_addr == null"); return null; } // if(!cache.containsKey(hdr.mbr)) cache.put(hdr.mbr, hdr.sock_addr); // update the cache if(log.isTraceEnabled()) log.trace("i-have-sock: " + hdr.mbr + " --> " + hdr.sock_addr + " (cache is " + cache + ')'); if(ping_dest != null && hdr.mbr.equals(ping_dest)) ping_addr_promise.setResult(hdr.sock_addr); break; // Return the cache to the sender of this message case FdHeader.GET_CACHE: Address sender=msg.getSrc(); // guaranteed to be non-null hdr=new FdHeader(FdHeader.GET_CACHE_RSP,new HashMap(cache)); msg=new Message(sender, null, null); msg.setFlag(Message.OOB); msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, msg)); break; case FdHeader.GET_CACHE_RSP: if(hdr.cachedAddrs == null) { if(log.isWarnEnabled()) log.warn("(GET_CACHE_RSP): cache is null"); return null; } get_cache_promise.setResult(hdr.cachedAddrs); break; } return null; case Event.CONFIG: if(bind_addr == null) { Map config=(Map)evt.getArg(); bind_addr=(InetAddress)config.get("bind_addr"); } break; } return up_prot.up(evt); // pass up to the layer above us } public Object down(Event evt) { switch(evt.getType()) { case Event.UNSUSPECT: bcast_task.removeSuspectedMember((Address)evt.getArg()); break; case Event.DISCONNECT: stopServerSocket(true); // graceful close break; case Event.SHUTDOWN: stopServerSocket(false); break; case Event.VIEW_CHANGE: View v=(View) evt.getArg(); final Vector
    new_mbrs=v.getMembers(); Runnable reshuffleSockets=new Runnable() { public void run() { synchronized(FD_SOCK.this) { members.removeAllElements(); members.addAll(new_mbrs); cache.keySet().retainAll(members); // remove all entries in 'cache' which are not in the new membership bcast_task.adjustSuspectedMembers(members); if(log.isDebugEnabled()) log.debug("VIEW_CHANGE received: " + members); if(members.size() > 1) { if(pinger_thread != null && pinger_thread.isAlive()) { Address tmp_ping_dest=determinePingDest(); if(ping_dest != null && tmp_ping_dest != null && !ping_dest.equals(tmp_ping_dest)) { interruptPingerThread(); // allows the thread to use the new socket } } else startPingerThread(); // only starts if not yet running } else { ping_dest=null; stopPingerThread(); } } } }; timer.execute(reshuffleSockets); break; default: return down_prot.down(evt); } return down_prot.down(evt); } /** * Runs as long as there are 2 members and more. Determines the member to be monitored and fetches its * server socket address (if n/a, sends a message to obtain it). The creates a client socket and listens on * it until the connection breaks. If it breaks, emits a SUSPECT message. It the connection is closed regularly, * nothing happens. In both cases, a new member to be monitored will be chosen and monitoring continues (unless * there are fewer than 2 members). */ public void run() { if(log.isTraceEnabled()) log.trace("pinger_thread started"); // 1. Broadcast my own addr:sock to all members so they can update their cache if(!srv_sock_sent) { if(srv_sock_addr != null) { sendIHaveSockMessage(null, // send to all members local_addr, srv_sock_addr); srv_sock_sent=true; } else if(log.isWarnEnabled()) log.warn("(VIEW_CHANGE): srv_sock_addr == null"); } // 2. Get the addr:pid cache from the coordinator (only if not already fetched) if(!got_cache_from_coord) { getCacheFromCoordinator(); got_cache_from_coord=true; } while(pinger_thread != null && Thread.currentThread().equals(pinger_thread) && running) { ping_dest=determinePingDest(); // gets the neighbor to our right if(log.isDebugEnabled()) log.debug("determinePingDest()=" + ping_dest); if(ping_dest == null) { break; } IpAddress ping_addr=fetchPingAddress(ping_dest); if(ping_addr == null) { if(!running) break; if(log.isErrorEnabled()) log.error("socket address for " + ping_dest + " could not be fetched, retrying"); Util.sleep(1000); continue; } if(!setupPingSocket(ping_addr)) { // covers use cases #7 and #8 in ManualTests.txt if(log.isDebugEnabled()) log.debug("could not create socket to " + ping_dest + "; suspecting " + ping_dest); broadcastSuspectMessage(ping_dest); continue; } if(log.isDebugEnabled()) log.debug("ping_dest=" + ping_dest + ", ping_sock=" + ping_sock + ", cache=" + cache); // at this point ping_input must be non-null, otherwise setupPingSocket() would have thrown an exception try { if(ping_input != null) { int c=ping_input.read(); switch(c) { case NORMAL_TERMINATION: if(log.isDebugEnabled()) log.debug("peer closed socket normally"); synchronized(this) { pinger_thread=null; } break; case ABNORMAL_TERMINATION: handleSocketClose(null); break; default: break; } } } catch(IOException ex) { // we got here when the peer closed the socket --> suspect peer and then continue handleSocketClose(ex); } catch(Throwable catch_all_the_rest) { log.error("exception", catch_all_the_rest); } } if(log.isDebugEnabled()) log.debug("pinger thread terminated"); synchronized(this) { pinger_thread=null; } } /* ----------------------------------- Private Methods -------------------------------------- */ void handleSocketClose(Exception ex) { teardownPingSocket(); // make sure we have no leftovers if(!regular_sock_close) { // only suspect if socket was not closed regularly (by interruptPingerThread()) if(log.isDebugEnabled()) log.debug("peer " + ping_dest + " closed socket (" + (ex != null ? ex.getClass().getName() : "eof") + ')'); broadcastSuspectMessage(ping_dest); } else { if(log.isDebugEnabled()) log.debug("socket to " + ping_dest + " was reset"); regular_sock_close=false; } } /** * Does *not* need to be synchronized on pinger_mutex because the caller (down()) already has the mutex acquired */ void startPingerThread() { running=true; if(pinger_thread == null) { ThreadFactory factory=getThreadFactory(); pinger_thread=factory.newThread(this, "FD_SOCK pinger"); pinger_thread.setDaemon(true); pinger_thread.start(); } } void stopPingerThread() { running=false; if(pinger_thread != null && pinger_thread.isAlive()) { regular_sock_close=true; pinger_thread=null; sendPingTermination(); // PATCH by Bruce Schuchardt (http://jira.jboss.com/jira/browse/JGRP-246) teardownPingSocket(); ping_addr_promise.setResult(null); get_cache_promise.setResult(null); } } // PATCH: send something so the connection handler can exit void sendPingTermination() { sendPingSignal(NORMAL_TERMINATION); } void sendPingInterrupt() { sendPingSignal(INTERRUPT); } void sendPingSignal(int signal) { synchronized(sock_mutex) { if(ping_sock != null) { try { OutputStream out=ping_sock.getOutputStream(); if(out != null) { out.write(signal); out.flush(); } } catch(Throwable t) { if(log.isTraceEnabled()) log.trace("problem sending signal " + signalToString(signal), t); } } } } /** * Interrupts the pinger thread. The Thread.interrupt() method doesn't seem to work under Linux with JDK 1.3.1 * (JDK 1.2.2 had no problems here), therefore we close the socket (setSoLinger has to be set !) if we are * running under Linux. This should be tested under Windows. (Solaris 8 and JDK 1.3.1 definitely works).

    * Oct 29 2001 (bela): completely removed Thread.interrupt(), but used socket close on all OSs. This makes this * code portable and we don't have to check for OSs.

    * Does *not* need to be synchronized on pinger_mutex because the caller (down()) already has the mutex acquired * @see {@link org.jgroups.tests.InterruptTest} to determine whether Thread.interrupt() works for InputStream.read(). */ void interruptPingerThread() { if(pinger_thread != null && pinger_thread.isAlive()) { regular_sock_close=true; sendPingInterrupt(); // PATCH by Bruce Schuchardt (http://jira.jboss.com/jira/browse/JGRP-246) teardownPingSocket(); // will wake up the pinger thread. less elegant than Thread.interrupt(), but does the job } } void startServerSocket() throws IOException { srv_sock=Util.createServerSocket(bind_addr, start_port); // grab a random unused port above 10000 srv_sock_addr=new IpAddress(bind_addr, srv_sock.getLocalPort()); if(srv_sock_handler != null) { srv_sock_handler.start(); // won't start if already running } } void stopServerSocket(boolean graceful) { if(srv_sock_handler != null) srv_sock_handler.stop(graceful); } /** * Creates a socket to dest, and assigns it to ping_sock. Also assigns ping_input */ boolean setupPingSocket(IpAddress dest) { synchronized(sock_mutex) { if(dest == null) { if(log.isErrorEnabled()) log.error("destination address is null"); return false; } try { SocketAddress destAddr=new InetSocketAddress(dest.getIpAddress(), dest.getPort()); ping_sock=new Socket(); ping_sock.setSoLinger(true, 1); ping_sock.setKeepAlive(keep_alive); ping_sock.connect(destAddr, sock_conn_timeout); ping_input=ping_sock.getInputStream(); return true; } catch(Throwable ex) { return false; } } } void teardownPingSocket() { synchronized(sock_mutex) { if(ping_sock != null) { try { ping_sock.shutdownInput(); ping_sock.close(); } catch(Exception ex) { } ping_sock=null; } Util.close(ping_input); ping_input=null; } } /** * Determines coordinator C. If C is null and we are the first member, return. Else loop: send GET_CACHE message * to coordinator and wait for GET_CACHE_RSP response. Loop until valid response has been received. */ void getCacheFromCoordinator() { Address coord; int attempts=num_tries; Message msg; FdHeader hdr; Map result; get_cache_promise.reset(); while(attempts > 0 && running) { if((coord=determineCoordinator()) != null) { if(coord.equals(local_addr)) { // we are the first member --> empty cache if(log.isDebugEnabled()) log.debug("first member; cache is empty"); return; } hdr=new FdHeader(FdHeader.GET_CACHE); msg=new Message(coord, null, null); msg.setFlag(Message.OOB); msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, msg)); result=get_cache_promise.getResult(get_cache_timeout); if(result != null) { cache.putAll(result); if(log.isTraceEnabled()) log.trace("got cache from " + coord + ": cache is " + cache); return; } else { if(log.isWarnEnabled()) log.warn("received null cache; retrying"); } } --attempts; } } /** * Sends a SUSPECT message to all group members. Only the coordinator (or the next member in line if the coord * itself is suspected) will react to this message by installing a new view. To overcome the unreliability * of the SUSPECT message (it may be lost because we are not above any retransmission layer), the following scheme * is used: after sending the SUSPECT message, it is also added to the broadcast task, which will periodically * re-send the SUSPECT until a view is received in which the suspected process is not a member anymore. The reason is * that - at one point - either the coordinator or another participant taking over for a crashed coordinator, will * react to the SUSPECT message and issue a new view, at which point the broadcast task stops. */ void broadcastSuspectMessage(Address suspected_mbr) { Message suspect_msg; FdHeader hdr; if(suspected_mbr == null) return; if(log.isTraceEnabled()) log.trace("suspecting " + suspected_mbr + " (own address is " + local_addr + ')'); // 1. Send a SUSPECT message right away; the broadcast task will take some time to send it (sleeps first) hdr=new FdHeader(FdHeader.SUSPECT); hdr.mbrs=new HashSet

    (1); hdr.mbrs.add(suspected_mbr); suspect_msg=new Message(); suspect_msg.setFlag(Message.OOB); suspect_msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, suspect_msg)); // 2. Add to broadcast task and start latter (if not yet running). The task will end when // suspected members are removed from the membership bcast_task.addSuspectedMember(suspected_mbr); if(stats) { num_suspect_events++; suspect_history.add(suspected_mbr); } } /** Sends or broadcasts a I_HAVE_SOCK response. If 'dst' is null, the reponse will be broadcast, otherwise it will be unicast back to the requester */ void sendIHaveSockMessage(Address dst, Address mbr, IpAddress addr) { Message msg=new Message(dst, null, null); msg.setFlag(Message.OOB); FdHeader hdr=new FdHeader(FdHeader.I_HAVE_SOCK); hdr.mbr=mbr; hdr.sock_addr=addr; msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, msg)); } /** Attempts to obtain the ping_addr first from the cache, then by unicasting q request to mbr, then by multicasting a request to all members. */ private IpAddress fetchPingAddress(Address mbr) { IpAddress ret; Message ping_addr_req; FdHeader hdr; if(mbr == null) { if(log.isErrorEnabled()) log.error("mbr == null"); return null; } // 1. Try to get the server socket address from the cache if((ret=cache.get(mbr)) != null) return ret; // 2. Try to get the server socket address from mbr ping_addr_promise.reset(); ping_addr_req=new Message(mbr, null, null); // unicast ping_addr_req.setFlag(Message.OOB); hdr=new FdHeader(FdHeader.WHO_HAS_SOCK); hdr.mbr=mbr; ping_addr_req.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, ping_addr_req)); if(!running) return null; ret=ping_addr_promise.getResult(500); if(ret != null) { return ret; } // 3. Try to get the server socket address from all members ping_addr_req=new Message(null); // multicast ping_addr_req.setFlag(Message.OOB); hdr=new FdHeader(FdHeader.WHO_HAS_SOCK); hdr.mbr=mbr; ping_addr_req.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, ping_addr_req)); ret=ping_addr_promise.getResult(500); return ret; } private synchronized Address determinePingDest() { Vector
    non_suspected_mbrs=new Vector
    (members); non_suspected_mbrs.removeAll(bcast_task.getSuspectedMembers()); return determineRightNeighbor(non_suspected_mbrs); } private synchronized Address determineRightNeighbor(Vector
    list) { Address result=null; if(list.size() > 1 && local_addr != null) { if(local_addr.equals(list.lastElement())) { result=list.firstElement(); } else { int myIndex=list.indexOf(local_addr); if(myIndex != -1){ result=list.get(myIndex + 1); } } } return result; } Address determineCoordinator() { return !members.isEmpty()? members.elementAt(0) : null; } static String signalToString(int signal) { switch(signal) { case NORMAL_TERMINATION: return "NORMAL_TERMINATION"; case ABNORMAL_TERMINATION: return "ABNORMAL_TERMINATION"; case INTERRUPT: return "INTERRUPT"; default: return "n/a"; } } /* ------------------------------- End of Private Methods ------------------------------------ */ public static class FdHeader extends Header implements Streamable { public static final byte SUSPECT=10; public static final byte WHO_HAS_SOCK=11; public static final byte I_HAVE_SOCK=12; public static final byte GET_CACHE=13; // sent by joining member to coordinator public static final byte GET_CACHE_RSP=14; // sent by coordinator to joining member in response to GET_CACHE byte type=SUSPECT; Address mbr=null; // set on WHO_HAS_SOCK (requested mbr), I_HAVE_SOCK IpAddress sock_addr; // set on I_HAVE_SOCK Map cachedAddrs=null; // set on GET_CACHE_RSP Set
    mbrs=null; // set on SUSPECT (list of suspected members) private static final long serialVersionUID=-7025890133989522764L; public FdHeader() { } // used for externalization public FdHeader(byte type) { this.type=type; } public FdHeader(byte type, Address mbr) { this.type=type; this.mbr=mbr; } public FdHeader(byte type, Address mbr, IpAddress sock_addr) { this.type=type; this.mbr=mbr; this.sock_addr = sock_addr ; } public FdHeader(byte type, Set
    mbrs) { this.type=type; this.mbrs=mbrs; } public FdHeader(byte type, Map cachedAddrs) { this.type=type; this.cachedAddrs=cachedAddrs; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(type2String(type)); if(mbr != null) sb.append(", mbr=").append(mbr); if(sock_addr != null) sb.append(", sock_addr=").append(sock_addr); if(cachedAddrs != null) sb.append(", cache=").append(cachedAddrs); if(mbrs != null) sb.append(", mbrs=").append(mbrs); return sb.toString(); } public static String type2String(byte type) { switch(type) { case SUSPECT: return "SUSPECT"; case WHO_HAS_SOCK: return "WHO_HAS_SOCK"; case I_HAVE_SOCK: return "I_HAVE_SOCK"; case GET_CACHE: return "GET_CACHE"; case GET_CACHE_RSP: return "GET_CACHE_RSP"; default: return "unknown type (" + type + ')'; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeObject(mbr); out.writeObject(sock_addr); out.writeObject(cachedAddrs); out.writeObject(mbrs); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); mbr=(Address)in.readObject(); sock_addr=(IpAddress)in.readObject(); cachedAddrs=(Map)in.readObject(); mbrs=(Set
    )in.readObject(); } public int size() { int retval=Global.BYTE_SIZE; // type retval+=Util.size(mbr); // use of Util.size(Address) with IpAddress overestimates size by one byte. // replace: retval+=Util.size(sock_addr); with the following: int ipaddr_size = 0 ; ipaddr_size += Global.BYTE_SIZE ; // presence byte if (sock_addr != null) ipaddr_size += sock_addr.size(); // IpAddress size retval += ipaddr_size ; retval+=Global.INT_SIZE; // cachedAddrs size Address key; IpAddress val; if(cachedAddrs != null) { for(Map.Entry entry: cachedAddrs.entrySet()) { if((key=entry.getKey()) != null) retval+=Util.size(key); retval+=Global.BYTE_SIZE; // presence for val if((val=entry.getValue()) != null) retval+=val.size(); } } retval+=Global.INT_SIZE; // mbrs size if(mbrs != null) { for(Address m: mbrs) { retval+=Util.size(m); } } return retval; } public void writeTo(DataOutputStream out) throws IOException { int size; out.writeByte(type); Util.writeAddress(mbr, out); Util.writeStreamable(sock_addr, out); size=cachedAddrs != null? cachedAddrs.size() : 0; out.writeInt(size); if(size > 0) { for(Map.Entry entry: cachedAddrs.entrySet()) { Address key=entry.getKey(); IpAddress val=entry.getValue(); Util.writeAddress(key, out); Util.writeStreamable(val, out); } } size=mbrs != null? mbrs.size() : 0; out.writeInt(size); if(size > 0) { for(Address address: mbrs) { Util.writeAddress(address, out); } } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { int size; type=in.readByte(); mbr=Util.readAddress(in); sock_addr=(IpAddress)Util.readStreamable(IpAddress.class, in); size=in.readInt(); if(size > 0) { if(cachedAddrs == null) cachedAddrs=new HashMap(size); for(int i=0; i < size; i++) { Address key=Util.readAddress(in); IpAddress val=(IpAddress)Util.readStreamable(IpAddress.class, in); cachedAddrs.put(key, val); } } size=in.readInt(); if(size > 0) { if(mbrs == null) mbrs=new HashSet
    (); for(int i=0; i < size; i++) { Address addr=Util.readAddress(in); mbrs.add(addr); } } } } /** * Handles the server-side of a client-server socket connection. Waits until a client connects, and then loops * until that client closes the connection. Note that there is no new thread spawned for the listening on the * client socket, therefore there can only be 1 client connection at the same time. Subsequent clients attempting * to create a connection will be blocked until the first client closes its connection. This should not be a problem * as the ring nature of the FD_SOCK protocol always has only 1 client connect to its right-hand-side neighbor. */ private class ServerSocketHandler implements Runnable { Thread acceptor=null; /** List */ final List clients=new LinkedList(); ServerSocketHandler() { start(); } final void start() { if(acceptor == null) { acceptor=getThreadFactory().newThread(this, "FD_SOCK server socket acceptor"); acceptor.setDaemon(true); acceptor.start(); } } final void stop(boolean graceful) { if(acceptor != null && acceptor.isAlive()) { try { srv_sock.close(); // this will terminate thread, peer will receive SocketException (socket close) } catch(Exception ex) { } } synchronized(clients) { for(ClientConnectionHandler handler: clients) { handler.stopThread(graceful); } clients.clear(); } acceptor=null; } /** Only accepts 1 client connection at a time (saving threads) */ public void run() { Socket client_sock; while(acceptor != null && srv_sock != null) { try { if(log.isTraceEnabled()) // +++ remove log.trace("waiting for client connections on " + srv_sock.getInetAddress() + ":" + srv_sock.getLocalPort()); client_sock=srv_sock.accept(); if(log.isTraceEnabled()) // +++ remove log.trace("accepted connection from " + client_sock.getInetAddress() + ':' + client_sock.getPort()); ClientConnectionHandler client_conn_handler=new ClientConnectionHandler(client_sock, clients); Thread t = getThreadFactory().newThread(client_conn_handler, "FD_SOCK client connection handler"); t.setDaemon(true); synchronized(clients) { clients.add(client_conn_handler); } t.start(); } catch(IOException io_ex2) { break; } } acceptor=null; } } /** Handles a client connection; multiple client can connect at the same time */ private static class ClientConnectionHandler implements Runnable { Socket client_sock=null; InputStream in; final Object mutex=new Object(); final List clients; ClientConnectionHandler(Socket client_sock, List clients) { this.client_sock=client_sock; this.clients=clients; } void stopThread(boolean graceful) { synchronized(mutex) { if(client_sock != null) { try { if(graceful) { OutputStream out=client_sock.getOutputStream(); out.write(NORMAL_TERMINATION); out.flush(); } closeClientSocket(); } catch(Throwable t) { } } } } private void closeClientSocket() { synchronized(mutex) { Util.close(client_sock); client_sock=null; } } public void run() { try { synchronized(mutex) { if(client_sock == null) return; in=client_sock.getInputStream(); } int b=0; do { b=in.read(); } while(b != ABNORMAL_TERMINATION && b != NORMAL_TERMINATION); } catch(IOException ex) { } finally { Socket sock=client_sock; // PATCH: avoid race condition causing NPE if (sock != null && !sock.isClosed()) closeClientSocket(); synchronized(clients) { clients.remove(this); } } } } /** * Task that periodically broadcasts a list of suspected members to the group. Goal is not to lose * a SUSPECT message: since these are bcast unreliably, they might get dropped. The BroadcastTask makes * sure they are retransmitted until a view has been received which doesn't contain the suspected members * any longer. Then the task terminates. */ private class BroadcastTask implements Runnable { final Set
    suspected_mbrs=new HashSet
    (); Future future; public Set
    getSuspectedMembers() { return Collections.unmodifiableSet(suspected_mbrs); } /** Adds a suspected member. Starts the task if not yet running */ public void addSuspectedMember(Address mbr) { if(mbr == null) return; if(!members.contains(mbr)) return; synchronized(suspected_mbrs) { if(suspected_mbrs.add(mbr)) startTask(); } } public void removeSuspectedMember(Address suspected_mbr) { if(suspected_mbr == null) return; if(log.isDebugEnabled()) log.debug("member is " + suspected_mbr); synchronized(suspected_mbrs) { suspected_mbrs.remove(suspected_mbr); if(suspected_mbrs.isEmpty()) { stopTask(); } } } public void removeAll() { synchronized(suspected_mbrs) { suspected_mbrs.clear(); stopTask(); } } private void startTask() { if(future == null || future.isDone()) { try { future=timer.scheduleWithFixedDelay(this, suspect_msg_interval, suspect_msg_interval, TimeUnit.MILLISECONDS); } catch(RejectedExecutionException e) { if(log.isWarnEnabled()) log.warn("task " + this + " was rejected as timer thread pool is shutting down"); } } } private void stopTask() { if(future != null) { future.cancel(false); future=null; } } /** * Removes all elements from suspected_mbrs that are not in the new membership */ public void adjustSuspectedMembers(Vector
    new_mbrship) { if(new_mbrship == null || new_mbrship.isEmpty()) return; synchronized(suspected_mbrs) { boolean modified=suspected_mbrs.retainAll(new_mbrship); if(log.isTraceEnabled() && modified) log.trace("adjusted suspected_mbrs: " + suspected_mbrs); if(suspected_mbrs.isEmpty()) stopTask(); } } public void run() { Message suspect_msg; FdHeader hdr; if(log.isDebugEnabled()) log.debug("broadcasting SUSPECT message (suspected_mbrs=" + suspected_mbrs + ") to group"); synchronized(suspected_mbrs) { if(suspected_mbrs.isEmpty()) { stopTask(); if(log.isDebugEnabled()) log.debug("task done (no suspected members)"); return; } hdr=new FdHeader(FdHeader.SUSPECT); hdr.mbrs=new HashSet
    (suspected_mbrs); } suspect_msg=new Message(); // mcast SUSPECT to all members suspect_msg.setFlag(Message.OOB); suspect_msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, suspect_msg)); if(log.isDebugEnabled()) log.debug("task done"); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/CAUSAL.java0000644000175000017500000010721211366547366025247 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import java.io.*; import java.util.*; /**

    * Implements casual ordering layer using vector clocks. *

    *

    * Causal protocol layer guarantees that if message m0 multicasted * by a process group member p0 causes process group member * p1 to multicast message m1 then all other remaining process group * members in a current view will receive messages in order m0 * followed by m1. *

    *

    * First time encountered, causal order seems very similar to FIFO order but * there is an important distinction. While FIFO order gurantees that * if process group member p0 multicasts m0 followed by m1 the messages * will be delivered in order m0,m1 to all other group members, causal * order expands this notion of an order from a single group member "space" * to a whole group space i.e if p0 sends message m0 which causes member * p1 to send message m1 then all other group members are guaranteed to * receive m0 followed by m1. *

    *

    * Causal protocol layer achieves this ordering type by introducing sense of * a time in a group using vector clocks. The idea is very simple. Each message * is labeled by a vector, contained in a causal header, representing the number of * prior causal messages received by the sending group member. Vector time of [3,5,2,4] in * a group of four members [p0,p1,p2,p3] means that process p0 has sent 3 messages * and has received 5,2 and 4 messages from a member p1,p2 and p3 respectively. *

    *

    * Each member increases its counter by 1 when it sends a message. When receiving * message mi from a member pi , (where pi != pj) containing vector time VT(mi), * process pj delays delivery of a message mi until: *

    *

    * for every k:1..n * * VT(mi)[k] == VT(pj)[k] + 1 if k=i, * VT(mi)[k] <= VT(pj)[k] otherwise *

    *

    * After the next causal message is delivered at process group pj, VT(pj) is * updated as follows: *

    *

    * for every k:1...n VT(pj)[k] == max(VT(mi)[k],VT(pj)[k]) *

    * @author Vladimir Blagojevic vladimir@cs.yorku.ca * @version $Id: CAUSAL.java,v 1.16.4.2 2008/01/22 10:01:18 belaban Exp $ * **/ public class CAUSAL extends Protocol { public static final class CausalHeader extends Header implements Streamable { /** * Comment for serialVersionUID */ private static final long serialVersionUID = 3760846744526927667L; /** * vector timestamp of this header/message */ private TransportedVectorTime t = null; /** *used for externalization */ public CausalHeader() { } public CausalHeader(TransportedVectorTime timeVector) { t = timeVector; } /** *Returns a vector timestamp carreid by this header *@return Vector timestamp contained in this header */ public TransportedVectorTime getVectorTime() { return t; } /** * Size of this vector timestamp estimation, used in fragmetation * @return headersize in bytes */ public int size() { int retval=Global.BYTE_SIZE; if(t == null) return retval; retval+=t.senderPosition; if(t.values != null) { retval+=t.values.length * Global.INT_SIZE; } return retval; } /** * Manual serialization */ public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(t); } /** * Manual deserialization */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { t = (TransportedVectorTime) in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { if(t == null) { out.writeBoolean(false); return; } out.writeBoolean(true); out.writeInt(t.senderPosition); int values[]=t.values; int len=values.length; out.writeInt(len); for(int i=0;i=len) throw new InstantiationException("sender position="+t.senderPosition+", values length="+len); t.values=new int[len]; for(int i=0;iserialVersionUID. */ private static final long serialVersionUID = 3257569486185183289L; public final static String NAME="CAUSAL_NEWVIEW_HEADER"; /** * New view id. */ private ViewId newViewId; /** * Sender local time. */ private int localTime; private boolean complete; public CausalNewViewHeader(ViewId newViewId, int localTime, boolean complete) { this.newViewId=newViewId; this.localTime=localTime; this.complete=complete; } /** * Used for externalization. */ public CausalNewViewHeader() { } public ViewId getNewViewId() { return newViewId; } public int getLocalTime() { return localTime; } public boolean isComplete() { return complete; } /** * Size of this vector timestamp estimation, used in fragmentation. * @return headersize in bytes */ public int size() { /*why 231, don't know but these are this values I get when flattening the object into byte buffer*/ return 231; } /** * Manual serialization */ public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(newViewId); out.writeInt(localTime); out.writeBoolean(complete); } /** * Manual deserialization */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { newViewId=(ViewId)in.readObject(); localTime=in.readInt(); complete=in.readBoolean(); } public void writeTo(DataOutputStream out) throws IOException { newViewId.writeTo(out); out.writeInt(localTime); out.writeBoolean(complete); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { newViewId=new ViewId(); newViewId.readFrom(in); localTime=in.readInt(); complete=in.readBoolean(); } public String toString() { return '[' + NAME + ':' + newViewId + "; @" + localTime + (complete?"; complete]":"; incomplete]"); } } public static final class MissingIndexesMessage implements Externalizable, Streamable { /** * Comment for serialVersionUID */ private static final long serialVersionUID = 3257007644266213432L; /** * Member indexes the sender is waiting the local time from. */ private int missingTimeIndexes[]; /** * Member indexes the sender is waiting the completion from. */ private int missingCompletionIndexes[]; public MissingIndexesMessage(Collection missingLocalTimes, Collection missingCompletions) { missingTimeIndexes=new int[missingLocalTimes.size()]; int i=0; for(Iterator it=missingLocalTimes.iterator();it.hasNext();) missingTimeIndexes[i++]=((Integer)it.next()).intValue(); missingCompletionIndexes=new int[missingCompletions.size()]; i=0; for(Iterator it=missingCompletions.iterator();it.hasNext();) missingCompletionIndexes[i++]=((Integer)it.next()).intValue(); } /** * Used for externalization. */ public MissingIndexesMessage() { } public int[] getMissingTimeIndexes() { return missingTimeIndexes; } public int[] getMissingCompletionIndexes() { return missingCompletionIndexes; } private static void writeIntArray(DataOutput out, int array[]) throws IOException { out.writeInt(array.length); for(int i=0;i0) sb.append(", "); sb.append(i).append(':').append(members[i]); } sb.append(" - local is ").append(localIndex); return sb.toString(); } } private final class NewCausalView { private final ActiveCausalView active; private final InternalView view; private final int timeVector[]; private final TreeSet missingTimes=new TreeSet(), missingCompletions=new TreeSet(); public NewCausalView(ActiveCausalView active, InternalView view) { this.active=active; this.view=view; // Setup time vector timeVector=new int[view.size()]; if (view.getLocalIndex()>=timeVector.length) throw new IllegalStateException("View: "+view+" timevector.length="+timeVector.length); for(int i=0;i=initialTimeVector.length) throw new IllegalStateException("View: "+view+" timevector.length="+initialTimeVector.length); } public InternalView getView() { return view; } public ViewId getViewId() { return view.getViewId(); } public int getLocalIndex() { return view.getLocalIndex(); } public synchronized int getLocalTime() { return timeVector[view.getLocalIndex()]; } /** * Increment local time. */ public synchronized void increment() { timeVector[view.getLocalIndex()]++; } /** * Returns a minimal lightweight representation of this Vector Time * suitable for network transport. * @return lightweight representation of this VectorTime in the * form of TransportedVectorTime object */ public synchronized TransportedVectorTime getTransportedVectorTime() { // Need to make a copy of the time vector int tmpTimeVector[]=new int[this.timeVector.length]; System.arraycopy(this.timeVector, 0, tmpTimeVector, 0, tmpTimeVector.length); return new TransportedVectorTime(view.getLocalIndex(), tmpTimeVector); } public synchronized boolean isCausallyNext(TransportedVectorTime vector) { int senderIndex = vector.getSenderIndex(); if (senderIndex == view.getLocalIndex()) return true; int[] otherTimeVector = vector.getValues(); if (otherTimeVector.length!=timeVector.length) { if(log.isWarnEnabled()) log.warn("isCausallyNext: got message with wrong time vector length: "+otherTimeVector.length+ ", expected: "+timeVector.length); return true; } boolean nextCausalFromSender = false; boolean nextCausal = true; for (int i=0;i timeVector[i]) nextCausal = false; } return (nextCausalFromSender && nextCausal); } public synchronized void max(TransportedVectorTime vector) { int otherTimeVector[]=vector.getValues(); if (otherTimeVector.length!=timeVector.length) { if(log.isWarnEnabled()) log.warn("max: got message with wrong time vector length: "+otherTimeVector.length+", expected: "+ timeVector.length); return; } for(int i=0;itimeVector[i]) timeVector[i]=otherTimeVector[i]; } } public synchronized void setFinalTimeVector(InternalView newView, int startTimeVector[]) { finalTimeVector=new int[timeVector.length]; System.arraycopy(timeVector, 0, finalTimeVector, 0, timeVector.length); for(int i=0;i0) sb.append(", "); sb.append(timeVector[i]); } return sb.toString(); } public String toString() { return "ActiveCausalView["+view+']'; } } private final class NewViewThread extends Thread { private final NewCausalView newView; private boolean updateRequested=false; NewViewThread(NewCausalView newView) { super(newView.getViewId().toString()); this.newView=newView; setDaemon(true); } NewCausalView getCausalView() { return newView; } public void run() { boolean sendUpdate=false, complete=false; LinkedList flush=null; for(;;) { Message update=null; synchronized(lock) { if(this != newViewThread) { break; } if(newView.hasMissingTimes()) { sendUpdate=true; } else if(currentView == null || (!currentView.getViewId().equals(newView.getViewId()) && currentView.hasEnded())) { currentView=new ActiveCausalView(newView.getView(), newView.timeVector); complete=true; newView.setMemberCompleted(localAddress); if(log.isTraceEnabled()) log.trace("Set up new active view: " + currentView + " @ " + currentView.timeVectorString()); } if(newView.hasMissingCompletions()) { sendUpdate=true; } else { newViewThread=null; enabled=true; flush=(LinkedList)downwardWaitingQueue.clone(); downwardWaitingQueue.clear(); if(log.isTraceEnabled()) log.trace("Done synchronizing, enabled view: " + currentView + " @ " + currentView.timeVectorString()); break; } if(sendUpdate) { update=new Message(null, localAddress, null); update.putHeader(CausalNewViewHeader.NAME , new CausalNewViewHeader(newView.getViewId(), newView.getLocalTime(), complete)); update.setObject(new MissingIndexesMessage(newView.getMissingTimes(), newView.getMissingCompletions())); } } if(update != null) { if(log.isTraceEnabled()) log.trace("Sending sync update"); down_prot.down(new Event(Event.MSG, update)); } synchronized(this) { // Wait for 50ms try { wait(500); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again // Ignore log.warn("Interrupted?!?", e); } sendUpdate=updateRequested; updateRequested=false; } } if (flush!=null) { int n=flush.size(); if (log.isDebugEnabled()) log.debug("Flushing "+n+" messages down..."); while(!flush.isEmpty()) { Event evt=(Event)flush.removeFirst(); down(evt); } if (log.isDebugEnabled()) log.debug("Done flushing "+n+" messages down..."); } } void updateRequested() { synchronized(this) { updateRequested=true; } } } private final Object lock=new Object(); /** * Local address. */ private Address localAddress; /** * Queue containing upward messages waiting for delivery i.e causal order. */ private final LinkedList upwardWaitingQueue=new LinkedList(); /** * Queue containing downward messages waiting for sending. */ private final LinkedList downwardWaitingQueue=new LinkedList(); private boolean enabled=false; /** * The active view (including its time vector), if any. */ private ActiveCausalView currentView; private NewViewThread newViewThread; private boolean debug=false; /** * Default constructor. */ public CAUSAL() { } public boolean setProperties(Properties props) { if (!super.setProperties(props)) return false; String s=props.getProperty("debug"); debug="debug".equalsIgnoreCase(s); return true; } /** * Adds a vectortimestamp to a sorted queue * @param tvt A vector time stamp */ private void addToDelayQueue(TransportedVectorTime tvt) { ListIterator i = upwardWaitingQueue.listIterator(0); TransportedVectorTime current = null; while (i.hasNext()) { current = (TransportedVectorTime) i.next(); if (tvt.lessThanOrEqual(current)) { upwardWaitingQueue.add(i.previousIndex(), tvt); return; } } upwardWaitingQueue.add(tvt); } // Must be called sync'd on lock private void disable() { enabled=false; } // Must be called sync'd on lock private boolean isEnabled() { return enabled; } // // Must be called sync'd on lock // private boolean tryEnable() { // if (currentView!=null && !currentView.hasEnded()) return false; // // currentView=new ActiveCausalView(newView.getView(), newView.timeVector); // newView=null; // enabled=true; // // // FIXME send all waiting messages. // // return true; // } /** * Process a downward event. * @param evt The event. */ public Object down(Event evt) { try { // If not a MSG, just pass down. if (evt.getType()!=Event.MSG) { return down_prot.down(evt); } Message msg = (Message) evt.getArg(); // If unicast, just pass down. if (msg.getDest()!=null && ! msg.getDest().isMulticastAddress()) { return down_prot.down(evt); } // Multicast MSG: // - if enabled, get the next time vector, add it and pass down; // - otherwise, add to the downward waiting queue. TransportedVectorTime tvt=null; synchronized(lock) { if (isEnabled()) { currentView.increment(); tvt=currentView.getTransportedVectorTime(); if (log.isTraceEnabled()) log.trace("Sent 1 down message @ "+currentView.timeVectorString()); } else { if (log.isTraceEnabled()) log.trace("Enqueued 1 down message..."); downwardWaitingQueue.add(evt); } } if (tvt!=null) { msg.putHeader(getName(), new CausalHeader(tvt)); return down_prot.down(evt); } } catch (RuntimeException e) { if (debug) log.error("*** down: "+e.getMessage(), e); throw e; } return null; } /** * Process an upward event. * @param evt The event. */ public Object up(Event evt) { try { switch (evt.getType()) { case Event.SET_LOCAL_ADDRESS: upSetLocalAddress(evt); break; case Event.VIEW_CHANGE: upViewChange(evt); break; case Event.MSG: return upMsg(evt); default: return up_prot.up(evt); } } catch (RuntimeException e) { if (debug) log.error("*** up: "+e.getMessage(), e); throw e; } return null; } private void upSetLocalAddress(Event evt) { localAddress = (Address) evt.getArg(); up_prot.up(evt); } /** * Process a VIEW_CHANGE event. * @param evt The event. */ private void upViewChange(Event evt) { View view=(View)evt.getArg(); InternalView iView=new InternalView(view.getVid(), view.getMembers(), localAddress); if(log.isDebugEnabled()) log.debug("New view: "+view); synchronized(lock) { // Disable sending disable(); // Create new causal view NewCausalView newView=new NewCausalView(currentView, iView); if (currentView!=null) { currentView.clearFinalTimeVector(); newView.setMemberLocalTime(localAddress, currentView.getLocalTime()); } else { newView.setMemberLocalTime(localAddress, 0); } if (log.isTraceEnabled()) log.trace("Starting synchronization thread for "+newView); newViewThread=new NewViewThread(newView); newViewThread.start(); } up_prot.up(evt); } private Object upMsg(Event evt) { Message msg = (Message) evt.getArg(); Address src=msg.getSrc(); // Check for a causal new view header Object obj = msg.getHeader(CausalNewViewHeader.NAME); if (obj instanceof CausalNewViewHeader) { processNewViewSynchronization(src, (CausalNewViewHeader)obj, msg.getObject()); return null; } obj = msg.getHeader(getName()); if (!(obj instanceof CausalHeader)) { if((msg.getDest() == null || msg.getDest().isMulticastAddress()) && log.isErrorEnabled()) log.error("NO CAUSAL.Header found"); return up_prot.up(evt); } TransportedVectorTime messageVector = ((CausalHeader)obj).getVectorTime(); synchronized (lock) { if (currentView==null||currentView.getView().getIndex(src)<0) { if (log.isDebugEnabled()) log.debug("Discarding "+obj+" from "+msg.getSrc()); return null; } if (currentView.isCausallyNext(messageVector)) { if (log.isTraceEnabled()) log.trace("passing up message "+msg+", headers are "+msg.printHeaders()+", local vector is "+currentView.timeVectorString()); up_prot.up(evt); currentView.max(messageVector); } else { if (log.isTraceEnabled()) log.trace("queuing message "+msg+", headers are "+msg.printHeaders()); messageVector.setAssociatedMessage(msg); addToDelayQueue(messageVector); } TransportedVectorTime queuedVector = null; while ((!upwardWaitingQueue.isEmpty()) && currentView.isCausallyNext((queuedVector = (TransportedVectorTime) upwardWaitingQueue.getFirst()))) { upwardWaitingQueue.remove(queuedVector); Message tmp=queuedVector.getAssociatedMessage(); if (log.isTraceEnabled()) log.trace("released message "+tmp+", headers are "+tmp.printHeaders()); up_prot.up(new Event(Event.MSG, tmp)); currentView.max(queuedVector); } } return null; } /** * */ private void processNewViewSynchronization(Address src, CausalNewViewHeader header, Object object) { // If from ourselves, ignore. if (localAddress.equals(src)) return; MissingIndexesMessage content=(MissingIndexesMessage)object; if(log.isTraceEnabled()) log.trace("Got sync update from "+src); synchronized(lock) { if (newViewThread==null) { if (currentView!=null&¤tView.getView().getViewId().equals(header.newViewId)) { // Somebody's late... int localIndex=currentView.getLocalIndex(); if (Arrays.binarySearch(content.getMissingCompletionIndexes(), localIndex)>=0) { Message update=new Message(null, localAddress, null); update.putHeader(CausalNewViewHeader.NAME , new CausalNewViewHeader(currentView.getView().getViewId(), 0, true)); // It has the time already update.setObject(new MissingIndexesMessage(Collections.EMPTY_LIST, Collections.EMPTY_LIST)); down_prot.down(new Event(Event.MSG, update)); } if (Arrays.binarySearch(content.getMissingTimeIndexes(), localIndex)>=0) { } } else { // Somebody's early... disable(); } return; } if (!newViewThread.getCausalView().getViewId().equals(header.newViewId)) return; if (log.isTraceEnabled()) log.trace("From "+src+": "+header); // Update the local time and completion status for the source. newViewThread.getCausalView().setMemberLocalTime(src, header.localTime); if (header.isComplete()) newViewThread.getCausalView().setMemberCompleted(src); // Check the requested times and completions int localIndex=newViewThread.getCausalView().getLocalIndex(); if ( Arrays.binarySearch(content.getMissingTimeIndexes(), localIndex)>=0 || Arrays.binarySearch(content.getMissingCompletionIndexes(), localIndex)>=0 ) { newViewThread.updateRequested(); } } } /** * Returns a name of this stack, each stackhas to have unique name * @return stack's name - CAUSAL */ public String getName() { return "CAUSAL"; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/BSH.java0000644000175000017500000001443011366547366024712 0ustar twernertwerner// $Id: BSH.java,v 1.18 2007/05/01 10:55:09 belaban Exp $ package org.jgroups.protocols; import bsh.EvalError; import bsh.Interpreter; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Header; import org.jgroups.Message; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; /** * Beanshell (www.beanshell.org) interpreter class. * The eval() method receives Java code, executes it and returns the * result of the evaluation (or an exception). * User: Bela * Date: Mar 8, 2003 * Time: 1:57:07 PM * @author Bela Ban */ public class BSH extends Protocol { static final String name="BSH"; Interpreter interpreter=null; public BSH() { } public String getName() { return name; } public void init() throws Exception { } public void start() throws Exception { } public void stop() { if(interpreter != null) destroyInterpreter(); } public void destroy() { } public Object up(Event evt) { Header h; Message msg; int type; if(evt.getType() == Event.MSG) { msg=(Message)evt.getArg(); h=msg.getHeader(name); if(h instanceof BshHeader) { type=((BshHeader)h).type; switch(type) { case BshHeader.REQ: handleRequest(msg.getSrc(), msg.getBuffer()); return null; case BshHeader.RSP: msg.putHeader(name, h); up_prot.up(evt); return null; case BshHeader.DESTROY_INTERPRETER: destroyInterpreter(); return null; default: if(log.isErrorEnabled()) log.error("header type was not REQ as expected (was " + type + ')'); return null; } } } return up_prot.up(evt); } void handleRequest(Address sender, byte[] buf) { Object retval; String code; if(buf == null) { if(log.isErrorEnabled()) log.error("buffer was null"); return; } code=new String(buf); // create interpreter just-in-time if(interpreter == null) { interpreter=new Interpreter(); if(log.isInfoEnabled()) log.info("beanshell interpreter was created"); try { interpreter.set("bsh_prot", this); if(log.isInfoEnabled()) log.info("set \"bsh_prot\" to " + this); } catch(EvalError err) { if(log.isErrorEnabled()) log.error("failed setting \"bsh_prot\": " + err); } } try { retval=interpreter.eval(code); if(log.isInfoEnabled()) log.info("eval: \"" + code + "\", retval=" + retval); } catch(EvalError ex) { if(log.isErrorEnabled()) log.error("error is " + Util.getStackTrace(ex)); retval=ex; } if(sender != null) { Message rsp=new Message(sender, null, null); // serialize the object if serializable, otherwise just send string // representation if(retval != null) { if(retval instanceof Serializable) rsp.setObject((Serializable)retval); else rsp.setObject(retval.toString()); } if(log.isInfoEnabled()) log.info("sending back response " + retval + " to " + rsp.getDest()); rsp.putHeader(name, new BshHeader(BshHeader.RSP)); down_prot.down(new Event(Event.MSG, rsp)); } } /* --------------------------- Callbacks ---------------------------- */ // public Object eval(String code) throws Exception { // Object retval=null; // try { // retval=interpreter.eval(code); // // if(log.isInfoEnabled()) log.info("BSH.eval()", "eval: \"" + code + // "\", retval=" + retval); // if(retval != null && !(retval instanceof Serializable)) { // if(log.isErrorEnabled()) log.error("BSH.eval", "return value " + retval + // " is not serializable, cannot be sent back " + // "(returning null)"); // return null; // } // return retval; // } // catch(EvalError ex) { // if(log.isErrorEnabled()) log.error("BSH.eval()", "error is " + Util.getStackTrace(ex)); // return ex; // } // } // public void destroyInterpreter() { interpreter=null; // allow it to be garbage collected if(log.isInfoEnabled()) log.info("beanshell interpreter was destroyed"); } /* ------------------------ End of Callbacks ------------------------ */ public static class BshHeader extends Header { public static final int REQ=1; public static final int RSP=2; public static final int DESTROY_INTERPRETER=3; int type=REQ; public BshHeader() { } public BshHeader(int type) { this.type=type; } public int size() { return 10; } public String toString() { StringBuilder sb=new StringBuilder(); if(type == REQ) sb.append("REQ"); else if(type == RSP) sb.append("RSP"); else if(type == DESTROY_INTERPRETER) sb.append("DESTROY_INTERPRETER"); else sb.append(""); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(type); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readInt(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/LOOPBACK.java0000644000175000017500000000620311366547366025467 0ustar twernertwerner// $Id: LOOPBACK.java,v 1.25.2.1 2008/05/22 13:23:06 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; /** Makes copies of outgoing messages, swaps sender and receiver and sends the message back up the stack. */ public class LOOPBACK extends TP { private String group_addr=null; public LOOPBACK() { } public String toString() { return "LOOPBACK(local address: " + local_addr + ')'; } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { } public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { } public String getInfo() { return null; } public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { } public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { } /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return "LOOPBACK"; } public void init() throws Exception { super.init(); // local_addr=new IpAddress("localhost", 10000) { // fake address // public String toString() { // return ""; // } // }; //local_addr=new org.jgroups.stack.IpAddress("localhost", 10000); // fake address local_addr = new IpAddress(12345); } public void destroy() { System.out.println("destroy();"); try { timer.stop(); } catch(InterruptedException e) { e.printStackTrace(); } } public void start() throws Exception { up_prot.up(new Event(Event.SET_LOCAL_ADDRESS, local_addr)); } /** * Caller by the layer above this layer. Usually we just put this Message * into the send queue and let one or more worker threads handle it. A worker thread * then removes the Message from the send queue, performs a conversion and adds the * modified Message to the send queue of the layer below it, by calling Down). */ public Object down(Event evt) { if(log.isTraceEnabled()) log.trace("event is " + evt + ", group_addr=" + group_addr + ", time is " + System.currentTimeMillis() + ", hdrs: " + Util.printEvent(evt)); switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Message rsp=msg.copy(); if(rsp.getSrc() == null) rsp.setSrc(local_addr); //dest_addr=msg.getDest(); //rsp.setDest(local_addr); //rsp.setSrc(dest_addr != null ? dest_addr : local_addr); up(new Event(Event.MSG, rsp)); break; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: group_addr=(String)evt.getArg(); break; } return null; } /*--------------------------- End of Protocol interface -------------------------- */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/SFC.java0000644000175000017500000005165211366547366024720 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import org.jgroups.util.Streamable; import org.jgroups.util.BoundedList; import java.util.*; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.TimeUnit; import java.io.*; /** * Simple flow control protocol. After max_credits bytes sent to the group (or an individual member), the sender blocks * until it receives an ack from all members that they indeed received max_credits bytes. * Design in doc/design/SimpleFlowControl.txt
    * Note that SFC supports only flow control for multicast messages; unicast flow control is not supported ! Use FC if * unicast flow control is required. * @author Bela Ban * @version $Id: SFC.java,v 1.19 2007/09/07 11:42:44 belaban Exp $ */ public class SFC extends Protocol { static final String name="SFC"; /** Max number of bytes to send per receiver until an ack must be received before continuing sending */ private long max_credits=2000000; private Long MAX_CREDITS; private static final Long ZERO_CREDITS=new Long(0); /** Current number of credits available to send */ @GuardedBy("lock") private long curr_credits_available; /** Map which keeps track of bytes received from senders */ @GuardedBy("received_lock") private final Map received=new HashMap(12); /** Set of members which have requested credits but from whom we have not yet received max_credits bytes */ @GuardedBy("received_lock") private final Set
    pending_requesters=new HashSet
    (); /** Set of members from whom we haven't yet received credits */ @GuardedBy("lock") private final Set
    pending_creditors=new HashSet
    (); private final Lock lock=new ReentrantLock(); /** Lock protecting access to received and pending_requesters */ private final Lock received_lock=new ReentrantLock(); /** Used to wait for and signal when credits become available again */ private final Condition credits_available=lock.newCondition(); /** Number of milliseconds after which we send a new credit request if we are waiting for credit responses */ private long max_block_time=5000; /** Last time a thread woke up from blocking and had to request credit */ private long last_blocked_request=0L; private final List
    members=new LinkedList
    (); private boolean running=true; private boolean frag_size_received=false; @GuardedBy("lock") long start, stop; // ---------------------- Management information ----------------------- long num_blockings=0; long num_bytes_sent=0; long num_credit_requests_sent=0; long num_credit_requests_received=0; long num_replenishments_received=0; long num_replenishments_sent=0; long total_block_time=0; final BoundedList blockings=new BoundedList(50); public void resetStats() { super.resetStats(); num_blockings=total_block_time=num_replenishments_received=num_credit_requests_sent=num_bytes_sent=0; num_replenishments_sent=num_credit_requests_received=0; blockings.clear(); } public long getMaxCredits() {return max_credits;} public long getCredits() {return curr_credits_available;} public long getBytesSent() {return num_bytes_sent;} public long getBlockings() {return num_blockings;} public long getCreditRequestsSent() {return num_credit_requests_sent;} public long getCreditRequestsReceived() {return num_credit_requests_received;} public long getReplenishmentsReceived() {return num_replenishments_received;} public long getReplenishmentsSent() {return num_replenishments_sent;} public long getTotalBlockingTime() {return total_block_time;} public double getAverageBlockingTime() {return num_blockings == 0? 0 : total_block_time / num_blockings;} public Map dumpStats() { Map retval=super.dumpStats(); if(retval == null) retval=new HashMap(); return retval; } public String printBlockingTimes() { return blockings.toString(); } public String printReceived() { received_lock.lock(); try { return received.toString(); } finally { received_lock.unlock(); } } public String printPendingCreditors() { lock.lock(); try { return pending_creditors.toString(); } finally { lock.unlock(); } } public String printPendingRequesters() { received_lock.lock(); try { return pending_requesters.toString(); } finally { received_lock.unlock(); } } public void unblock() { lock.lock(); try { curr_credits_available=max_credits; credits_available.signalAll(); } finally { lock.unlock(); } } // ------------------- End of management information ---------------------- public final String getName() { return name; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("max_block_time"); if(str != null) { max_block_time=Long.parseLong(str); props.remove("max_block_time"); } str=props.getProperty("max_credits"); if(str != null) { max_credits=Long.parseLong(str); props.remove("max_credits"); } Util.checkBufferSize("SFC.max_credits", max_credits); MAX_CREDITS=new Long(max_credits); curr_credits_available=max_credits; if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest != null && !dest.isMulticastAddress()) // only handle multicast messages break; boolean send_credit_request=false; lock.lock(); try { while(curr_credits_available <=0 && running) { if(log.isTraceEnabled()) log.trace("blocking (current credits=" + curr_credits_available + ")"); try { num_blockings++; // will be signalled when we have credit responses from all members boolean rc=credits_available.await(max_block_time, TimeUnit.MILLISECONDS); if(rc || (curr_credits_available <=0 && running)) { if(log.isTraceEnabled()) log.trace("returned from await but credits still unavailable (credits=" +curr_credits_available +")"); long now=System.currentTimeMillis(); if(now - last_blocked_request >= max_block_time) { last_blocked_request=now; lock.unlock(); // send the credit request without holding the lock try { sendCreditRequest(true); } finally { lock.lock(); // now acquire the lock again } } } else { // reset the last_blocked_request stamp so the // next timed out block will for sure send a request last_blocked_request=0; } } catch(InterruptedException e) { // bela June 16 2007: http://jira.jboss.com/jira/browse/JGRP-536 // if(log.isWarnEnabled()) // log.warn("thread was interrupted", e); // Thread.currentThread().interrupt(); // pass the exception on to the caller // return null; } } // when we get here, curr_credits_available is guaranteed to be > 0 int len=msg.getLength(); num_bytes_sent+=len; curr_credits_available-=len; // we'll block on insufficient credits on the next down() call if(curr_credits_available <=0) { pending_creditors.clear(); synchronized(members) { pending_creditors.addAll(members); } send_credit_request=true; } } finally { lock.unlock(); } // we don't need to protect send_credit_request because a thread above either (a) decrements the credits // by the msg length and sets send_credit_request to true or (b) blocks because there are no credits // available. So only 1 thread can ever set send_credit_request at any given time if(send_credit_request) { if(log.isTraceEnabled()) log.trace("sending credit request to group"); start=System.nanoTime(); // only 1 thread is here at any given time Object ret=down_prot.down(evt); // send the message before the credit request sendCreditRequest(false); // do this outside of the lock return ret; } break; case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; case Event.SUSPECT: handleSuspect((Address)evt.getArg()); break; case Event.INFO: Map map=(Map)evt.getArg(); handleInfo(map); break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Header hdr=(Header)msg.getHeader(name); Address sender=msg.getSrc(); if(hdr != null) { switch(hdr.type) { case Header.CREDIT_REQUEST: handleCreditRequest(sender, false); break; case Header.URGENT_CREDIT_REQUEST: handleCreditRequest(sender, true); break; case Header.REPLENISH: handleCreditResponse(sender); break; default: if(log.isErrorEnabled()) log.error("unknown header type " + hdr.type); break; } return null; // we don't pass the request further up } Address dest=msg.getDest(); if(dest != null && !dest.isMulticastAddress()) // we don't handle unicast messages break; handleMessage(msg, sender); break; case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; case Event.SUSPECT: handleSuspect((Address)evt.getArg()); break; case Event.INFO: Map map=(Map)evt.getArg(); handleInfo(map); break; } return up_prot.up(evt); } public void start() throws Exception { super.start(); if(!frag_size_received) { log.warn("No fragmentation protocol was found. When flow control (e.g. FC or SFC) is used, we recommend " + "a fragmentation protocol, due to http://jira.jboss.com/jira/browse/JGRP-590"); } running=true; } public void stop() { super.stop(); running=false; lock.lock(); try { credits_available.signalAll(); } finally { lock.unlock(); } } private void handleInfo(Map map) { if(map != null) { Integer frag_size=(Integer)map.get("frag_size"); if(frag_size != null) { if(frag_size > max_credits) { log.warn("The fragmentation size of the fragmentation protocol is " + frag_size + ", which is greater than the max credits. While this is not incorrect, " + "it may lead to long blockings. Frag size should be less than max_credits " + "(http://jira.jboss.com/jira/browse/JGRP-590)"); } frag_size_received=true; } } } private void handleMessage(Message msg, Address sender) { int len=msg.getLength(); // we don't care about headers, this is faster than size() Long new_val; boolean send_credit_response=false; received_lock.lock(); try { Long credits=received.get(sender); if(credits == null) { new_val=MAX_CREDITS; received.put(sender, new_val); } else { new_val=credits.longValue() + len; received.put(sender, new_val); } // if(log.isTraceEnabled()) // log.trace("received " + len + " bytes from " + sender + ": total=" + new_val + " bytes"); // see whether we have any pending credit requests if(!pending_requesters.isEmpty() && pending_requesters.contains(sender) && new_val.longValue() >= max_credits) { pending_requesters.remove(sender); if(log.isTraceEnabled()) log.trace("removed " + sender + " from credit requesters; sending credits"); received.put(sender, ZERO_CREDITS); send_credit_response=true; } } finally { received_lock.unlock(); } if(send_credit_response) // send outside of the monitor sendCreditResponse(sender); } private void handleCreditRequest(Address sender, boolean urgent) { boolean send_credit_response=false; received_lock.lock(); try { num_credit_requests_received++; Long bytes=received.get(sender); if(log.isTraceEnabled()) log.trace("received credit request from " + sender + " (total received: " + bytes + " bytes"); if(bytes == null) { if(log.isErrorEnabled()) log.error("received credit request from " + sender + ", but sender is not in received hashmap;" + " adding it"); send_credit_response=true; } else { if(bytes.longValue() < max_credits && !urgent) { if(log.isTraceEnabled()) log.trace("adding " + sender + " to pending credit requesters"); pending_requesters.add(sender); } else { send_credit_response=true; } } if(send_credit_response) received.put(sender, ZERO_CREDITS); } finally{ received_lock.unlock(); } if(send_credit_response) { sendCreditResponse(sender); } } private void handleCreditResponse(Address sender) { lock.lock(); try { num_replenishments_received++; if(pending_creditors.remove(sender) && pending_creditors.isEmpty()) { curr_credits_available=max_credits; stop=System.nanoTime(); long diff=(stop-start)/1000000L; if(log.isTraceEnabled()) log.trace("replenished credits to " + curr_credits_available + " (total blocking time=" + diff + " ms)"); blockings.add(new Long(diff)); total_block_time+=diff; credits_available.signalAll(); } } finally{ lock.unlock(); } } private void handleViewChange(View view) { List
    mbrs=view != null? view.getMembers() : null; if(mbrs != null) { synchronized(members) { members.clear(); members.addAll(mbrs); } } lock.lock(); try { // remove all members which left from pending_creditors if(pending_creditors.retainAll(members) && pending_creditors.isEmpty()) { // the collection was changed and is empty now as a result of retainAll() curr_credits_available=max_credits; if(log.isTraceEnabled()) log.trace("replenished credits to " + curr_credits_available); credits_available.signalAll(); } } finally { lock.unlock(); } received_lock.lock(); try { // remove left members received.keySet().retainAll(members); // add new members with *full* credits (see doc/design/SimpleFlowControl.txt for reason) for(Address mbr: members) { if(!received.containsKey(mbr)) received.put(mbr, MAX_CREDITS); } // remove left members from pending credit requesters pending_requesters.retainAll(members); } finally{ received_lock.unlock(); } } private void handleSuspect(Address suspected_mbr) { // this is the same as a credit response - we cannot block forever for a crashed member handleCreditResponse(suspected_mbr); } private void sendCreditRequest(boolean urgent) { Message credit_req=new Message(); // credit_req.setFlag(Message.OOB); // we need to receive the credit request after regular messages byte type=urgent? Header.URGENT_CREDIT_REQUEST : Header.CREDIT_REQUEST; credit_req.putHeader(name, new Header(type)); num_credit_requests_sent++; down_prot.down(new Event(Event.MSG, credit_req)); } private void sendCreditResponse(Address dest) { Message credit_rsp=new Message(dest); credit_rsp.setFlag(Message.OOB); Header hdr=new Header(Header.REPLENISH); credit_rsp.putHeader(name, hdr); if(log.isTraceEnabled()) log.trace("sending credit response to " + dest); num_replenishments_sent++; down_prot.down(new Event(Event.MSG, credit_rsp)); } public static class Header extends org.jgroups.Header implements Streamable { public static final byte CREDIT_REQUEST = 1; // the sender of the message is the requester public static final byte REPLENISH = 2; // the sender of the message is the creditor public static final byte URGENT_CREDIT_REQUEST = 3; byte type=CREDIT_REQUEST; public Header() { } public Header(byte type) { this.type=type; } public int size() { return Global.BYTE_SIZE; } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); } public String toString() { switch(type) { case REPLENISH: return "REPLENISH"; case CREDIT_REQUEST: return "CREDIT_REQUEST"; case URGENT_CREDIT_REQUEST: return "URGENT_CREDIT_REQUEST"; default: return ""; } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TransportedVectorTime.java0000644000175000017500000000761111366547366030610 0ustar twernertwerner// $Id: TransportedVectorTime.java,v 1.7.4.1 2008/01/22 10:01:20 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Message; import java.io.Serializable; /** * Lighweight representation of the VectorTime clock suitable for network transport * * @author Vladimir Blagojevic vladimir@cs.yorku.ca * @version $Revision: 1.7.4.1 $ */ public class TransportedVectorTime implements Serializable { /** * index of the sender */ int senderPosition; /** * values represented as an array */ int[] values; /** * Message associated with this vector clock */ private transient Message m; private static final long serialVersionUID = 5857647322589533545L; /** * */ public TransportedVectorTime() { } /** * Constructs TransportedVectorTime with sender index and vector values * * @param senderIndex index of the sender of the message * @param values vector values */ public TransportedVectorTime(int senderIndex, int[] values) { this.values = values; this.senderPosition = senderIndex; } /** * Returns sender index * @return sender index position */ public int getSenderIndex() { return senderPosition; } /** * Returns vector values * @return an array of vector values */ public int[] getValues() { return values; } /** * Returns size of this vector timestamp i.e number of process group members * @return vector timestamp size */ public int size() { return values.length; } /** * Sets a message associated with this vector timestamp * @param owner Message that is associated with this vector timestamp */ public void setAssociatedMessage(Message owner) { m = owner; } /** * Returns a message associated with this vector timestamp. * @return Message associated with this vector timestamp */ public Message getAssociatedMessage() { return m; } /** *

    *Checks if this TransportedVectorTime is less than or equal to the the specified TransportedVectorTime. *The check is done as follows: *

    *

    * VT1<=VT2 iff for every i:1..k VT1[i]<=VT2[i] *

    * @param other TransportedVectorTimebeing compared with this. * @return true if this TransportedVectorTimeis less than or equal from * other, false othwerwise */ public boolean lessThanOrEqual(TransportedVectorTime other) { int[] b = other.getValues(); int[] a = values; for (int k = 0; k < a.length; k++) { if (a[k] < b[k]) return true; else if (a[k] > b[k]) return false; } return true; // equals ?!? } /** *

    * Checks if this TransportedVectorTimeis equal to the specified TransportedVectorTime. * The check is done as follows: *

    *

    * VT1==VT2 iff for every i:1..k VT1[i]==VT2[i] * @param other TransportedVectorTimebeing compared with this. * @return true if the equation given above is true, false otherwise */ public boolean equals(Object other) { int a [] = getValues(); int b [] = ((TransportedVectorTime)other).getValues(); for (int i = 0; i < a.length; i++) if (a[i] != b[i]) return false; return true; } /** * Returns String representation of this vector timestamp * @return String representing this vetor timestamp */ public String toString() { String classType = "TransportedVectorTime["; int bufferLength = classType.length() + values.length * 2 + 1; StringBuilder buf = new StringBuilder(bufferLength); buf.append(classType); for (int i = 0; i < values.length - 1; i++) { buf.append(values[i]).append(','); } buf.append(values[values.length - 1]); buf.append(']'); return buf.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FILE_PING.java0000644000175000017500000001243211366547366025632 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.util.Util; import org.jgroups.util.Promise; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * Simple discovery protocol which uses a file on shared storage such as an SMB share, NFS mount or S3. The local * address information, e.g. UUID and physical addresses mappings are written to the file and the content is read and * added to our transport's UUID-PhysicalAddress cache.

    * The design is at doc/design/FILE_PING.txt * @author Bela Ban * @version $Id: FILE_PING.java,v 1.5.2.7 2009/09/29 04:36:30 belaban Exp $ */ public class FILE_PING extends Discovery { protected static final String name="FILE_PING"; protected static final String SUFFIX=".node"; /* ----------------------------------------- Properties -------------------------------------------------- */ // location of the shared directory used for discovery" protected String location=File.separator + "tmp" + File.separator + "jgroups"; /* --------------------------------------------- Fields ------------------------------------------------------ */ protected File root_dir=null; protected FilenameFilter filter; public String getName() { return name; } public boolean setProperties(Properties props) { String str; str=props.getProperty("location"); if(str != null) { location=str; props.remove("location"); } return super.setProperties(props); } public void init() throws Exception { super.init(); root_dir=new File(location); if(root_dir.exists()) { if(!root_dir.isDirectory()) throw new IllegalArgumentException("location " + root_dir.getPath() + " is not a directory"); } else { root_dir.mkdirs(); } if(!root_dir.exists()) throw new IllegalArgumentException("location " + root_dir.getPath() + " could not be accessed"); filter=new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(SUFFIX); } }; } public void sendGetMembersRequest(Promise promise) throws Exception{ String cluster_name=group_addr; List

    existing_mbrs=readAll(cluster_name); // If we don't find any files, return immediately if(existing_mbrs.isEmpty() || (existing_mbrs.size() == 1 && existing_mbrs.contains(local_addr))) { if(promise != null) { promise.setResult(null); } } else { // 1. Send GET_MBRS_REQ message to members listed in the file for(final Address dest: existing_mbrs) { if(dest.equals(local_addr)) continue; PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); final Message msg=new Message(dest); msg.setFlag(Message.OOB); msg.putHeader(getName(), hdr); // needs to be getName(), so we might get "MPING" ! if(log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); timer.execute(new Runnable() { public void run() { try { down_prot.down(new Event(Event.MSG, msg)); } catch(Exception ex){ if(log.isErrorEnabled()) log.error("failed sending discovery request to " + dest, ex); } } }); } } // Write my own data to file writeToFile(local_addr, cluster_name); } /** * Reads all information from the given directory under clustername * @return */ protected List
    readAll(String clustername) { List
    retval=new ArrayList
    (); File dir=new File(root_dir, clustername); if(!dir.exists()) dir.mkdir(); File[] files=dir.listFiles(filter); if(files != null) { for(File file: files) retval.add(readFile(file)); } return retval; } protected static Address readFile(File file) { Address retval=null; DataInputStream in=null; try { in=new DataInputStream(new FileInputStream(file)); return Util.readAddress(in); } catch(Exception e) { } finally { Util.close(in); } return retval; } protected void writeToFile(Address addr, String clustername) { DataOutputStream out=null; File dir=new File(root_dir, clustername); if(!dir.exists()) dir.mkdir(); File file=new File(dir, addr.toString() + SUFFIX); file.deleteOnExit(); try { out=new DataOutputStream(new FileOutputStream(file)); Util.writeAddress(addr, out); } catch(Exception e) { } finally { Util.close(out); } } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/UdpHeader.java0000644000175000017500000000237611366547366026145 0ustar twernertwerner// $Id: UdpHeader.java,v 1.9 2007/05/01 10:55:10 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.util.Streamable; import java.io.*; public class UdpHeader extends Header implements Streamable { public String channel_name=null; int size=0; public UdpHeader() { } // used for externalization public UdpHeader(String n) { channel_name=n; if(channel_name != null) size=channel_name.length()+2; // +2 for writeUTF() } public String toString() { return "[UDP:channel_name=" + channel_name + ']'; } public int size() { return size; } public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(channel_name); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { channel_name=in.readUTF(); } public void writeTo(DataOutputStream out) throws IOException { out.writeUTF(channel_name); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { channel_name=in.readUTF(); if(channel_name != null) size=channel_name.length()+2; // +2 for writeUTF() } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FD_PING.java0000644000175000017500000001326111366547366025405 0ustar twernertwernerpackage org.jgroups.protocols; import org.apache.commons.logging.Log; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Properties; /** * Protocol which uses an executable (e.g. /sbin/ping, or a script) to check whether a given host is up or not, * taking 1 argument; the host name of the host to be pinged. Property 'cmd' determines the program to be executed * (use a fully qualified name if the program is not on the path). * @author Bela Ban * @version $Id: FD_PING.java,v 1.4.4.1 2008/01/22 10:01:20 belaban Exp $ */ public class FD_PING extends FD { /** Command (script or executable) to ping a host: a return value of 0 means success, anything else is a failure. * The only argument passed to cmd is the host's address (symbolic name or dotted-decimal IP address) */ String cmd="ping"; /** Write the stdout of the command to the log */ boolean verbose=true; public String getName() { return "FD_PING"; } public boolean setProperties(Properties props) { String str; str=props.getProperty("cmd"); if(str != null) { cmd=str; props.remove("cmd"); } str=props.getProperty("verbose"); if(str != null) { verbose=new Boolean(str).booleanValue(); props.remove("verbose"); } super.setProperties(props); if(props.size() > 0) { log.error("the following properties are not recognized: " + props); return false; } return true; } protected Monitor createMonitor() { return new PingMonitor(); } /** * Executes the ping command. Each time the command fails, we increment num_tries. If num_tries > max_tries, we * emit a SUSPECT message. If ping_dest changes, or we do receive traffic from ping_dest, we reset num_tries to 0. */ protected class PingMonitor extends Monitor { public void run() { if(ping_dest == null) { if(log.isWarnEnabled()) log.warn("ping_dest is null: members=" + members + ", pingable_mbrs=" + pingable_mbrs + ", local_addr=" + local_addr); return; } // 1. execute ping command String host=ping_dest instanceof IpAddress? ((IpAddress)ping_dest).getIpAddress().getHostAddress() : ping_dest.toString(); String command=cmd + " " + host; if(log.isDebugEnabled()) log.debug("executing \"" + command + "\" (own address=" + local_addr + ')'); try { Log tmp_log=verbose? log : null; int rc=Pinger.execute(command, tmp_log); num_heartbeats++; if(rc == 0) { // success num_tries=0; } else { // failure num_tries++; if(log.isDebugEnabled()) log.debug("could not ping " + ping_dest + " (tries=" + num_tries + ')'); } if(num_tries >= max_tries) { if(log.isDebugEnabled()) log.debug("[" + local_addr + "]: could not ping " + ping_dest + " for " + (num_tries +1) + " times (" + ((num_tries+1) * timeout) + " milliseconds), suspecting it"); // broadcast a SUSPECT message to all members - loop until // unsuspect or view change is received bcast_task.addSuspectedMember(ping_dest); num_tries=0; if(stats) { num_suspect_events++; suspect_history.add(ping_dest); } } } catch(Exception ex) { if(log.isErrorEnabled()) log.error("failed executing command " + command, ex); } } } protected static class Pinger { static int execute(String command, Log log) throws IOException, InterruptedException { Process p=Runtime.getRuntime().exec(command); InputStream in=p.getInputStream(), err=p.getErrorStream(); try { Reader in_reader, err_reader; in_reader=new Reader(in, log); err_reader=new Reader(err, log); in_reader.start(); err_reader.start(); in_reader.join(); err_reader.join(); return p.exitValue(); } finally { Util.close(in); Util.close(err); } } static class Reader extends Thread { InputStreamReader in; Log log=null; boolean trace=false; Reader(InputStream in, Log log) { this.in=new InputStreamReader(in); this.log=log; if(log != null) { trace=log.isTraceEnabled(); } } public void run() { int c; StringBuilder sb=new StringBuilder(); while(true) { try { c=in.read(); if(c == -1) break; sb.append((char)c); } catch(IOException e) { break; } } if(log.isTraceEnabled()) log.trace(sb.toString()); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/PARTITION.java0000644000175000017500000000347211366547366025653 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.Protocol; /** * Protocol to simulate a partition. This can be done in 2 ways: send down a PARTITION event (with a boolean flag on or * off, to start or end a partition), or by grabbing a reference to the protocol via the ProtocolStack and calling the * methods startPartition() or stopPartition() directly. This can also be done via JMX.

    * A partition simply discards all messages, but let's other events pass. * @author Bela Ban * @version $Id: PARTITION.java,v 1.3 2007/08/27 11:05:32 belaban Exp $ */ public class PARTITION extends Protocol { protected boolean partition_on=false; protected Address local_address=null; public String getName() { return "PARTITION"; } public boolean isPartitionOn() { return partition_on; } public void startPartition() { partition_on=true; } public void stopPartition() { partition_on=false; } public Object down(Event evt) { switch(evt.getType()) { case Event.START_PARTITION: startPartition(); return null; case Event.STOP_PARTITION: stopPartition(); return null; default: return down_prot.down(evt); } } public Object up(Event evt) { if(evt.getType() == Event.SET_LOCAL_ADDRESS) local_address=(Address)evt.getArg(); if(partition_on == false) return up_prot.up(evt); if(evt.getType() != Event.MSG) return up_prot.up(evt); Message msg=(Message)evt.getArg(); if(msg.getSrc().equals(local_address)) return up_prot.up(evt); return null; } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/SMACK.java0000644000175000017500000003330511366547366025136 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.AckMcastSenderWindow; import org.jgroups.stack.AckReceiverWindow; import org.jgroups.stack.Protocol; import org.jgroups.stack.StaticInterval; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.*; /** * Simple Multicast ACK protocol. A positive acknowledgment-based protocol for reliable delivery of * multicast messages, which does not need any group membership service. * Basically works as follows: *

      *
    • Sender S sends multicast message M
    • *
    • When member P receives M, it sends back a unicast ack to S
    • *
    • When S receives the ack from P, it checks whether P is in its * membership list. If not, P will be added. This is necessary to retransmit the next message * sent to P.
    • *
    • When S sends a multicast message M, all members are added to a * retransmission entry (containing all members to which the message * was sent), which is added to a hashmap (keyed by seqno). Whenever * an ack is received from receiver X, X will be removed from the * retransmission list for the given seqno. When the retransmission * list is empty, the seqno will be removed from the hashmap.
    • *
    • A retransmitter thread in the sender periodically retransmits * (either via unicast, or multicast) messages for which no ack has * been received yet
    • *
    • When a max number of (unsuccessful) retransmissions have been * exceeded, all remaining members for that seqno are removed from * the local membership, and the seqno is removed from the hashmap, * ceasing all retransmissions
    • *
    * Advantage of this protocol: no group membership necessary, fast. * @author Bela Ban Aug 2002 * @version $Id: SMACK.java,v 1.26.2.1 2009/09/08 12:25:11 belaban Exp $ *
    Fix membershop bug: start a, b, kill b, restart b: b will be suspected by a. */ public class SMACK extends Protocol implements AckMcastSenderWindow.RetransmitCommand { long[] timeout=new long[]{1000,2000,3000}; // retransmit timeouts (for AckMcastSenderWindow) int max_xmits=10; // max retransmissions (if still no ack, member will be removed) final Set
    members=new LinkedHashSet
    (); // contains Addresses AckMcastSenderWindow sender_win=null; final Map receivers=new HashMap(); // keys=sender (Address), values=AckReceiverWindow final Map xmit_table=new HashMap(); // keeps track of num xmits / member (keys: mbr, val:num) Address local_addr=null; // my own address long seqno=1; // seqno for msgs sent by this sender long vid=1; // for the fake view changes boolean print_local_addr=true; static final String name="SMACK"; public SMACK() { } public String getName() { return name; } public boolean setProperties(Properties props) { String str; long[] tmp; super.setProperties(props); str=props.getProperty("print_local_addr"); if(str != null) { print_local_addr=Boolean.valueOf(str).booleanValue(); props.remove("print_local_addr"); } str=props.getProperty("timeout"); if(str != null) { tmp=Util.parseCommaDelimitedLongs(str); props.remove("timeout"); if(tmp != null && tmp.length > 0) timeout=tmp; } str=props.getProperty("max_xmits"); if(str != null) { max_xmits=Integer.parseInt(str); props.remove("max_xmits"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void stop() { AckReceiverWindow win; if(sender_win != null) { sender_win.stop(); sender_win=null; } for(Iterator it=receivers.values().iterator(); it.hasNext();) { win=(AckReceiverWindow)it.next(); win.reset(); } receivers.clear(); } public Object up(Event evt) { Address sender; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); addMember(local_addr); if(print_local_addr) { System.out.println("\n-------------------------------------------------------\n" + "GMS: address is " + local_addr + "\n-------------------------------------------------------"); } break; case Event.SUSPECT: if(log.isInfoEnabled()) log.info("removing suspected member " + evt.getArg()); removeMember((Address)evt.getArg()); break; case Event.MSG: Message msg=(Message)evt.getArg(), tmp_msg; if(msg == null) break; sender=msg.getSrc(); SmackHeader hdr=(SmackHeader)msg.getHeader(name); if(hdr == null) // is probably a unicast message break; switch(hdr.type) { case SmackHeader.MCAST: // send an ack, then pass up (if not already received) if(log.isTraceEnabled()) log.trace("received #" + hdr.seqno + " from " + sender); AckReceiverWindow win=receivers.get(sender); if(win == null) { addMember(sender); win=new AckReceiverWindow(hdr.seqno); receivers.put(sender, win); } boolean added=win.add(hdr.seqno, msg); Message ack_msg=new Message(sender); ack_msg.putHeader(name, new SmackHeader(SmackHeader.ACK, hdr.seqno)); down_prot.down(new Event(Event.MSG, ack_msg)); // message is passed up if OOB. Later, when remove() is called, we discard it. This affects ordering ! // http://jira.jboss.com/jira/browse/JGRP-379 if(msg.isFlagSet(Message.OOB) && added) { up_prot.up(new Event(Event.MSG, msg)); } // now remove as many messages as possible while((tmp_msg=win.remove()) != null) { // discard OOB msg as it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-379) if(tmp_msg.isFlagSet(Message.OOB)) { continue; } up_prot.up(new Event(Event.MSG, tmp_msg)); } return null; case SmackHeader.ACK: addMember(msg.getSrc()); sender_win.ack(hdr.seqno, msg.getSrc()); sender_win.clearStableMessages(); if(log.isTraceEnabled()) log.trace("received ack for #" + hdr.seqno + " from " + msg.getSrc()); return null; case SmackHeader.JOIN_ANNOUNCEMENT: if(log.isInfoEnabled()) log.info("received join announcement by " + msg.getSrc()); if(!containsMember(sender)) { Message join_rsp=new Message(sender); join_rsp.putHeader(name, new SmackHeader(SmackHeader.JOIN_ANNOUNCEMENT, -1)); down_prot.down(new Event(Event.MSG, join_rsp)); } addMember(sender); return null; case SmackHeader.LEAVE_ANNOUNCEMENT: if(log.isInfoEnabled()) log.info("received leave announcement by " + msg.getSrc()); removeMember(sender); return null; default: if(log.isWarnEnabled()) log.warn("detected SmackHeader with invalid type: " + hdr); break; } break; } return up_prot.up(evt); } public Object down(Event evt) { Message leave_msg; switch(evt.getType()) { case Event.DISCONNECT: leave_msg=new Message(); leave_msg.putHeader(name, new SmackHeader(SmackHeader.LEAVE_ANNOUNCEMENT, -1)); down_prot.down(new Event(Event.MSG, leave_msg)); sender_win.stop(); break; case Event.CONNECT: Object ret=down_prot.down(evt); sender_win=new AckMcastSenderWindow(this, new StaticInterval(timeout)); // send join announcement Message join_msg=new Message(); join_msg.putHeader(name, new SmackHeader(SmackHeader.JOIN_ANNOUNCEMENT, -1)); down_prot.down(new Event(Event.MSG, join_msg)); return ret; // add a header with the current sequence number and increment seqno case Event.MSG: Message msg=(Message)evt.getArg(); if(msg == null) break; if(msg.getDest() == null || msg.getDest().isMulticastAddress()) { msg.putHeader(name, new SmackHeader(SmackHeader.MCAST, seqno)); sender_win.add(seqno, msg, new Vector
    (members)); if(log.isTraceEnabled()) log.trace("sending mcast #" + seqno); seqno++; } break; } return down_prot.down(evt); } /* ----------------------- Interface AckMcastSenderWindow.RetransmitCommand -------------------- */ public void retransmit(long seqno, Message msg, Address dest) { msg.setDest(dest); if(log.isInfoEnabled()) log.info(seqno + ", msg=" + msg); down_prot.down(new Event(Event.MSG, msg)); } /* -------------------- End of Interface AckMcastSenderWindow.RetransmitCommand ---------------- */ public static class SmackHeader extends Header implements Streamable { public static final byte MCAST=1; public static final byte ACK=2; public static final byte JOIN_ANNOUNCEMENT=3; public static final byte LEAVE_ANNOUNCEMENT=4; byte type=0; long seqno=-1; private static final long serialVersionUID=7605481696520929774L; public SmackHeader() { } public SmackHeader(byte type, long seqno) { this.type=type; this.seqno=seqno; } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeLong(seqno); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); seqno=in.readLong(); } public int size() { return Global.LONG_SIZE + Global.BYTE_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(seqno); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); seqno=in.readLong(); } public String toString() { switch(type) { case MCAST: return "MCAST"; case ACK: return "ACK"; case JOIN_ANNOUNCEMENT: return "JOIN_ANNOUNCEMENT"; case LEAVE_ANNOUNCEMENT: return "LEAVE_ANNOUNCEMENT"; default: return ""; } } } /* ------------------------------------- Private methods --------------------------------------- */ void addMember(Address mbr) { Vector
    tmp=null; synchronized(members) { if(members.add(mbr)) { tmp=new Vector
    (members); } } if(tmp != null) { if(log.isTraceEnabled()) log.trace("added " + mbr + ", members=" + tmp); View new_view=new View(new ViewId(local_addr, vid++), tmp); up_prot.up(new Event(Event.VIEW_CHANGE, new_view)); down_prot.down(new Event(Event.VIEW_CHANGE, new_view)); } } void removeMember(Address mbr) { Vector
    tmp=null; synchronized(members) { if(members.remove(mbr)) tmp=new Vector
    (members); } if(tmp != null) { if(log.isTraceEnabled()) log.trace("removed " + mbr + ", members=" + tmp); View new_view=new View(new ViewId(local_addr, vid++), tmp); up_prot.up(new Event(Event.VIEW_CHANGE, new_view)); down_prot.down(new Event(Event.VIEW_CHANGE, new_view)); if(sender_win != null) sender_win.remove(mbr); // causes retransmissions to mbr to stop } } boolean containsMember(Address mbr) { synchronized(members) { return members.contains(mbr); } } /* --------------------------------- End of Private methods ------------------------------------ */ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/UNICAST.java0000644000175000017500000010322211366547366025402 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.AckReceiverWindow; import org.jgroups.stack.AckSenderWindow; import org.jgroups.stack.Protocol; import org.jgroups.stack.StaticInterval; import org.jgroups.util.AgeOutCache; import org.jgroups.util.Streamable; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.*; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.*; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Lock; /** * Reliable unicast layer. Uses acknowledgement scheme similar to TCP to provide lossless transmission * of unicast messages (for reliable multicast see NAKACK layer). When a message is sent to a peer for * the first time, we add the pair to the hashtable (peer address is the key). All * messages sent to that peer will be added to hashtable.peer_addr.sent_msgs. When we receive a * message from a peer for the first time, another entry will be created and added to the hashtable * (unless already existing). Msgs will then be added to hashtable.peer_addr.received_msgs.

    This * layer is used to reliably transmit point-to-point messages, that is, either messages sent to a * single receiver (vs. messages multicast to a group) or for example replies to a multicast message. The * sender uses an AckSenderWindow which retransmits messages for which it hasn't received * an ACK, the receiver uses AckReceiverWindow which keeps track of the lowest seqno * received so far, and keeps messages in order.

    * Messages in both AckSenderWindows and AckReceiverWindows will be removed. A message will be removed from * AckSenderWindow when an ACK has been received for it and messages will be removed from AckReceiverWindow * whenever a message is received: the new message is added and then we try to remove as many messages as * possible (until we stop at a gap, or there are no more messages). * @author Bela Ban * @version $Id: UNICAST.java,v 1.91.2.24 2009/09/18 08:03:20 belaban Exp $ */ public class UNICAST extends Protocol implements AckSenderWindow.RetransmitCommand, AgeOutCache.Handler

    { public static final long DEFAULT_FIRST_SEQNO=Global.DEFAULT_FIRST_UNICAST_SEQNO; /* ------------------------------------------ Properties ------------------------------------------ */ /** Whether to loop back messages sent to self. Default is false. */ private boolean loopback=false; private long[] timeout= { 400, 800, 1600, 3200 }; // for AckSenderWindow: max time to wait for missing acks /* --------------------------------------------- JMX ---------------------------------------------- */ private long num_msgs_sent=0, num_msgs_received=0, num_bytes_sent=0, num_bytes_received=0; private long num_acks_sent=0, num_acks_received=0, num_xmits=0; /* --------------------------------------------- Fields ------------------------------------------------ */ private final Vector
    members=new Vector
    (11); private final ConcurrentMap send_table=new ConcurrentHashMap(); private final ConcurrentMap recv_table=new ConcurrentHashMap(); private Address local_addr=null; private TimeScheduler timer=null; // used for retransmissions (passed to AckSenderWindow) private boolean started=false; // didn't make this 'connected' in case we need to send early acks which may race to the socket private volatile boolean disconnected=false; /** Regular messages which have been added, but not removed */ private final AtomicInteger undelivered_msgs=new AtomicInteger(0); private long last_conn_id=0; /** Max number of milliseconds we try to retransmit a message to any given member. After that, the connection is removed. Any new connection to that member will start with seqno #1 again. 0 disables it */ protected long max_retransmit_time=60 * 1000L; private AgeOutCache
    cache=null; public long[] getTimeout() {return timeout;} public void setTimeout(long[] val) { if(val != null) timeout=val; } public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} public String getMembers() {return members != null? members.toString() : "[]";} public String printConnections() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: recv_table.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } public long getNumMessagesSent() { return num_msgs_sent; } public long getNumMessagesReceived() { return num_msgs_received; } public long getNumBytesSent() { return num_bytes_sent; } public long getNumBytesReceived() { return num_bytes_received; } public long getNumAcksSent() { return num_acks_sent; } public long getNumAcksReceived() { return num_acks_received; } public long getNumberOfRetransmissions() { return num_xmits; } public long getMaxRetransmitTime() { return max_retransmit_time; } public void setMaxRetransmitTime(long max_retransmit_time) { this.max_retransmit_time=max_retransmit_time; if(cache != null && max_retransmit_time > 0) cache.setTimeout(max_retransmit_time); } public int getAgeOutCacheSize() { return cache != null? cache.size() : 0; } public String printAgeOutCache() { return cache != null? cache.toString() : "n/a"; } public AgeOutCache getAgeOutCache() { return cache; } /** The number of messages in all Entry.sent_msgs tables (haven't received an ACK yet) */ public int getNumberOfUnackedMessages() { int num=0; for(SenderEntry entry: send_table.values()) { if(entry.sent_msgs != null) num+=entry.sent_msgs.size(); } return num; } public String printUnackedMessages() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: send_table.entrySet()) { sb.append(entry.getKey()).append(": "); SenderEntry val=entry.getValue(); if(val.sent_msgs != null) sb.append(val.sent_msgs).append("\n"); } return sb.toString(); } public int getNumberOfMessagesInReceiveWindows() { int num=0; for(ReceiverEntry entry: recv_table.values()) { if(entry.received_msgs != null) num+=entry.received_msgs.size(); } return num; } public void resetStats() { num_msgs_sent=num_msgs_received=num_bytes_sent=num_bytes_received=num_acks_sent=num_acks_received=0; num_xmits=0; } public Map dumpStats() { Map m=super.dumpStats(); m.put("unacked_msgs", printUnackedMessages()); m.put("num_msgs_sent", num_msgs_sent); m.put("num_msgs_received", num_msgs_received); m.put("num_bytes_sent", num_bytes_sent); m.put("num_bytes_received", num_bytes_received); m.put("num_acks_sent", num_acks_sent); m.put("num_acks_received", num_acks_received); m.put("num_xmits", num_xmits); m.put("num_unacked_msgs", getNumberOfUnackedMessages()); m.put("num_msgs_in_recv_windows", getNumberOfMessagesInReceiveWindows()); return m; } public String getName() { return "UNICAST"; } public boolean setProperties(Properties props) { String str; long[] tmp; super.setProperties(props); str=props.getProperty("timeout"); if(str != null) { tmp=Util.parseCommaDelimitedLongs(str); if(tmp != null && tmp.length > 0) timeout=tmp; props.remove("timeout"); } str=props.getProperty("window_size"); if(str != null) { props.remove("window_size"); log.warn("window_size is deprecated and will be ignored"); } str=props.getProperty("min_threshold"); if(str != null) { props.remove("min_threshold"); log.warn("min_threshold is deprecated and will be ignored"); } str=props.getProperty("use_gms"); if(str != null) { log.warn("use_gms has been deprecated and is ignored"); props.remove("use_gms"); } str=props.getProperty("immediate_ack"); if(str != null) { log.warn("immediate_ack has been deprecated and is ignored"); props.remove("immediate_ack"); } str=props.getProperty("loopback"); if(str != null) { loopback=Boolean.valueOf(str).booleanValue(); props.remove("loopback"); } str=props.getProperty("eager_lock_release"); if(str != null) { log.warn("eager_lock_release has been deprecated and is ignored"); props.remove("eager_lock_release"); } if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } public void start() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer is null"); if(max_retransmit_time > 0) cache=new AgeOutCache
    (timer, max_retransmit_time, this); started=true; } public void stop() { started=false; removeAllConnections(); undelivered_msgs.set(0); } public Object up(Event evt) { Message msg; Address dst, src; UnicastHeader hdr; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); dst=msg.getDest(); if(dst == null || dst.isMulticastAddress()) // only handle unicast messages break; // pass up // changed from removeHeader(): we cannot remove the header because if we do loopback=true at the // transport level, we will not have the header on retransmit ! (bela Aug 22 2006) hdr=(UnicastHeader)msg.getHeader(getName()); if(hdr == null) break; src=msg.getSrc(); switch(hdr.type) { case UnicastHeader.DATA: // received regular message handleDataReceived(src, hdr.seqno, hdr.conn_id, hdr.first, msg); return null; // we pass the deliverable message up in handleDataReceived() case UnicastHeader.ACK: // received ACK for previously sent message handleAckReceived(src, hdr.seqno); break; case UnicastHeader.SEND_FIRST_SEQNO: handleResendingOfFirstMessage(src); break; default: log.error("UnicastHeader type " + hdr.type + " not known !"); break; } return null; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return up_prot.up(evt); // Pass up to the layer above us } public Object down(Event evt) { switch (evt.getType()) { case Event.MSG: // Add UnicastHeader, add to AckSenderWindow and pass down Message msg=(Message)evt.getArg(); Address dst=msg.getDest(); /* only handle unicast messages */ if (dst == null || dst.isMulticastAddress()) { break; } if(!started) { if(log.isTraceEnabled()) log.trace("discarded message as start() has not yet been called, message: " + msg); return null; } // if the dest is self --> pass the message back up if(loopback && local_addr != null && local_addr.equals(dst)) { msg.setSrc(local_addr); up_prot.up(evt); num_msgs_sent++; num_bytes_sent+=msg.getLength(); return null; } SenderEntry entry=send_table.get(dst); if(entry == null) { entry=new SenderEntry(getNewConnectionId(), this, timeout, timer, local_addr); SenderEntry existing=send_table.putIfAbsent(dst, entry); if(existing != null) entry=existing; else { if(log.isTraceEnabled()) log.trace(local_addr + ": created connection to " + dst); if(cache != null && !members.contains(dst)) cache.add(dst); } } long seqno=-2, send_conn_id=-1; UnicastHeader hdr; entry.lock(); // threads will only sync if they access the same entry try { seqno=entry.sent_msgs_seqno; send_conn_id=entry.send_conn_id; hdr=new UnicastHeader(UnicastHeader.DATA, seqno, send_conn_id, seqno == DEFAULT_FIRST_SEQNO); msg.putHeader(getName(), hdr); // AckSenderWindow.add() is costly as it calls Retransmitter.add() which calls TimeScheduler.schedule(), // which adds the scheduled task to a DelayQueue, which does costly tree rebalancing. // In 2.9 (or 3.0), we'll replace use of ScheduledThreadPoolExecutor in TimeScheduler with // a ConcurrentSkipListMap, which is faster (log(n) access cost for most ops). CSLM requires JDK 1.6 // Note that moving the next statement out of the lock scope made for some really ugly code, that's // why this was reverted ! entry.sent_msgs.add(seqno, msg); // add *including* UnicastHeader, adds to retransmitter entry.sent_msgs_seqno++; } finally { entry.unlock(); } if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" --> DATA(").append(dst).append(": #").append(seqno). append(", conn_id=").append(send_conn_id); if(hdr.first) sb.append(", first"); sb.append(')'); log.trace(sb); } // moved passing down of message out of the synchronized block: similar to NAKACK, we do *not* need // to send unicast messages in order of sequence numbers because they will be sorted into the correct // order at the receiver anyway. Of course, most of the time, the order will be correct (FIFO), so // the cost of reordering is minimal. This is part of http://jira.jboss.com/jira/browse/JGRP-303 try { // we catch the exception in this case because the msg is in the XMIT table and will be retransmitted send(msg, evt); } catch(Throwable t) { log.warn("failed sending the message", t); } return null; // we already passed the msg down case Event.VIEW_CHANGE: // remove connections to peers that are not members anymore ! View view=(View)evt.getArg(); Vector
    new_members=view.getMembers(); Set
    non_members=new HashSet
    (send_table.keySet()); non_members.addAll(recv_table.keySet()); synchronized(members) { members.clear(); if(new_members != null) members.addAll(new_members); non_members.removeAll(members); if(cache != null) { cache.removeAll(members); } } if(!non_members.isEmpty()) { if(log.isTraceEnabled()) log.trace("removing non members " + non_members); for(Address non_mbr: non_members) removeConnection(non_mbr); } break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.CONNECT: disconnected=false; break; case Event.DISCONNECT: disconnected=true; break; } return down_prot.down(evt); // Pass on to the layer below us } private void send(Message msg, Event evt) { down_prot.down(evt); num_msgs_sent++; num_bytes_sent+=msg.getLength(); } /** * Removes and resets from connection table (which is already locked). Returns true if member was found, * otherwise false. This method is public only so it can be invoked by unit testing, but should not otherwise be * used ! */ public void removeConnection(Address mbr) { SenderEntry entry=send_table.remove(mbr); if(entry != null) entry.reset(); ReceiverEntry entry2=recv_table.remove(mbr); if(entry2 != null) entry2.reset(); } /** * This method is public only so it can be invoked by unit testing, but should not otherwise be used ! */ protected void removeAllConnections() { for(SenderEntry entry: send_table.values()) entry.reset(); send_table.clear(); for(ReceiverEntry entry2: recv_table.values()) entry2.reset(); recv_table.clear(); } /** Called by AckSenderWindow to resend messages for which no ACK has been received yet */ public void retransmit(long seqno, Message msg) { if(log.isTraceEnabled()) log.trace(local_addr + " --> XMIT(" + msg.getDest() + ": #" + seqno + ')'); down_prot.down(new Event(Event.MSG, msg)); num_xmits++; } /** * Called by AgeOutCache, to removed expired connections * @param key */ public void expired(Address key) { if(key != null) { if(log.isDebugEnabled()) log.debug("removing connection to " + key + " because it expired"); removeConnection(key); } } /** * Check whether the hashtable contains an entry e for sender (create if not). If * e.received_msgs is null and first is true: create a new AckReceiverWindow(seqno) and * add message. Set e.received_msgs to the new window. Else just add the message. */ private void handleDataReceived(Address sender, long seqno, long conn_id, boolean first, Message msg) { if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder(); sb.append(local_addr).append(" <-- DATA(").append(sender).append(": #").append(seqno); if(conn_id != 0) sb.append(", conn_id=").append(conn_id); if(first) sb.append(", first"); sb.append(')'); log.trace(sb); } AckReceiverWindow win; Address send_request_for_first_seqno=null; synchronized(recv_table) { ReceiverEntry entry=recv_table.get(sender); win=entry != null? entry.received_msgs : null; if(first) { if(entry == null || win == null) { win=createReceiverWindow(sender, entry, seqno, conn_id); } else { // entry != null && win != null if(conn_id != entry.recv_conn_id) { if(log.isTraceEnabled()) log.trace(local_addr + ": conn_id=" + conn_id + " != " + entry.recv_conn_id + "; resetting receiver window"); win=createReceiverWindow(sender, entry, seqno, conn_id); } else { ; } } } else { // entry == null && win == null OR entry != null && win == null OR entry != null && win != null if(win == null || entry.recv_conn_id != conn_id) send_request_for_first_seqno=sender; // drops the message and returns (see below) } } if(send_request_for_first_seqno != null) { sendRequestForFirstSeqno(send_request_for_first_seqno); return; } byte result=win.add2(seqno, msg); // win is guaranteed to be non-null if we get here boolean added=result > 0; num_msgs_received++; num_bytes_received+=msg.getLength(); if(added && !msg.isFlagSet(Message.OOB)) undelivered_msgs.incrementAndGet(); // Cannot be replaced with if(!added), see https://jira.jboss.org/jira/browse/JGRP-1043 comment 15/Sep/09 06:57 AM // We *have* to do send the ACK, to cover the following scenario: // - A sends #3 to B // - B removes #3 and sends ACK(3) to A. B's next_to_remove is now 4 // - B's ACK(3) to A is dropped by the network // - A keeps retransmitting #3 to B, until it gets an ACK(3) // -B will never ACK #3 if the 2 lines below are commented ==> endless retransmission of A's #3 ! if(result == -1) { // only ack if seqno was < next_to_remove ! sendAck(msg.getSrc(), seqno); } // message is passed up if OOB. Later, when remove() is called, we discard it. This affects ordering ! // http://jira.jboss.com/jira/browse/JGRP-377 if(msg.isFlagSet(Message.OOB)) { if(added) up_prot.up(new Event(Event.MSG, msg)); Message oob_msg=win.removeOOBMessage(); if(!(undelivered_msgs.get() > 0 && win.hasMessagesToRemove())) { if(oob_msg != null) sendAckForMessage(oob_msg); return; } } final AtomicBoolean processing=win.getProcessing(); if(!processing.compareAndSet(false, true)) { return; } // Try to remove (from the AckReceiverWindow) as many messages as possible as pass them up boolean released_processing=false; int num_regular_msgs_removed=0; // Prevents concurrent passing up of messages by different threads (http://jira.jboss.com/jira/browse/JGRP-198); // this is all the more important once we have a threadless stack (http://jira.jboss.com/jira/browse/JGRP-181), // where lots of threads can come up to this point concurrently, but only 1 is allowed to pass at a time // We *can* deliver messages from *different* senders concurrently, e.g. reception of P1, Q1, P2, Q2 can result in // delivery of P1, Q1, Q2, P2: FIFO (implemented by UNICAST) says messages need to be delivered only in the // order in which they were sent by their senders try { while(true) { List msgs=win.removeMany(processing); if(msgs.isEmpty()) { released_processing=true; return; } Message highest_removed=msgs.get(msgs.size() -1); sendAckForMessage(highest_removed); // guaranteed not to throw an exception ! for(Message m: msgs) { // discard OOB msg: it has already been delivered (http://jira.jboss.com/jira/browse/JGRP-377) if(m.isFlagSet(Message.OOB)) continue; num_regular_msgs_removed++; try { up_prot.up(new Event(Event.MSG, m)); } catch(Throwable t) { log.error("couldn't deliver message " + m, t); } } } } finally { // We keep track of regular messages that we added, but couldn't remove (because of ordering). // When we have such messages pending, then even OOB threads will remove and process them // http://jira.jboss.com/jira/browse/JGRP-781 undelivered_msgs.addAndGet(-num_regular_msgs_removed); // double dutch: m == null always releases 'processing', however if remove() throws an exception we still // release 'processing' if(!released_processing) processing.set(false); } } private AckReceiverWindow createReceiverWindow(Address sender, ReceiverEntry entry, long seqno, long conn_id) { if(entry == null) { entry=new ReceiverEntry(); recv_table.put(sender, entry); } if(entry.received_msgs != null) entry.received_msgs.reset(); entry.received_msgs=new AckReceiverWindow(seqno); entry.recv_conn_id=conn_id; if(log.isTraceEnabled()) log.trace(local_addr + ": created receiver window for " + sender + " at seqno=#" + seqno + " for conn-id=" + conn_id); return entry.received_msgs; } /** Add the ACK to hashtable.sender.sent_msgs */ private void handleAckReceived(Address sender, long seqno) { if(log.isTraceEnabled()) log.trace(new StringBuilder().append(local_addr).append(" <-- ACK(").append(sender). append(": #").append(seqno).append(')')); SenderEntry entry=send_table.get(sender); AckSenderWindow win=entry != null? entry.sent_msgs : null; if(win != null) { win.ack(seqno); // removes message from retransmission num_acks_received++; } } /** * We need to resend our first message with our conn_id * @param sender */ private void handleResendingOfFirstMessage(Address sender) { if(log.isTraceEnabled()) log.trace(local_addr + " <-- SEND_FIRST_SEQNO(" + sender + ")"); SenderEntry entry=send_table.get(sender); AckSenderWindow win=entry != null? entry.sent_msgs : null; if(win == null) { if(log.isErrorEnabled()) log.error(local_addr + ": sender window for " + sender + " not found"); return; } Message rsp=win.getLowestMessage(); if(rsp == null) return; // We need to copy the UnicastHeader and put it back into the message because Message.copy() doesn't copy // the headers and therefore we'd modify the original message in the sender retransmission window // (https://jira.jboss.org/jira/browse/JGRP-965) Message copy=rsp.copy(); UnicastHeader hdr=(UnicastHeader)copy.getHeader(getName()); UnicastHeader newhdr=hdr.copy(); newhdr.first=true; copy.putHeader(getName(), newhdr); down_prot.down(new Event(Event.MSG, copy)); } private void sendAck(Address dst, long seqno) { if(disconnected) // if we are disconnected, then don't send any acks which throw exceptions on shutdown return; Message ack=new Message(dst); // commented Jan 23 2008 (bela): TP.enable_unicast_bundling should decide whether we bundle or not, and *not* // the OOB flag ! Bundling UNICAST ACKs should be really fast ack.setFlag(Message.OOB); ack.putHeader(getName(), new UnicastHeader(UnicastHeader.ACK, seqno)); if(log.isTraceEnabled()) log.trace(new StringBuilder().append(local_addr).append(" --> ACK(").append(dst). append(": #").append(seqno).append(')')); try { down_prot.down(new Event(Event.MSG, ack)); num_acks_sent++; } catch(Throwable t) { log.error("failed sending ACK(" + seqno + ") to " + dst, t); } } private void sendAckForMessage(Message msg) { UnicastHeader hdr=msg != null? (UnicastHeader)msg.getHeader(getName()) : null; Address sender=msg != null? msg.getSrc() : null; if(hdr != null && sender != null) sendAck(sender, hdr.seqno); } private long getNewConnectionId() { long retval=System.currentTimeMillis(); synchronized(this) { if(last_conn_id == retval) retval++; last_conn_id=retval; return retval; } } private void sendRequestForFirstSeqno(Address dest) { Message msg=new Message(dest); msg.setFlag(Message.OOB); UnicastHeader hdr=new UnicastHeader(UnicastHeader.SEND_FIRST_SEQNO, 0); msg.putHeader(getName(), hdr); if(log.isTraceEnabled()) log.trace(local_addr + " --> SEND_FIRST_SEQNO(" + dest + ")"); down_prot.down(new Event(Event.MSG, msg)); } public static class UnicastHeader extends Header implements Streamable { public static final byte DATA=0; public static final byte ACK=1; public static final byte SEND_FIRST_SEQNO = 2; byte type=DATA; long seqno=0; long conn_id=0; boolean first=false; static final int serialized_size=Global.BYTE_SIZE * 2 + Global.LONG_SIZE * 2; private static final long serialVersionUID=-8983745221189309298L; public UnicastHeader() {} // used for externalization public UnicastHeader(byte type, long seqno) { this.type=type; this.seqno=seqno; } public UnicastHeader(byte type, long seqno, long conn_id) { this(type, seqno); this.conn_id=conn_id; } public UnicastHeader(byte type, long seqno, long conn_id, boolean first) { this(type, seqno, conn_id); this.first=first; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("[UNICAST: ").append(type2Str(type)).append(", seqno=").append(seqno); if(conn_id != 0) sb.append(", conn_id=").append(conn_id); if(first) sb.append(", first"); sb.append(']'); return sb.toString(); } public static String type2Str(byte t) { switch(t) { case DATA: return "DATA"; case ACK: return "ACK"; case SEND_FIRST_SEQNO: return "SEND_FIRST_SEQNO"; default: return ""; } } public final int size() { return serialized_size; } public UnicastHeader copy() { return new UnicastHeader(type, seqno, conn_id, first); } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeLong(seqno); out.writeLong(conn_id); out.writeBoolean(first); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); seqno=in.readLong(); conn_id=in.readLong(); first=in.readBoolean(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); out.writeLong(seqno); out.writeLong(conn_id); out.writeBoolean(first); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); seqno=in.readLong(); conn_id=in.readLong(); first=in.readBoolean(); } } private static final class SenderEntry { // stores (and retransmits) msgs sent by us to a certain peer final AckSenderWindow sent_msgs; long sent_msgs_seqno=DEFAULT_FIRST_SEQNO; // seqno for msgs sent by us final long send_conn_id; final Lock lock=new ReentrantLock(); public SenderEntry(long send_conn_id, AckSenderWindow.RetransmitCommand cmd, long[] timeout, TimeScheduler timer, Address local_addr) { this.send_conn_id=send_conn_id; sent_msgs=new AckSenderWindow(cmd, new StaticInterval(timeout), timer, local_addr); } public void lock() { lock.lock(); } public void unlock() { lock.unlock(); } void reset() { if(sent_msgs != null) sent_msgs.reset(); sent_msgs_seqno=DEFAULT_FIRST_SEQNO; } public String toString() { StringBuilder sb=new StringBuilder(); if(sent_msgs != null) sb.append("sent_msgs=").append(sent_msgs).append('\n'); sb.append("send_conn_id=" + send_conn_id + "\n"); return sb.toString(); } } private static final class ReceiverEntry { AckReceiverWindow received_msgs=null; // stores all msgs rcvd by a certain peer in seqno-order long recv_conn_id=0; void reset() { if(received_msgs != null) received_msgs.reset(); } public String toString() { StringBuilder sb=new StringBuilder(); if(received_msgs != null) sb.append("received_msgs=").append(received_msgs).append('\n'); sb.append("recv_conn_id=" + recv_conn_id + "\n"); return sb.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/AuthHeader.java0000644000175000017500000000301211366547366026302 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.auth.AuthToken; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; /** * AuthHeader is a holder object for the token that is passed from the joiner to the coordinator * @author Chris Mills */ public class AuthHeader extends Header implements Streamable{ private transient AuthToken token=null; private static final long serialVersionUID=-1653071035656480519L; public AuthHeader(){ } /** * Sets the token value to that of the passed in token object * @param token the new authentication token */ public void setToken(AuthToken token){ this.token = token; } /** * Used to get the token from the AuthHeader * @return the token found inside the AuthHeader */ public AuthToken getToken(){ return this.token; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.token = (AuthToken)in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(this.token); } public void writeTo(DataOutputStream out) throws IOException { Util.writeAuthToken(this.token, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { this.token = Util.readAuthToken(in); } public int size(){ //need to fix this return Util.sizeOf(this); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/Discovery.java0000644000175000017500000004504511366547366026253 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.protocols.pbcast.JoinRsp; import org.jgroups.stack.Protocol; import org.jgroups.util.Promise; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.InterruptedIOException; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * The Discovery protocol layer retrieves the initial membership (used by the GMS when started * by sending event FIND_INITIAL_MBRS down the stack). We do this by specific subclasses, e.g. by mcasting PING * requests to an IP MCAST address or, if gossiping is enabled, by contacting the GossipRouter. * The responses should allow us to determine the coordinator whom we have to * contact, e.g. in case we want to join the group. When we are a server (after having * received the BECOME_SERVER event), we'll respond to PING requests with a PING * response.

    The FIND_INITIAL_MBRS event will eventually be answered with a * FIND_INITIAL_MBRS_OK event up the stack. * The following properties are available *

      *
    • timeout - the timeout (ms) to wait for the initial members, default is 3000=3 secs *
    • num_initial_members - the minimum number of initial members for a FIND_INITAL_MBRS, default is 2 *
    • num_ping_requests - the number of GET_MBRS_REQ messages to be sent (min=1), distributed over timeout ms *
    * @author Bela Ban * @version $Id: Discovery.java,v 1.32.2.11 2010/04/08 09:35:33 belaban Exp $ */ public abstract class Discovery extends Protocol { final Vector
    members=new Vector
    (11); Address local_addr=null; String group_addr=null; long timeout=3000; int num_initial_members=2; boolean is_server=false; TimeScheduler timer=null; /** Number of GET_MBRS_REQ messages to be sent (min=1), distributed over timeout ms */ int num_ping_requests=2; int num_discovery_requests=0; /** Minimum number of server responses (PingRsp.isServer()=true). If this value is greater than 0, we'll ignore num_initial_members */ int num_initial_srv_members=0; /** Return from the discovery phase as soon as we have 1 coordinator response */ boolean break_on_coord_rsp=true; private final Set ping_responses=new HashSet(); private final PingSenderTask sender=new PingSenderTask(); public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); } /** Called after local_addr was set */ public void localAddressSet(Address addr) { } public abstract void sendGetMembersRequest(Promise promise) throws Exception; public void handleDisconnect() { } public void handleConnect() { } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { this.timeout=timeout; } public int getNumInitialMembers() { return num_initial_members; } public void setNumInitialMembers(int num_initial_members) { this.num_initial_members=num_initial_members; } public int getNumPingRequests() { return num_ping_requests; } public void setNumPingRequests(int num_ping_requests) { this.num_ping_requests=num_ping_requests; } public int getNumberOfDiscoveryRequestsSent() { return num_discovery_requests; } public Vector providedUpServices() { Vector ret=new Vector(1); ret.addElement(new Integer(Event.FIND_INITIAL_MBRS)); return ret; } /** * sets the properties of the PING protocol. * The following properties are available * property: timeout - the timeout (ms) to wait for the initial members, default is 3000=3 secs * property: num_initial_members - the minimum number of initial members for a FIND_INITAL_MBRS, default is 2 * @param props - a property set * @return returns true if all properties were parsed properly * returns false if there are unrecnogized properties in the property set */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("timeout"); // max time to wait for initial members if(str != null) { timeout=Long.parseLong(str); if(timeout <= 0) { if(log.isErrorEnabled()) log.error("timeout must be > 0"); return false; } props.remove("timeout"); } str=props.getProperty("num_initial_members"); // wait for at most n members if(str != null) { num_initial_members=Integer.parseInt(str); props.remove("num_initial_members"); } str=props.getProperty("num_initial_srv_members"); // wait for at most n server members if(str != null) { num_initial_srv_members=Integer.parseInt(str); props.remove("num_initial_srv_members"); } str=props.getProperty("break_on_coord_rsp"); // terminate discovery phase when we receive 1 response from a coord if(str != null) { break_on_coord_rsp=Boolean.parseBoolean(str); props.remove("break_on_coord_rsp"); } str=props.getProperty("num_ping_requests"); // number of GET_MBRS_REQ messages if(str != null) { num_ping_requests=Integer.parseInt(str); props.remove("num_ping_requests"); if(num_ping_requests < 1) num_ping_requests=1; } if(!props.isEmpty()) { StringBuilder sb=new StringBuilder(); for(Enumeration e=props.propertyNames(); e.hasMoreElements();) { sb.append(e.nextElement().toString()); if(e.hasMoreElements()) { sb.append(", "); } } if(log.isErrorEnabled()) log.error("The following properties are not recognized: " + sb); return false; } return true; } public void resetStats() { super.resetStats(); num_discovery_requests=0; } public void start() throws Exception { super.start(); } public void stop() { is_server=false; } /** * Finds the initial membership: sends a GET_MBRS_REQ to all members, waits 'timeout' ms or * until 'num_initial_members' have been retrieved * @return List */ public List findInitialMembers(Promise promise) { num_discovery_requests++; final Responses rsps=new Responses(num_initial_members, num_initial_srv_members, break_on_coord_rsp, promise); synchronized(ping_responses) { ping_responses.add(rsps); } sender.start(promise); try { return rsps.get(timeout); } catch(Exception e) { return new LinkedList(); } finally { sender.stop(); synchronized(ping_responses) { ping_responses.remove(rsps); } } } public String findInitialMembersAsString() { List results=findInitialMembers(null); if(results == null || results.isEmpty()) return ""; StringBuilder sb=new StringBuilder(); for(PingRsp rsp: results) { sb.append(rsp).append("\n"); } return sb.toString(); } /** * An event was received from the layer below. Usually the current layer will want to examine * the event type and - depending on its type - perform some computation * (e.g. removing headers from a MSG event type, or updating the internal membership list * when receiving a VIEW_CHANGE event). * Finally the event is either a) discarded, or b) an event is sent down * the stack using PassDown or c) the event (or another event) is sent up * the stack using PassUp. *

    * For the PING protocol, the Up operation does the following things. * 1. If the event is a Event.MSG then PING will inspect the message header. * If the header is null, PING simply passes up the event * If the header is PingHeader.GET_MBRS_REQ then the PING protocol * will PassDown a PingRequest message * If the header is PingHeader.GET_MBRS_RSP we will add the message to the initial members * vector and wake up any waiting threads. * 2. If the event is Event.SET_LOCAL_ADDR we will simple set the local address of this protocol * 3. For all other messages we simple pass it up to the protocol above * * @param evt - the event that has been sent from the layer below */ public Object up(Event evt) { Message msg, rsp_msg; PingHeader rsp_hdr; PingRsp rsp; Address coord; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); PingHeader hdr=(PingHeader)msg.getHeader(getName()); if(hdr == null) { return up_prot.up(evt); } switch(hdr.type) { case PingHeader.GET_MBRS_REQ: // return Rsp(local_addr, coord) if(local_addr != null && msg.getSrc() != null && local_addr.equals(msg.getSrc())) { return null; } synchronized(members) { coord=!members.isEmpty()? members.firstElement() : local_addr; } PingRsp ping_rsp=new PingRsp(local_addr, coord, is_server); rsp_msg=new Message(msg.getSrc(), null, null); rsp_msg.setFlag(Message.OOB); rsp_hdr=new PingHeader(PingHeader.GET_MBRS_RSP, ping_rsp); rsp_msg.putHeader(getName(), rsp_hdr); if(log.isTraceEnabled()) log.trace("received GET_MBRS_REQ from " + msg.getSrc() + ", sending response " + rsp_hdr); down_prot.down(new Event(Event.MSG, rsp_msg)); return null; case PingHeader.GET_MBRS_RSP: // add response to vector and notify waiting thread rsp=hdr.arg; if(log.isTraceEnabled()) log.trace("received GET_MBRS_RSP, rsp=" + rsp); // myfuture.addResponse(rsp); synchronized(ping_responses) { for(Responses rsps: ping_responses) rsps.addResponse(rsp); } return null; default: if(log.isWarnEnabled()) log.warn("got PING header with unknown type (" + hdr.type + ')'); return null; } case Event.SET_LOCAL_ADDRESS: up_prot.up(evt); local_addr=(Address)evt.getArg(); localAddressSet(local_addr); break; default: up_prot.up(evt); // Pass up to the layer above us break; } return null; } /** * An event is to be sent down the stack. The layer may want to examine its type and perform * some action on it, depending on the event's type. If the event is a message MSG, then * the layer may need to add a header to it (or do nothing at all) before sending it down * the stack using PassDown. In case of a GET_ADDRESS event (which tries to * retrieve the stack's address from one of the bottom layers), the layer may need to send * a new response event back up the stack using up_prot.up(). * The PING protocol is interested in several different down events, * Event.FIND_INITIAL_MBRS - sent by the GMS layer and expecting a GET_MBRS_OK * Event.TMP_VIEW and Event.VIEW_CHANGE - a view change event * Event.BECOME_SERVER - called after client has joined and is fully working group member * Event.CONNECT, Event.DISCONNECT. */ public Object down(Event evt) { switch(evt.getType()) { case Event.FIND_INITIAL_MBRS: // sent by GMS layer, pass up a GET_MBRS_OK event // sends the GET_MBRS_REQ to all members, waits 'timeout' ms or until 'num_initial_members' have been retrieved long start=System.currentTimeMillis(); List rsps=findInitialMembers((Promise)evt.getArg()); long diff=System.currentTimeMillis() - start; if(log.isTraceEnabled()) log.trace("discovery took "+ diff + " ms: responses: " + Util.printPingRsps(rsps)); return rsps; case Event.TMP_VIEW: case Event.VIEW_CHANGE: Vector

    tmp; if((tmp=((View)evt.getArg()).getMembers()) != null) { synchronized(members) { members.clear(); members.addAll(tmp); } } return down_prot.down(evt); case Event.BECOME_SERVER: // called after client has joined and is fully working group member down_prot.down(evt); is_server=true; return null; case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: group_addr=(String)evt.getArg(); Object ret=down_prot.down(evt); handleConnect(); return ret; case Event.DISCONNECT: handleDisconnect(); return down_prot.down(evt); default: return down_prot.down(evt); // Pass on to the layer below us } } /* -------------------------- Private methods ---------------------------- */ @SuppressWarnings("unchecked") protected final View makeView(Vector mbrs) { Address coord; long id; ViewId view_id=new ViewId(local_addr); coord=view_id.getCoordAddress(); id=view_id.getId(); return new View(coord, id, mbrs); } class PingSenderTask { private Future senderFuture; public PingSenderTask() {} public synchronized void start(final Promise promise) { long delay = (long)(timeout / (double)num_ping_requests); if(senderFuture == null || senderFuture.isDone()) { senderFuture=timer.scheduleWithFixedDelay(new Runnable() { public void run() { try { sendGetMembersRequest(promise); } catch(InterruptedIOException ie) { ; } catch(InterruptedException ex) { ; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed sending discovery request", ex); } } }, 0, delay, TimeUnit.MILLISECONDS); } } public synchronized void stop() { if(senderFuture != null) { senderFuture.cancel(true); senderFuture=null; } } } private static class Responses { final Promise promise; final List ping_rsps=new LinkedList(); final int num_expected_rsps; final int num_expected_srv_rsps; final boolean break_on_coord_rsp; public Responses(int num_expected_rsps, int num_expected_srv_rsps, boolean break_on_coord_rsp, Promise promise) { this.num_expected_rsps=num_expected_rsps; this.num_expected_srv_rsps=num_expected_srv_rsps; this.break_on_coord_rsp=break_on_coord_rsp; this.promise=promise != null? promise : new Promise(); } public void addResponse(PingRsp rsp) { if(rsp == null) return; promise.getLock().lock(); try { // https://jira.jboss.org/jira/browse/JGRP-1179 int index=ping_rsps.indexOf(rsp); if(index == -1) { ping_rsps.add(rsp); promise.getCond().signalAll(); } else if(rsp.isCoord()) { PingRsp pr=ping_rsps.get(index); // Check if the already existing element is not server if(!pr.isCoord()) { ping_rsps.set(index, rsp); promise.getCond().signalAll(); } } } finally { promise.getLock().unlock(); } } public List get(long timeout) throws InterruptedException { long start_time=System.currentTimeMillis(), time_to_wait=timeout; promise.getLock().lock(); try { while(time_to_wait > 0 && !promise.hasResult()) { // if num_expected_srv_rsps > 0, then it overrides num_expected_rsps if(num_expected_srv_rsps > 0) { int received_srv_rsps=getNumServerResponses(ping_rsps); if(received_srv_rsps >= num_expected_srv_rsps) return new LinkedList(ping_rsps); } else if(ping_rsps.size() >= num_expected_rsps) { return new LinkedList(ping_rsps); } if(break_on_coord_rsp && containsCoordinatorResponse(ping_rsps)) return new LinkedList(ping_rsps); promise.getCond().await(time_to_wait, TimeUnit.MILLISECONDS); time_to_wait=timeout - (System.currentTimeMillis() - start_time); } return new LinkedList(ping_rsps); } finally { promise.getLock().unlock(); } } private static int getNumServerResponses(List rsps) { int cnt=0; for(PingRsp rsp: rsps) { if(rsp.isServer()) cnt++; } return cnt; } private static boolean containsCoordinatorResponse(List rsps) { if(rsps == null || rsps.isEmpty()) return false; for(PingRsp rsp: rsps) { if(rsp.isCoord()) return true; } return false; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TCP.java0000644000175000017500000001151111366547366024721 0ustar twernertwerner// $Id: TCP.java,v 1.44.2.3 2008/05/22 13:23:06 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.blocks.ConnectionTable; import org.jgroups.stack.IpAddress; import org.jgroups.util.PortsManager; import java.net.InetAddress; import java.util.Collection; import java.util.Properties; /** * TCP based protocol. Creates a server socket, which gives us the local address of this group member. For * each accept() on the server socket, a new thread is created that listens on the socket. * For each outgoing message m, if m.dest is in the ougoing hashtable, the associated socket will be reused * to send message, otherwise a new socket is created and put in the hashtable. * When a socket connection breaks or a member is removed from the group, the corresponding items in the * incoming and outgoing hashtables will be removed as well.
    * This functionality is in ConnectionTable, which isT used by TCP. TCP sends messages using ct.send() and * registers with the connection table to receive all incoming messages. * @author Bela Ban */ public class TCP extends BasicTCP implements ConnectionTable.Receiver { // , BasicConnectionTable.ConnectionListener { private ConnectionTable ct=null; public TCP() { } public String getName() { return "TCP"; } public int getOpenConnections() {return ct.getNumConnections();} public String printConnections() {return ct.toString();} /** Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { super.setProperties(props); if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void send(Address dest, byte[] data, int offset, int length) throws Exception { ct.send(dest, data, offset, length); } public void retainAll(Collection
    members) { ct.retainAll(members); } public void start() throws Exception { ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port,pm); // ct.addConnectionListener(this); ct.setUseSendQueues(use_send_queues); ct.setSendQueueSize(send_queue_size); // ct.addConnectionListener(this); ct.setReceiveBufferSize(recv_buf_size); ct.setSendBufferSize(send_buf_size); ct.setSocketConnectionTimeout(sock_conn_timeout); ct.setPeerAddressReadTimeout(peer_addr_read_timeout); ct.setTcpNodelay(tcp_nodelay); ct.setLinger(linger); local_addr=ct.getLocalAddress(); if(additional_data != null && local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(additional_data); //http://jira.jboss.com/jira/browse/JGRP-626 //we first start threads in TP super.start(); //and then we kick off acceptor thread for CT ct.start(); } public void stop() { //and for stopping we do vice versa ct.stop(); super.stop(); } /** * @param reaperInterval * @param connExpireTime * @param bindAddress * @param startPort * @throws Exception * @return ConnectionTable * Sub classes overrides this method to initialize a different version of * ConnectionTable. */ protected ConnectionTable getConnectionTable(long reaperInterval, long connExpireTime, InetAddress bindAddress, InetAddress externalAddress, int startPort, int endPort, PortsManager pm) throws Exception { ConnectionTable cTable; if(reaperInterval == 0 && connExpireTime == 0) { cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort, pm); } else { if(reaperInterval == 0) { reaperInterval=5000; if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + reaperInterval); } if(connExpireTime == 0) { connExpireTime=1000 * 60 * 5; if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + connExpireTime); } cTable=new ConnectionTable(this, bindAddress, externalAddress, startPort, endPort, reaperInterval, connExpireTime, pm); } cTable.setThreadFactory(getThreadFactory()); return cTable; } // public void connectionOpened(Address peer_addr) { // } // // public void connectionClosed(Address peer_addr) { // if(log.isTraceEnabled()) // log.trace("removing connection to " + peer_addr + " from connection table as peer closed connection"); // ct.remove(peer_addr); // } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FD_SIMPLE.java0000644000175000017500000002670011366547366025643 0ustar twernertwerner// $Id: FD_SIMPLE.java,v 1.19.2.2 2009/02/05 09:28:37 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.Promise; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Streamable; import java.io.*; import java.util.HashMap; import java.util.Iterator; import java.util.Properties; import java.util.Vector; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Simple failure detection protocol. Periodically sends a are-you-alive message to a randomly chosen member * (excluding itself) and waits for a response. If a response has not been received within timeout msecs, a counter * associated with that member will be incremented. If the counter exceeds max_missed_hbs, that member will be * suspected. When a message or a heartbeat are received, the counter is reset to 0. * * @author Bela Ban Aug 2002 * @version $Revision: 1.19.2.2 $ */ public class FD_SIMPLE extends Protocol { Address local_addr=null; TimeScheduler timer=null; final Lock heartbeat_lock=new ReentrantLock(); @GuardedBy("heartbeat_lock") Future heartbeat_future=null; HeartbeatTask task; long interval=3000; // interval in msecs between are-you-alive messages long timeout=3000; // time (in msecs) to wait for a response to are-you-alive final Vector members=new Vector(); final HashMap counters=new HashMap(); // keys=Addresses, vals=Integer (count) int max_missed_hbs=5; // max number of missed responses until a member is suspected static final String name="FD_SIMPLE"; public String getName() { return "FD_SIMPLE"; } public void init() throws Exception { timer=getTransport().getTimer(); } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("timeout"); if(str != null) { timeout=Long.parseLong(str); props.remove("timeout"); } str=props.getProperty("interval"); if(str != null) { interval=Long.parseLong(str); props.remove("interval"); } str=props.getProperty("max_missed_hbs"); if(str != null) { max_missed_hbs=Integer.parseInt(str); props.remove("max_missed_hbs"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void stop() { heartbeat_lock.lock(); try { if(heartbeat_future != null) { heartbeat_future.cancel(true); // we need to interrupt the thread as it may call wait() heartbeat_future=null; task=null; } } finally { heartbeat_lock.unlock(); } } public Object up(Event evt) { Message msg, rsp; Address sender; FdHeader hdr=null; boolean counter_reset=false; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: msg=(Message)evt.getArg(); sender=msg.getSrc(); resetCounter(sender); counter_reset=true; hdr=(FdHeader)msg.getHeader(name); if(hdr == null) break; switch(hdr.type) { case FdHeader.ARE_YOU_ALIVE: // are-you-alive request, send i-am-alive response rsp=new Message(sender); rsp.putHeader(name, new FdHeader(FdHeader.I_AM_ALIVE)); down_prot.down(new Event(Event.MSG, rsp)); return null; // don't pass up further case FdHeader.I_AM_ALIVE: if(log.isInfoEnabled()) log.info("received I_AM_ALIVE response from " + sender); heartbeat_lock.lock(); try { if(task != null) task.receivedHeartbeatResponse(sender); } finally { heartbeat_lock.unlock(); } if(!counter_reset) resetCounter(sender); return null; default: if(log.isWarnEnabled()) log.warn("FdHeader type " + hdr.type + " not known"); return null; } } return up_prot.up(evt); // pass up to the layer above us } public Object down(Event evt) { View new_view; Address key; switch(evt.getType()) { // Start heartbeat thread when we have more than 1 member; stop it when membership drops below 2 case Event.VIEW_CHANGE: new_view=(View)evt.getArg(); members.clear(); members.addAll(new_view.getMembers()); if(new_view.size() > 1) { heartbeat_lock.lock(); try { if(heartbeat_future == null || heartbeat_future.isDone()) { task=new HeartbeatTask(); if(log.isInfoEnabled()) log.info("starting heartbeat task"); heartbeat_future=timer.scheduleWithFixedDelay(task, interval, interval, TimeUnit.MILLISECONDS); } } finally { heartbeat_lock.unlock(); } } else { heartbeat_lock.lock(); try { if(heartbeat_future != null) { if(log.isInfoEnabled()) log.info("stopping heartbeat task"); heartbeat_future.cancel(true); heartbeat_future=null; task=null; } } finally { heartbeat_lock.unlock(); } } // remove all keys from 'counters' which are not in this new view synchronized(counters) { for(Iterator it=counters.keySet().iterator(); it.hasNext();) { key=(Address)it.next(); if(!members.contains(key)) { if(log.isInfoEnabled()) log.info("removing " + key + " from counters"); it.remove(); } } } } return down_prot.down(evt); } /* -------------------------------- Private Methods ------------------------------- */ Address getHeartbeatDest() { Address retval=null; int r, size; Vector members_copy; if(members == null || members.size() < 2 || local_addr == null) return null; members_copy=(Vector)members.clone(); members_copy.removeElement(local_addr); // don't select myself as heartbeat destination size=members_copy.size(); r=((int)(Math.random() * (size + 1))) % size; retval=(Address)members_copy.elementAt(r); return retval; } int incrementCounter(Address mbr) { Integer cnt; int ret=0; if(mbr == null) return ret; synchronized(counters) { cnt=(Integer)counters.get(mbr); if(cnt == null) { cnt=new Integer(0); counters.put(mbr, cnt); } else { ret=cnt.intValue() + 1; counters.put(mbr, new Integer(ret)); } return ret; } } void resetCounter(Address mbr) { if(mbr == null) return; synchronized(counters) { counters.put(mbr, new Integer(0)); } } String printCounters() { StringBuilder sb=new StringBuilder(); Address key; synchronized(counters) { for(Iterator it=counters.keySet().iterator(); it.hasNext();) { key=(Address)it.next(); sb.append(key).append(": ").append(counters.get(key)).append('\n'); } } return sb.toString(); } /* ----------------------------- End of Private Methods --------------------------- */ public static class FdHeader extends Header implements Streamable { static final byte ARE_YOU_ALIVE=1; // sent periodically to a random member static final byte I_AM_ALIVE=2; // response to above message byte type=ARE_YOU_ALIVE; private static final long serialVersionUID=4021056597004641352L; public FdHeader() { } // used for externalization FdHeader(byte type) { this.type=type; } public String toString() { switch(type) { case ARE_YOU_ALIVE: return "[FD_SIMPLE: ARE_YOU_ALIVE]"; case I_AM_ALIVE: return "[FD_SIMPLE: I_AM_ALIVE]"; default: return "[FD_SIMPLE: unknown type (" + type + ")]"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); } public int size() { return Global.BYTE_SIZE; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); } } class HeartbeatTask implements Runnable { final Promise
    promise=new Promise
    (); Address dest=null; public void receivedHeartbeatResponse(Address from) { if(from != null && dest != null && from.equals(dest)) promise.setResult(from); } public void run() { Message msg; int num_missed_hbs=0; dest=getHeartbeatDest(); if(dest == null) { if(log.isWarnEnabled()) log.warn("heartbeat destination was null, will not send ARE_YOU_ALIVE message"); return; } if(log.isInfoEnabled()) log.info("sending ARE_YOU_ALIVE message to " + dest + ", counters are\n" + printCounters()); promise.reset(); msg=new Message(dest); msg.putHeader(name, new FdHeader(FdHeader.ARE_YOU_ALIVE)); down_prot.down(new Event(Event.MSG, msg)); promise.getResult(timeout); num_missed_hbs=incrementCounter(dest); if(num_missed_hbs >= max_missed_hbs) { if(log.isInfoEnabled()) log.info("missed " + num_missed_hbs + " from " + dest + ", suspecting member"); up_prot.up(new Event(Event.SUSPECT, dest)); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/SEQUENCER.java0000644000175000017500000003503211366547366025631 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.SeqnoTable; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.*; import java.util.concurrent.atomic.AtomicLong; /** * Implementation of total order protocol using a sequencer. Consult doc/design/SEQUENCER.txt for details * @author Bela Ban * @version $Id: SEQUENCER.java,v 1.20.2.1 2009/12/15 17:08:38 belaban Exp $ */ public class SEQUENCER extends Protocol { private Address local_addr=null, coord=null; private final Collection
    members=new ArrayList
    (); static final String name="SEQUENCER"; private volatile boolean is_coord=false; private AtomicLong seqno=new AtomicLong(0); /** Maintains messages forwarded to the coord which which no ack has been received yet. * Needs to be sorted so we resend them in the right order */ private final Map forward_table=new TreeMap(); /** Map: maintains the highest seqnos seen for a given member */ private final SeqnoTable received_table=new SeqnoTable(0); private long forwarded_msgs=0; private long bcast_msgs=0; private long received_forwards=0; private long received_bcasts=0; public boolean isCoordinator() {return is_coord;} public Address getCoordinator() {return coord;} public Address getLocalAddress() {return local_addr;} public String getName() {return name;} public long getForwarded() {return forwarded_msgs;} public long getBroadcast() {return bcast_msgs;} public long getReceivedForwards() {return received_forwards;} public long getReceivedBroadcasts() {return received_bcasts;} public void resetStats() { forwarded_msgs=bcast_msgs=received_forwards=received_bcasts=0L; } public Map dumpStats() { Map m=super.dumpStats(); if(m == null) m=new HashMap(); m.put("forwarded", new Long(forwarded_msgs)); m.put("broadcast", new Long(bcast_msgs)); m.put("received_forwards", new Long(received_forwards)); m.put("received_bcasts", new Long(received_bcasts)); return m; } public String printStats() { return dumpStats().toString(); } public boolean setProperties(Properties props) { super.setProperties(props); if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest == null || dest.isMulticastAddress()) { // only handle multicasts long next_seqno=seqno.getAndIncrement(); if(is_coord) { SequencerHeader hdr=new SequencerHeader(SequencerHeader.BCAST, local_addr, next_seqno); msg.putHeader(name, hdr); broadcast(msg, false); // don't copy, just use the message passed as argument } else { // SequencerHeader hdr=new SequencerHeader(SequencerHeader.FORWARD, local_addr, next_seqno); // msg.putHeader(name, hdr); forwardToCoord(msg, next_seqno); } return null; // don't pass down } break; case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return down_prot.down(evt); } public Object up(Event evt) { Message msg; SequencerHeader hdr; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: msg=(Message)evt.getArg(); hdr=(SequencerHeader)msg.getHeader(name); if(hdr == null) break; // pass up switch(hdr.type) { case SequencerHeader.FORWARD: if(!is_coord) { if(log.isErrorEnabled()) log.error(local_addr + ": non-coord; dropping FORWARD request from " + msg.getSrc()); return null; } broadcast(msg, true); // do copy the message received_forwards++; return null; case SequencerHeader.BCAST: deliver(msg, evt, hdr); received_bcasts++; return null; case SequencerHeader.WRAPPED_BCAST: unwrapAndDeliver(msg); // unwrap the original message (in the payload) and deliver it received_bcasts++; return null; } break; case Event.VIEW_CHANGE: Object retval=up_prot.up(evt); handleViewChange((View)evt.getArg()); return retval; case Event.SUSPECT: handleSuspect((Address)evt.getArg()); break; } return up_prot.up(evt); } /* --------------------------------- Private Methods ----------------------------------- */ private void handleViewChange(View v) { Vector
    mbrs=v.getMembers(); if(mbrs.isEmpty()) return; boolean coord_changed=false; synchronized(this) { members.clear(); members.addAll(mbrs); Address prev_coord=coord; coord=mbrs.firstElement(); is_coord=local_addr != null && local_addr.equals(coord); coord_changed=prev_coord != null && !prev_coord.equals(coord); } if(coord_changed) { resendMessagesInForwardTable(); // maybe optimize in the future: broadcast directly if coord } // remove left members from received_table received_table.retainAll(mbrs); } private void handleSuspect(Address suspected_mbr) { boolean coord_changed=false; if(suspected_mbr == null) return; synchronized(this) { List
    non_suspected_mbrs=new ArrayList
    (members); non_suspected_mbrs.remove(suspected_mbr); if(!non_suspected_mbrs.isEmpty()) { Address prev_coord=coord; coord=non_suspected_mbrs.get(0); is_coord=local_addr != null && local_addr.equals(coord); coord_changed=prev_coord != null && !prev_coord.equals(coord); } } if(coord_changed) { resendMessagesInForwardTable(); // maybe optimize in the future: broadcast directly if coord } } /** * Sends all messages currently in forward_table to the new coordinator (changing the dest field). * This needs to be done, so the underlying reliable unicast protocol (e.g. UNICAST) adds these messages * to its retransmission mechanism
    * Note that we need to resend the messages in order of their seqnos ! We also need to prevent other message * from being inserted until we're done, that's why there's synchronization. */ private void resendMessagesInForwardTable() { Map copy; synchronized(forward_table) { copy=new TreeMap(forward_table); } for(Map.Entry entry: copy.entrySet()) { Long key=entry.getKey(); byte[] val=entry.getValue(); Message forward_msg=new Message(coord, null, val); SequencerHeader hdr=new SequencerHeader(SequencerHeader.FORWARD, local_addr, key); forward_msg.putHeader(name, hdr); if (log.isTraceEnabled()) { log.trace("resending msg " + local_addr + "::" + key + " to coord (" + coord + ")"); } down_prot.down(new Event(Event.MSG, forward_msg)); } } private void forwardToCoord(final Message msg, long seqno) { msg.setSrc(local_addr); if(log.isTraceEnabled()) log.trace("forwarding msg " + msg + " (seqno " + seqno + ") to coord (" + coord + ")"); byte[] marshalled_msg; try { marshalled_msg=Util.objectToByteBuffer(msg); synchronized(forward_table) { forward_table.put(seqno, marshalled_msg); } Message forward_msg=new Message(coord, null, marshalled_msg); SequencerHeader hdr=new SequencerHeader(SequencerHeader.FORWARD, local_addr, seqno); forward_msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, forward_msg)); forwarded_msgs++; } catch(Exception e) { log.error("failed marshalling message", e); } } private void broadcast(final Message msg, boolean copy) { Message bcast_msg=null; final SequencerHeader hdr=(SequencerHeader)msg.getHeader(name); if(!copy) { bcast_msg=msg; // no need to add a header, message already has one } else { bcast_msg=new Message(null, local_addr, msg.getRawBuffer(), msg.getOffset(), msg.getLength()); SequencerHeader new_hdr=new SequencerHeader(SequencerHeader.WRAPPED_BCAST, hdr.getOriginalSender(), hdr.getSeqno()); bcast_msg.putHeader(name, new_hdr); } if(log.isTraceEnabled()) log.trace("broadcasting msg " + bcast_msg + " (seqno " + hdr.getSeqno() + ")"); down_prot.down(new Event(Event.MSG, bcast_msg)); bcast_msgs++; } /** * Unmarshal the original message (in the payload) and then pass it up (unless already delivered) * @param msg */ private void unwrapAndDeliver(final Message msg) { try { SequencerHeader hdr=(SequencerHeader)msg.getHeader(name); Message msg_to_deliver=(Message)Util.objectFromByteBuffer(msg.getRawBuffer(), msg.getOffset(), msg.getLength()); long msg_seqno=hdr.getSeqno(); if(!canDeliver(msg_to_deliver.getSrc(), msg_seqno)) return; if(log.isTraceEnabled()) log.trace("delivering msg " + msg_to_deliver + " (seqno " + msg_seqno + "), original sender " + msg_to_deliver.getSrc()); up_prot.up(new Event(Event.MSG, msg_to_deliver)); } catch(Exception e) { log.error("failure unmarshalling buffer", e); } } private void deliver(Message msg, Event evt, SequencerHeader hdr) { Address sender=msg.getSrc(); if(sender == null) { if(log.isErrorEnabled()) log.error("sender is null, cannot deliver msg " + msg); return; } long msg_seqno=hdr.getSeqno(); if(!canDeliver(sender, msg_seqno)) return; if(log.isTraceEnabled()) log.trace("delivering msg " + msg + " (seqno " + msg_seqno + "), sender " + sender); up_prot.up(evt); } private boolean canDeliver(Address sender, long seqno) { // this is the ack for the message sent by myself if(sender.equals(local_addr)) { synchronized(forward_table) { forward_table.remove(seqno); } } // if msg was already delivered, discard it boolean added=received_table.add(sender, seqno); if(!added) { if(log.isWarnEnabled()) log.warn("seqno (" + sender + "::" + seqno + " has already been received " + "(highest received=" + received_table.getHighestReceived(sender) + "); discarding duplicate message"); } return added; } /* ----------------------------- End of Private Methods -------------------------------- */ public static class SequencerHeader extends Header implements Streamable { private static final byte FORWARD = 1; private static final byte BCAST = 2; private static final byte WRAPPED_BCAST = 3; private static final long serialVersionUID=-4656046501158108558L; byte type=-1; /** the original sender's address and a seqno */ ViewId tag=null; public SequencerHeader() { } public SequencerHeader(byte type, Address original_sender, long seqno) { this.type=type; this.tag=new ViewId(original_sender, seqno); } public Address getOriginalSender() { return tag != null? tag.getCoordAddress() : null; } public long getSeqno() { return tag != null? tag.getId() : -1; } public String toString() { StringBuilder sb=new StringBuilder(64); sb.append(printType()); if(tag != null) sb.append(" (tag=").append(tag).append(")"); return sb.toString(); } private final String printType() { switch(type) { case FORWARD: return "FORWARD"; case BCAST: return "BCAST"; case WRAPPED_BCAST: return "WRAPPED_BCAST"; default: return "n/a"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeObject(tag); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); tag=(ViewId)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); Util.writeStreamable(tag, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); tag=(ViewId)Util.readStreamable(ViewId.class, in); } public int size() { int size=Global.BYTE_SIZE *2; // type + presence byte if(tag != null) size+=tag.serializedSize(); return size; } } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/BasicTCP.java0000644000175000017500000002315111366547366025666 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.Util; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collection; import java.util.Properties; import java.util.Set; /** * Shared base class for tcpip protocols * @author Scott Marlow */ public abstract class BasicTCP extends TP { /** Should we drop unicast messages to suspected members or not */ boolean skip_suspected_members=true; /** When we cannot send a message to P (on an exception), then we send a SUSPECT message up the stack */ boolean suspect_on_send_failure=false; /** List the maintains the currently suspected members. This is used so we don't send too many SUSPECT * events up the stack (one per message !) */ final BoundedList
    suspected_mbrs=new BoundedList
    (20); protected InetAddress external_addr=null; // the IP address which is broadcast to other group members protected int start_port=7800; // find first available port starting at this port protected int end_port=0; // maximum port to bind to protected long reaper_interval=0; // time in msecs between connection reaps protected long conn_expire_time=0; // max time a conn can be idle before being reaped /** Use separate send queues for each connection */ boolean use_send_queues=true; int send_queue_size=10000; // max number of messages in a send queue int recv_buf_size=150000; int send_buf_size=150000; int sock_conn_timeout=2000; // max time in millis for a socket creation in ConnectionTable int peer_addr_read_timeout=1000; // max time to block on reading of peer address boolean tcp_nodelay=false; int linger=-1; // SO_LINGER (number of ms, -1 disables it) public int getStartPort() {return start_port;} public void setStartPort(int start_port) {this.start_port=start_port;} public int getEndPort() {return end_port;} public void setEndPort(int end_port) {this.end_port=end_port;} public long getReaperInterval() {return reaper_interval;} public void setReaperInterval(long reaper_interval) {this.reaper_interval=reaper_interval;} public long getConnExpireTime() {return conn_expire_time;} public void setConnExpireTime(long conn_expire_time) {this.conn_expire_time=conn_expire_time;} public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("start_port"); if(str != null) { start_port=Integer.parseInt(str); props.remove("start_port"); } //https://jira.jboss.org/jira/browse/JGRP-865 str=props.getProperty("bind_port"); if(str != null) { start_port=Integer.parseInt(str); props.remove("bind_port"); } str=props.getProperty("end_port"); if(str != null) { end_port=Integer.parseInt(str); props.remove("end_port"); } str=props.getProperty("external_addr"); if(str != null) { try { external_addr=InetAddress.getByName(str); } catch(UnknownHostException unknown) { if(log.isFatalEnabled()) log.fatal("(external_addr): host " + str + " not known"); return false; } props.remove("external_addr"); } str=props.getProperty("reaper_interval"); if(str != null) { reaper_interval=Long.parseLong(str); props.remove("reaper_interval"); } str=props.getProperty("conn_expire_time"); if(str != null) { conn_expire_time=Long.parseLong(str); props.remove("conn_expire_time"); } str=props.getProperty("sock_conn_timeout"); if(str != null) { sock_conn_timeout=Integer.parseInt(str); props.remove("sock_conn_timeout"); } str=props.getProperty("peer_addr_read_timeout"); if(str != null) { peer_addr_read_timeout=Integer.parseInt(str); props.remove("peer_addr_read_timeout"); } str=props.getProperty("recv_buf_size"); if(str != null) { recv_buf_size=Integer.parseInt(str); props.remove("recv_buf_size"); } str=props.getProperty("send_buf_size"); if(str != null) { send_buf_size=Integer.parseInt(str); props.remove("send_buf_size"); } str=props.getProperty("skip_suspected_members"); if(str != null) { skip_suspected_members=Boolean.valueOf(str).booleanValue(); props.remove("skip_suspected_members"); } str=props.getProperty("suspect_on_send_failure"); if(str != null) { suspect_on_send_failure=Boolean.valueOf(str).booleanValue(); props.remove("suspect_on_send_failure"); } str=props.getProperty("use_send_queues"); if(str != null) { use_send_queues=Boolean.valueOf(str).booleanValue(); props.remove("use_send_queues"); } str=props.getProperty("send_queue_size"); if(str != null) { send_queue_size=Integer.parseInt(str); props.remove("send_queue_size"); } str=props.getProperty("tcp_nodelay"); if(str != null) { tcp_nodelay=Boolean.parseBoolean(str); props.remove("tcp_nodelay"); } str=props.getProperty("linger"); if(str != null) { linger=Integer.parseInt(str); if(linger > 0) { linger=Math.min(1, linger / 1000); // convert from ms to secs } props.remove("linger"); } Util.checkBufferSize(getName() + ".recv_buf_size", recv_buf_size); Util.checkBufferSize(getName() + ".send_buf_size", send_buf_size); return true; } public void init() throws Exception { super.init(); if(start_port <= 0) { Protocol dynamic_discovery_prot=stack.findProtocol("MPING"); if(dynamic_discovery_prot == null) dynamic_discovery_prot=stack.findProtocol("TCPGOSSIP"); if(dynamic_discovery_prot != null) { if(log.isDebugEnabled()) log.debug("dynamic discovery is present (" + dynamic_discovery_prot + "), so start_port=" + start_port + " is okay"); } else { throw new IllegalArgumentException("start_port cannot be set to " + start_port + ", as no dynamic discovery protocol (e.g. MPING or TCPGOSSIP) has been detected."); } } } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { Set
    mbrs; synchronized(members) { mbrs=(Set
    )members.clone(); } for(Address dest: mbrs) { sendToSingleMember(dest, data, offset, length); } } public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { if(log.isTraceEnabled()) log.trace("dest=" + dest + " (" + length + " bytes)"); if(skip_suspected_members) { if(suspected_mbrs.contains(dest)) { if(log.isTraceEnabled()) log.trace("will not send unicast message to " + dest + " as it is currently suspected"); return; } } try { send(dest, data, offset, length); } catch(Exception e) { if(log.isTraceEnabled()) log.trace("failure sending message to " + dest, e); if(suspect_on_send_failure && members.contains(dest)) { if(!suspected_mbrs.contains(dest)) { suspected_mbrs.add(dest); up_prot.up(new Event(Event.SUSPECT, dest)); } } } } public String getInfo() { StringBuilder sb=new StringBuilder(); sb.append("connections: ").append(printConnections()).append("\n"); return sb.toString(); } public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { if(multicast) msg.setDest(null); else msg.setDest(dest); } public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { postUnmarshalling(msg, dest, null, multicast); } public abstract String printConnections(); public abstract void send(Address dest, byte[] data, int offset, int length) throws Exception; public abstract void retainAll(Collection
    members); /** ConnectionTable.Receiver interface */ public void receive(Address sender, byte[] data, int offset, int length) { receive(local_addr, sender, data, offset, length); } protected Object handleDownEvent(Event evt) { Object ret=super.handleDownEvent(evt); if(evt.getType() == Event.VIEW_CHANGE) { suspected_mbrs.clear(); retainAll(members); // remove all connections from the ConnectionTable which are not members } else if(evt.getType() == Event.UNSUSPECT) { Address suspected_mbr=(Address)evt.getArg(); suspected_mbrs.remove(suspected_mbr); } return ret; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FD_ALL.java0000644000175000017500000004013211366547366025255 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.stack.Protocol; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.util.*; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.io.*; /** * Failure detection based on simple heartbeat protocol. Every member * periodically multicasts a heartbeat. Every member also maintains a table of * all members (minus itself). When data or a heartbeat from P are received, we * reset the timestamp for P to the current time. Periodically, we check for * expired members, and suspect those. * * @author Bela Ban * @version $Id: FD_ALL.java,v 1.12.2.3 2008/06/18 17:34:57 vlada Exp $ */ public class FD_ALL extends Protocol { /** Map of addresses and timestamps of last updates */ final Map timestamps=new ConcurrentHashMap(); /** Number of milliseconds after which a HEARTBEAT is sent to the cluster */ long interval=3000; /** * Number of milliseconds after which a node P is suspected if neither a * heartbeat nor data were received from P */ long timeout=5000; /** * when a message is received from P, this is treated as if P sent a * heartbeat */ boolean msg_counts_as_heartbeat=true; Address local_addr=null; final List
    members=new ArrayList
    (); boolean shun=true; TimeScheduler timer=null; // task which multicasts HEARTBEAT message after 'interval' ms @GuardedBy("lock") private ScheduledFuture heartbeat_sender_future=null; // task which checks for members exceeding timeout and suspects them @GuardedBy("lock") private ScheduledFuture timeout_checker_future=null; private boolean tasks_running=false; protected int num_heartbeats_sent, num_heartbeats_received=0; protected int num_suspect_events=0; final static String name="FD_ALL"; final BoundedList
    suspect_history=new BoundedList
    (20); final Map invalid_pingers=new HashMap(7); // keys=Address, val=Integer (number of pings from suspected mbrs) final Lock lock=new ReentrantLock(); public String getName() {return FD_ALL.name;} public String getLocalAddress() {return local_addr != null? local_addr.toString() : "null";} public String getMembers() {return members != null? members.toString() : "null";} public int getHeartbeatsSent() {return num_heartbeats_sent;} public int getHeartbeatsReceived() {return num_heartbeats_received;} public int getSuspectEventsSent() {return num_suspect_events;} public long getTimeout() {return timeout;} public void setTimeout(long timeout) {this.timeout=timeout;} public long getInterval() {return interval;} public void setInterval(long interval) {this.interval=interval;} public boolean isShun() {return shun;} public void setShun(boolean flag) {this.shun=flag;} public boolean isRunning() {return tasks_running;} public String printSuspectHistory() { StringBuilder sb=new StringBuilder(); for(Address tmp:suspect_history) { sb.append(new Date()).append(": ").append(tmp).append("\n"); } return sb.toString(); } public String printTimestamps() { return printTimeStamps(); } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("timeout"); if(str != null) { timeout=Long.parseLong(str); props.remove("timeout"); } str=props.getProperty("interval"); if(str != null) { interval=Long.parseLong(str); props.remove("interval"); } str=props.getProperty("shun"); if(str != null) { shun=Boolean.valueOf(str).booleanValue(); props.remove("shun"); } str=props.getProperty("msg_counts_as_heartbeat"); if(str != null) { msg_counts_as_heartbeat=Boolean.valueOf(str).booleanValue(); props.remove("msg_counts_as_heartbeat"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void resetStats() { num_heartbeats_sent=num_heartbeats_received=num_suspect_events=0; suspect_history.clear(); } public void init() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); } public void stop() { stopTasks(); } public Object up(Event evt) { Message msg; Header hdr; Address sender; switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: msg=(Message)evt.getArg(); hdr=(Header)msg.getHeader(name); if(msg_counts_as_heartbeat) update(msg.getSrc()); // update when data is received too ? maybe a bit costly if(hdr == null) break; // message did not originate from FD_ALL layer, just pass up switch(hdr.type) { case Header.HEARTBEAT: // heartbeat request; send heartbeat ack sender=msg.getSrc(); if(sender.equals(local_addr)) break; //if(log.isTraceEnabled()) // log.trace(local_addr + ": received a heartbeat from " + sender); // 2. Shun the sender of a HEARTBEAT message if that sender is not a member. This will cause // the sender to leave the group (and possibly rejoin it later) if(shun && members != null && !members.contains(sender)) { shunInvalidHeartbeatSender(sender); break; } update(sender); // updates the heartbeat entry for 'sender' num_heartbeats_received++; break; // don't pass up ! case Header.SUSPECT: if(log.isTraceEnabled()) log.trace("[SUSPECT] suspect hdr is " + hdr); down_prot.down(new Event(Event.SUSPECT, hdr.suspected_mbr)); up_prot.up(new Event(Event.SUSPECT, hdr.suspected_mbr)); break; case Header.NOT_MEMBER: if(shun) { if(log.isDebugEnabled()) log.debug("[NOT_MEMBER] I'm being shunned; exiting"); up_prot.up(new Event(Event.EXIT)); } break; } return null; } return up_prot.up(evt); // pass up to the layer above us } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: down_prot.down(evt); View v=(View)evt.getArg(); handleViewChange(v); return null; } return down_prot.down(evt); } private void startTasks() { startHeartbeatSender(); startTimeoutChecker(); tasks_running=true; if(log.isTraceEnabled()) log.trace("started heartbeat sender and timeout checker tasks"); } private void stopTasks() { stopTimeoutChecker(); stopHeartbeatSender(); tasks_running=false; if(log.isTraceEnabled()) log.trace("stopped heartbeat sender and timeout checker tasks"); } private void startTimeoutChecker() { lock.lock(); try { if(timeout_checker_future == null || timeout_checker_future.isDone()) { timeout_checker_future=timer.scheduleWithFixedDelay(new TimeoutChecker(), interval, interval, TimeUnit.MILLISECONDS); } } finally { lock.unlock(); } } private void stopTimeoutChecker() { lock.lock(); try { if(timeout_checker_future != null) { timeout_checker_future.cancel(true); timeout_checker_future=null; } } finally { lock.unlock(); } } private void startHeartbeatSender() { lock.lock(); try { if(heartbeat_sender_future == null || heartbeat_sender_future.isDone()) { heartbeat_sender_future=timer.scheduleWithFixedDelay(new HeartbeatSender(), interval, interval, TimeUnit.MILLISECONDS); } } finally { lock.unlock(); } } private void stopHeartbeatSender() { lock.lock(); try { if(heartbeat_sender_future != null) { heartbeat_sender_future.cancel(true); heartbeat_sender_future=null; } } finally { lock.unlock(); } } private void update(Address sender) { if(sender != null && !sender.equals(local_addr)) timestamps.put(sender, Long.valueOf(System.currentTimeMillis())); } private void handleViewChange(View v) { Vector
    mbrs=v.getMembers(); members.clear(); members.addAll(mbrs); Set
    keys=timestamps.keySet(); keys.retainAll(mbrs); // remove all nodes which have left the cluster for(Iterator
    it=mbrs.iterator();it.hasNext();) { // and add new members Address mbr=it.next(); if(mbr.equals(local_addr)) continue; if(!timestamps.containsKey(mbr)) { timestamps.put(mbr, Long.valueOf(System.currentTimeMillis())); } } invalid_pingers.clear(); if(!tasks_running && members.size() > 1) startTasks(); else if(tasks_running && members.size() < 2) stopTasks(); } /** * If sender is not a member, send a NOT_MEMBER to sender (after n pings * received) */ private void shunInvalidHeartbeatSender(Address sender) { int num_pings=0; Message shun_msg; if(invalid_pingers.containsKey(sender)) { num_pings=invalid_pingers.get(sender).intValue(); if(num_pings >= 3) { if(log.isDebugEnabled()) log.debug(sender + " is not in " + members + " ! Shunning it"); shun_msg=new Message(sender, null, null); shun_msg.setFlag(Message.OOB); shun_msg.putHeader(name, new Header(Header.NOT_MEMBER)); down_prot.down(new Event(Event.MSG, shun_msg)); invalid_pingers.remove(sender); } else { num_pings++; invalid_pingers.put(sender, new Integer(num_pings)); } } else { num_pings++; invalid_pingers.put(sender, Integer.valueOf(num_pings)); } } private String printTimeStamps() { StringBuilder sb=new StringBuilder(); long current_time=System.currentTimeMillis(); for(Iterator> it=timestamps.entrySet().iterator();it.hasNext();) { Map.Entry entry=it.next(); sb.append(entry.getKey()).append(": "); sb.append(current_time - entry.getValue().longValue()).append(" ms old\n"); } return sb.toString(); } void suspect(Address mbr) { Message suspect_msg=new Message(); suspect_msg.setFlag(Message.OOB); Header hdr=new Header(Header.SUSPECT, mbr); suspect_msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, suspect_msg)); num_suspect_events++; suspect_history.add(mbr); } public static class Header extends org.jgroups.Header implements Streamable { public static final byte HEARTBEAT=0; public static final byte SUSPECT=1; public static final byte NOT_MEMBER=2; // received as response by pinged mbr when we are not a member byte type=Header.HEARTBEAT; Address suspected_mbr=null; /** used for externalization */ public Header() {} public Header(byte type) { this.type=type; } public Header(byte type,Address suspect) { this(type); this.suspected_mbr=suspect; } public String toString() { switch(type) { case FD_ALL.Header.HEARTBEAT: return "heartbeat"; case FD_ALL.Header.SUSPECT: return "SUSPECT (suspected_mbr=" + suspected_mbr + ")"; case FD_ALL.Header.NOT_MEMBER: return "NOT_MEMBER"; default: return "unknown type (" + type + ")"; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeObject(suspected_mbr); } public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException { type=in.readByte(); suspected_mbr=(Address)in.readObject(); } public int size() { int retval=Global.BYTE_SIZE; // type retval+=Util.size(suspected_mbr); return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); Util.writeAddress(suspected_mbr, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); suspected_mbr=Util.readAddress(in); } } /** * Class which periodically multicasts a HEARTBEAT message to the cluster */ class HeartbeatSender implements Runnable { public void run() { Message heartbeat=new Message(); // send to all heartbeat.setFlag(Message.OOB); Header hdr=new Header(Header.HEARTBEAT); heartbeat.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, heartbeat)); num_heartbeats_sent++; } } class TimeoutChecker extends HeartbeatSender { public void run() { if(log.isTraceEnabled()) log.trace("checking for expired senders, table is:\n" + printTimeStamps()); long current_time=System.currentTimeMillis(), diff; for(Iterator> it=timestamps.entrySet().iterator();it.hasNext();) { Map.Entry entry=it.next(); Address key=entry.getKey(); Long val=entry.getValue(); if(val == null) { it.remove(); continue; } diff=current_time - val.longValue(); if(diff > timeout) { if(log.isTraceEnabled()) log.trace("haven't received a heartbeat from " + key + " for " + diff + " ms, suspecting it"); suspect(key); } } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TunnelHeader.java0000644000175000017500000000222211366547366026650 0ustar twernertwerner// $Id: TunnelHeader.java,v 1.7 2007/05/01 10:55:10 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; public class TunnelHeader extends Header implements Streamable { public String channel_name=null; public TunnelHeader() {} // used for externalization public TunnelHeader(String n) {channel_name=n;} public int size() { return channel_name == null? 1 : channel_name.length() +3; } public String toString() { return "[TUNNEL:channel_name=" + channel_name + ']'; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(channel_name); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { channel_name=(String)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { Util.writeString(channel_name, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { channel_name=Util.readString(in); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/DUPL.java0000644000175000017500000000700611366547366025043 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.stack.Protocol; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Address; import java.util.Properties; /** Duplicates outgoing or incoming messages by copying them * @author Bela Ban * @version $Id: DUPL.java,v 1.3.2.2 2008/06/09 09:41:19 belaban Exp $ */ public class DUPL extends Protocol { private static enum Direction {UP,DOWN}; protected int incoming_copies=1; protected int outgoing_copies=1; protected boolean copy_unicast_msgs=true; protected boolean copy_multicast_msgs=true; public DUPL() { } public DUPL(boolean copy_multicast_msgs, boolean copy_unicast_msgs, int incoming_copies, int outgoing_copies) { this.copy_multicast_msgs=copy_multicast_msgs; this.copy_unicast_msgs=copy_unicast_msgs; this.incoming_copies=incoming_copies; this.outgoing_copies=outgoing_copies; } public String getName() { return "DUPL"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("incoming_copies"); if(str != null) { incoming_copies=Integer.parseInt(str); props.remove("incoming_copies"); } str=props.getProperty("outgoing_copies"); if(str != null) { outgoing_copies=Integer.parseInt(str); props.remove("outgoing_copies"); } str=props.getProperty("copy_unicast_msgs"); if(str != null) { copy_unicast_msgs=Boolean.parseBoolean(str); props.remove("copy_unicast_msgs"); } str=props.getProperty("copy_multicast_msgs"); if(str != null) { copy_multicast_msgs=Boolean.parseBoolean(str); props.remove("copy_multicast_msgs"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public Object down(Event evt) { boolean copy=(copy_multicast_msgs || copy_unicast_msgs) && outgoing_copies > 0; if(!copy) return down_prot.down(evt); switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); copy(msg, outgoing_copies, Direction.DOWN); break; } return down_prot.down(evt); } public Object up(Event evt) { boolean copy=(copy_multicast_msgs || copy_unicast_msgs) && incoming_copies > 0; if(!copy) return up_prot.up(evt); switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); copy(msg, incoming_copies, Direction.UP); break; } return up_prot.up(evt); } private void copy(Message msg, int num_copies, Direction direction) { Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); if((multicast && copy_multicast_msgs) || (!multicast && copy_unicast_msgs)) { for(int i=0; i < num_copies; i++) { Message copy=msg.copy(true); switch(direction) { case UP: up_prot.up(new Event(Event.MSG, copy)); break; case DOWN: down_prot.down(new Event(Event.MSG, copy)); break; } } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/MPING.java0000644000175000017500000003235611366547366025157 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.util.Buffer; import org.jgroups.util.ExposedByteArrayOutputStream; import org.jgroups.util.Util; import java.io.*; import java.net.*; import java.util.*; /** * Uses its own IP multicast socket to send and receive discovery requests/responses. Can be used in * conjuntion with a non-UDP transport, e.g. TCP.

    * The discovery is assymetric: discovery requests are broadcast via the multicast socket, and * received via the multicast socket by everyone in the group. However, the discovery responses are sent * back via the regular transport (e.g. TCP) to the sender (discovery request contained sender's regular address, * e.g. 192.168.0.2:7800). * @author Bela Ban * @version $Id: MPING.java,v 1.29.2.8 2009/05/18 16:18:21 galderz Exp $ */ public class MPING extends PING implements Runnable { MulticastSocket mcast_sock=null; /** If we have multiple mcast send sockets, e.g. send_interfaces or send_on_all_interfaces enabled */ MulticastSocket[] mcast_send_sockets=null; Thread receiver=null; InetAddress bind_addr=null; int ip_ttl=8; InetAddress mcast_addr=null; int mcast_port=7555; /** If true, the transport should use all available interfaces to receive multicast messages */ boolean receive_on_all_interfaces=false; /** List of interfaces to receive multicasts on. The multicast receive socket will listen * on all of these interfaces. This is a comma-separated list of IP addresses or interface names. E.g. * "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded; we only bind to an interface once. * If this property is set, it override receive_on_all_interfaces. */ List receive_interfaces=null; /** If true, the transport should use all available interfaces to send multicast messages. This means * the same multicast message is sent N times, so use with care */ boolean send_on_all_interfaces=false; /** List of interfaces to send multicasts on. The multicast send socket will send the * same multicast message on all of these interfaces. This is a comma-separated list of IP addresses or * interface names. E.g. "192.168.5.1,eth1,127.0.0.1". Duplicates are discarded. * If this property is set, it override send_on_all_interfaces. */ List send_interfaces=null; /** Pre-allocated byte stream. Used for serializing datagram packets. Will grow as needed */ final ExposedByteArrayOutputStream out_stream=new ExposedByteArrayOutputStream(128); byte receive_buf[]=new byte[1024]; private static final boolean can_bind_to_mcast_addr; // are we running on Linux ? static { can_bind_to_mcast_addr=Util.checkForLinux() || Util.checkForSolaris() || Util.checkForHp(); } public String getName() { return "MPING"; } public InetAddress getBindAddr() { return bind_addr; } public void setBindAddr(InetAddress bind_addr) { this.bind_addr=bind_addr; } public List getReceiveInterfaces() { return receive_interfaces; } public List getSendInterfaces() { return send_interfaces; } public boolean isReceiveOnAllInterfaces() { return receive_on_all_interfaces; } public boolean isSendOnAllInterfaces() { return send_on_all_interfaces; } public int getTTL() { return ip_ttl; } public void setTTL(int ip_ttl) { this.ip_ttl=ip_ttl; } public InetAddress getMcastAddr() { return mcast_addr; } public void setMcastAddr(InetAddress mcast_addr) { this.mcast_addr=mcast_addr; } public int getMcastPort() { return mcast_port; } public void setMcastPort(int mcast_port) { this.mcast_port=mcast_port; } public boolean setProperties(Properties props) { String str; try { bind_addr=Util.getBindAddress(props); } catch(UnknownHostException unknown) { log.fatal("failed getting bind_addr", unknown); return false; } catch(SocketException ex) { log.fatal("failed getting bind_addr", ex); return false; } str=Util.getProperty(new String[]{Global.MPING_MCAST_ADDR}, props, "mcast_addr", false, "230.5.6.7"); if(str != null) { try { mcast_addr=InetAddress.getByName(str); } catch(UnknownHostException e) { log.error("could not resolve " + str, e); return false; } props.remove("mcast_addr"); } str=Util.getProperty(new String[]{Global.MPING_MCAST_PORT}, props, "mcast_port", false, "7555"); if(str != null) { mcast_port=Integer.parseInt(str); props.remove("mcast_port"); } str=Util.getProperty(new String[]{Global.MPING_IP_TTL}, props, "ip_ttl", false, "16"); if(str != null) { ip_ttl=Integer.parseInt(str); props.remove("ip_ttl"); } str=props.getProperty("bind_to_all_interfaces"); if(str != null) { receive_on_all_interfaces=Boolean.parseBoolean(str); props.remove("bind_to_all_interfaces"); log.warn("bind_to_all_interfaces has been deprecated; use receive_on_all_interfaces instead"); props.remove("bind_to_all_interfaces"); } str=props.getProperty("receive_on_all_interfaces"); if(str != null) { receive_on_all_interfaces=Boolean.parseBoolean(str); props.remove("receive_on_all_interfaces"); } str=props.getProperty("receive_interfaces"); if(str != null) { try { receive_interfaces=Util.parseInterfaceList(str); props.remove("receive_interfaces"); } catch(Exception e) { log.error("error determining interfaces (" + str + ")", e); return false; } } str=props.getProperty("send_on_all_interfaces"); if(str != null) { send_on_all_interfaces=Boolean.parseBoolean(str); props.remove("send_on_all_interfaces"); } str=props.getProperty("send_interfaces"); if(str != null) { try { send_interfaces=Util.parseInterfaceList(str); props.remove("send_interfaces"); } catch(Exception e) { log.error("error determining interfaces (" + str + ")", e); return false; } } if(mcast_addr == null) { try { mcast_addr=InetAddress.getByName("230.5.6.7"); } catch(UnknownHostException e) { log.error("failed getting default mcast address", e); return false; } } return super.setProperties(props); } public Object up(Event evt) { if(evt.getType() == Event.CONFIG) { if(bind_addr == null) { Map config=(Map)evt.getArg(); bind_addr=(InetAddress)config.get("bind_addr"); } return up_prot.up(evt); } return super.up(evt); } public void start() throws Exception { if(can_bind_to_mcast_addr) // https://jira.jboss.org/jira/browse/JGRP-836 - prevent cross talking on Linux mcast_sock=Util.createMulticastSocket(mcast_addr, mcast_port, log); else mcast_sock=new MulticastSocket(mcast_port); mcast_sock.setTimeToLive(ip_ttl); if(receive_on_all_interfaces || (receive_interfaces != null && !receive_interfaces.isEmpty())) { List interfaces; if(receive_interfaces != null) interfaces=receive_interfaces; else interfaces=Util.getAllAvailableInterfaces(); bindToInterfaces(interfaces, mcast_sock, mcast_addr); } else { if(bind_addr != null) mcast_sock.setInterface(bind_addr); mcast_sock.joinGroup(mcast_addr); } // 3b. Create mcast sender socket if(send_on_all_interfaces || (send_interfaces != null && !send_interfaces.isEmpty())) { List interfaces; NetworkInterface intf; if(send_interfaces != null) interfaces=send_interfaces; else interfaces=Util.getAllAvailableInterfaces(); mcast_send_sockets=new MulticastSocket[interfaces.size()]; int index=0; for(Iterator it=interfaces.iterator(); it.hasNext();) { intf=(NetworkInterface)it.next(); mcast_send_sockets[index]=new MulticastSocket(); mcast_send_sockets[index].setNetworkInterface(intf); mcast_send_sockets[index].setTimeToLive(ip_ttl); index++; } } startReceiver(); super.start(); } private void bindToInterfaces(List interfaces, MulticastSocket s, InetAddress mcast_addr) throws IOException { SocketAddress tmp_mcast_addr=new InetSocketAddress(mcast_addr, mcast_port); for(Iterator it=interfaces.iterator(); it.hasNext();) { NetworkInterface i=(NetworkInterface)it.next(); for(Enumeration en2=i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr=(InetAddress)en2.nextElement(); s.joinGroup(tmp_mcast_addr, i); if(log.isTraceEnabled()) log.trace("joined " + tmp_mcast_addr + " on " + i.getName() + " (" + addr + ")"); break; } } } private void startReceiver() { if(receiver == null || !receiver.isAlive()) { receiver=new Thread(Util.getGlobalThreadGroup(), this, "ReceiverThread"); receiver.setDaemon(true); receiver.start(); if(log.isTraceEnabled()) log.trace("receiver thread started"); } } public void stop() { Util.close(mcast_sock); mcast_sock=null; receiver=null; super.stop(); } void sendMcastDiscoveryRequest(Message msg) { Buffer buf; DatagramPacket packet; DataOutputStream out=null; try { if(msg.getSrc() == null) msg.setSrc(local_addr); out_stream.reset(); out=new DataOutputStream(out_stream); msg.writeTo(out); out.flush(); // flushes contents to out_stream buf=new Buffer(out_stream.getRawBuffer(), 0, out_stream.size()); packet=new DatagramPacket(buf.getBuf(), buf.getOffset(), buf.getLength(), mcast_addr, mcast_port); discovery_reception.reset(); if(mcast_send_sockets != null) { MulticastSocket s; for(int i=0; i < mcast_send_sockets.length; i++) { s=mcast_send_sockets[i]; try { s.send(packet); } catch(Exception e) { log.error("failed sending packet on socket " + s); } } } else { // DEFAULT path if(mcast_sock != null) mcast_sock.send(packet); } waitForDiscoveryRequestReception(); } catch(IOException ex) { log.error("failed sending discovery request", ex); } finally { Util.close(out); } } public void run() { DatagramPacket packet=new DatagramPacket(receive_buf, receive_buf.length); byte[] data; ByteArrayInputStream inp_stream=null; DataInputStream inp=null; Message msg; while(mcast_sock != null && receiver != null && Thread.currentThread().equals(receiver)) { packet.setData(receive_buf, 0, receive_buf.length); try { mcast_sock.receive(packet); data=packet.getData(); inp_stream=new ByteArrayInputStream(data, 0, data.length); inp=new DataInputStream(inp_stream); msg=new Message(); msg.readFrom(inp); up(new Event(Event.MSG, msg)); } catch(SocketException socketEx) { break; } catch(Throwable ex) { log.error("failed receiving packet (from " + packet.getSocketAddress() + ")", ex); } finally { closeInputStream(inp); closeInputStream(inp_stream); } } if(log.isTraceEnabled()) log.trace("receiver thread terminated"); } private static void closeInputStream(InputStream inp) { if(inp != null) try {inp.close();} catch(Throwable e) {} } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/PING.java0000644000175000017500000002764111366547366025043 0ustar twernertwerner package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.GossipClient; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import org.jgroups.util.Promise; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.*; /** * The PING protocol layer retrieves the initial membership (used by the GMS when started * by sending event FIND_INITIAL_MBRS down the stack). We do this by mcasting PING * requests to an IP MCAST address (or, if gossiping is enabled, by contacting the GossipRouter). * The responses should allow us to determine the coordinator whom we have to * contact, e.g. in case we want to join the group. When we are a server (after having * received the BECOME_SERVER event), we'll respond to PING requests with a PING * response.

    The FIND_INITIAL_MBRS event will eventually be answered with a * FIND_INITIAL_MBRS_OK event up the stack. * The following properties are available * property: gossip_host - if you are using GOSSIP then this defines the host of the GossipRouter, default is null * property: gossip_port - if you are using GOSSIP then this defines the port of the GossipRouter, default is null * @author Bela Ban * @version $Id: PING.java,v 1.36.2.7 2009/04/27 08:35:47 belaban Exp $ */ public class PING extends Discovery { String gossip_host=null; Vector gossip_hosts=null; int gossip_port=0; long gossip_refresh=20000; // time in msecs after which the entry in GossipRouter will be refreshed GossipClient client; int port_range=1; // number of ports to be probed for initial membership private List

    initial_hosts=null; // hosts to be contacted for the initial membership int sock_conn_timeout=1000; // max time in millis for a socket creation int sock_read_timeout=3000; // max time in millis for a socket read protected final Promise discovery_reception=new Promise(); /** Time (in ms) to wait for our own discovery message to be received. 0 means don't wait. If the discovery message is not received within discovery_timeout ms, a warning will be logged */ private long discovery_timeout=0L; public static final String name="PING"; public String getName() { return name; } /** * sets the properties of the PING protocol. * The following properties are available * property: timeout - the timeout (ms) to wait for the initial members, default is 3000=3 secs * property: num_initial_members - the minimum number of initial members for a FIND_INITAL_MBRS, default is 2 * property: gossip_host - if you are using GOSSIP then this defines the host of the GossipRouter, default is null * property: gossip_port - if you are using GOSSIP then this defines the port of the GossipRouter, default is null * * @param props - a property set containing only PING properties * @return returns true if all properties were parsed properly * returns false if there are unrecnogized properties in the property set */ public boolean setProperties(Properties props) { String str; str=props.getProperty("gossip_host"); if(str != null) { gossip_host=str; props.remove("gossip_host"); } str=props.getProperty("gossip_hosts"); if(str != null) { try { gossip_hosts=createInitialHosts(str); } catch(UnknownHostException e) { throw new IllegalArgumentException(e); } props.remove("gossip_hosts"); } str=props.getProperty("gossip_port"); if(str != null) { gossip_port=Integer.parseInt(str); props.remove("gossip_port"); } str=props.getProperty("gossip_refresh"); if(str != null) { gossip_refresh=Long.parseLong(str); props.remove("gossip_refresh"); } str=props.getProperty("sock_conn_timeout"); // wait for at most n members if(str != null) { sock_conn_timeout=Integer.parseInt(str); props.remove("sock_conn_timeout"); } str=props.getProperty("sock_read_timeout"); // wait for at most n members if(str != null) { sock_read_timeout=Integer.parseInt(str); props.remove("sock_read_timeout"); } str=props.getProperty("port_range"); // if member cannot be contacted on base port, if(str != null) { // how many times can we increment the port port_range=Integer.parseInt(str); if(port_range < 1) { port_range=1; } props.remove("port_range"); } str=props.getProperty("discovery_timeout"); // wait for at most n members if(str != null) { discovery_timeout=Integer.parseInt(str); props.remove("discovery_timeout"); } str=props.getProperty("initial_hosts"); if(str != null) { props.remove("initial_hosts"); try { initial_hosts=new ArrayList
    (); List tmp=createInitialHosts(str); initial_hosts.addAll(tmp); } catch(UnknownHostException e) { if(log.isErrorEnabled()) log.error("failed constructing initial list of hosts", e); return false; } } return super.setProperties(props); } public int getGossipPort() { return gossip_port; } public void setGossipPort(int gossip_port) { this.gossip_port=gossip_port; } public long getGossipRefresh() { return gossip_refresh; } public void setGossipRefresh(long gossip_refresh) { this.gossip_refresh=gossip_refresh; } public void init() throws Exception { super.init(); if(gossip_hosts != null) { client=new GossipClient(gossip_hosts, gossip_refresh); client.setSocketConnectionTimeout(sock_conn_timeout); client.setSocketReadTimeout(sock_read_timeout); } else if(gossip_host != null && gossip_port != 0) { client=new GossipClient(new IpAddress(InetAddress.getByName(gossip_host), gossip_port), gossip_refresh); client.setSocketConnectionTimeout(sock_conn_timeout); client.setSocketReadTimeout(sock_read_timeout); } } public void stop() { super.stop(); if(client != null) { client.stop(); } discovery_reception.reset(); } public void localAddressSet(Address addr) { // Add own address to initial_hosts if not present: we must always be able to ping ourself ! if(initial_hosts != null && addr != null) { if(initial_hosts.contains(addr)) { initial_hosts.remove(addr); if(log.isDebugEnabled()) log.debug("[SET_LOCAL_ADDRESS]: removing my own address (" + addr + ") from initial_hosts; initial_hosts=" + initial_hosts); } } } public void handleConnect() { if(client != null) client.register(group_addr, local_addr); } public void handleDisconnect() { if(client != null) { if(group_addr != null && local_addr != null) client.unregister(group_addr, local_addr); client.stop(); } } public void sendGetMembersRequest(Promise promise) throws Exception { Message msg; PingHeader hdr; List
    gossip_rsps; if(client != null) { gossip_rsps=client.getMembers(group_addr); if(gossip_rsps != null && !gossip_rsps.isEmpty()) { // Set a temporary membership in the UDP layer, so that the following multicast // will be sent to all of them Event view_event=new Event(Event.TMP_VIEW, makeView(new Vector
    (gossip_rsps))); down_prot.down(view_event); // needed e.g. by failure detector or UDP } else { //do nothing return; } if(!gossip_rsps.isEmpty()) { for(Iterator
    it=gossip_rsps.iterator(); it.hasNext();) { Address dest=it.next(); msg=new Message(dest, null, null); // unicast msg msg.setFlag(Message.OOB); msg.putHeader(getName(), new PingHeader(PingHeader.GET_MBRS_REQ, null)); down_prot.down(new Event(Event.MSG, msg)); } } Util.sleep(500); } else { if(initial_hosts != null && !initial_hosts.isEmpty()) { for(Address addr: initial_hosts) { // if(tmpMbrs.contains(addr)) { // ; // continue; // changed as suggested by Mark Kopec // } msg=new Message(addr, null, null); msg.setFlag(Message.OOB); msg.putHeader(name, new PingHeader(PingHeader.GET_MBRS_REQ, null)); if(log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); down_prot.down(new Event(Event.MSG, msg)); } } else { // 1. Mcast GET_MBRS_REQ message hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); msg=new Message(null); // mcast msg msg.setFlag(Message.OOB); msg.putHeader(getName(), hdr); // needs to be getName(), so we might get "MPING" ! sendMcastDiscoveryRequest(msg); } } } public Object up(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); PingHeader hdr=(PingHeader)msg.getHeader(getName()); if(hdr != null && hdr.type == PingHeader.GET_MBRS_REQ && msg.getSrc().equals(local_addr)) { discovery_reception.setResult(true); } } return super.up(evt); } void sendMcastDiscoveryRequest(Message discovery_request) { discovery_reception.reset(); down_prot.down(new Event(Event.MSG, discovery_request)); waitForDiscoveryRequestReception(); } /* -------------------------- Private methods ---------------------------- */ protected void waitForDiscoveryRequestReception() { if(discovery_timeout > 0) { try { discovery_reception.getResultWithTimeout(discovery_timeout); } catch(TimeoutException e) { if(log.isWarnEnabled()) log.warn("didn't receive my own discovery request - multicast socket might not be configured correctly"); } } } /** * Input is "daddy[8880],sindhu[8880],camille[5555]. Return List of IpAddresses */ private Vector createInitialHosts(String l) throws UnknownHostException { StringTokenizer tok=new StringTokenizer(l, ","); String t; IpAddress addr; Vector retval=new Vector(); while(tok.hasMoreTokens()) { try { t=tok.nextToken().trim(); String host=t.substring(0, t.indexOf('[')); host=host.trim(); int port=Integer.parseInt(t.substring(t.indexOf('[') + 1, t.indexOf(']'))); for(int i=port; i < port + port_range; i++) { addr=new IpAddress(host, i); retval.add(addr); } } catch(NumberFormatException e) { if(log.isErrorEnabled()) log.error("exeption is " + e); } } return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/UDP.java0000644000175000017500000010631411366547366024731 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.stack.IpAddress; import org.jgroups.util.BoundedList; import org.jgroups.util.Util; import java.io.IOException; import java.io.InterruptedIOException; import java.net.*; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; /** * IP multicast transport based on UDP. Messages to the group (msg.dest == null) will * be multicast (to all group members), whereas point-to-point messages * (msg.dest != null) will be unicast to a single member. Uses a multicast and * a unicast socket.

    * The following properties are read by the UDP protocol: *

      *
    • param mcast_addr - the multicast address to use; default is 228.8.8.8. *
    • param mcast_port - (int) the port that the multicast is sent on; default is 7600 *
    • param ip_mcast - (boolean) flag whether to use IP multicast; default is true. *
    • param ip_ttl - the default time-to-live for multicast packets sent out on this * socket; default is 32. *
    • param use_packet_handler - boolean, defaults to false. * If set, the mcast and ucast receiver threads just put * the datagram's payload (a byte buffer) into a queue, from where a separate thread * will dequeue and handle them (unmarshal and pass up). This frees the receiver * threads from having to do message unmarshalling; this time can now be spent * receiving packets. If you have lots of retransmissions because of network * input buffer overflow, consider setting this property to true. *
    * @author Bela Ban * @version $Id: UDP.java,v 1.156.2.17 2009/05/18 16:18:21 galderz Exp $ */ public class UDP extends TP implements Runnable { /** Socket used for *
      *
    1. sending unicast packets and *
    2. receiving unicast packets *
    * The address of this socket will be our local address (local_addr) */ DatagramSocket sock=null; /** * BoundedList of the last 100 ports used. This is to avoid reusing a port for DatagramSocket */ private static final BoundedList last_ports_used=new BoundedList(100); private static final boolean can_bind_to_mcast_addr; // are we running on Linux ? static { can_bind_to_mcast_addr=Util.checkForLinux() || Util.checkForSolaris() || Util.checkForHp(); } /** IP multicast socket for sending and receiving multicast packets */ MulticastSocket mcast_sock=null; /** If we have multiple mcast send sockets, e.g. send_interfaces or send_on_all_interfaces enabled */ MulticastSocket[] mcast_send_sockets=null; /** * Traffic class for sending unicast and multicast datagrams. * Valid values are (check {@link DatagramSocket#setTrafficClass(int)} ); for details): *
      *
    • IPTOS_LOWCOST (0x02), decimal 2
    • *
    • IPTOS_RELIABILITY (0x04)<, decimal 4/LI> *
    • IPTOS_THROUGHPUT (0x08), decimal 8
    • *
    • IPTOS_LOWDELAY (0x10), decimal 16
    • *
    */ int tos=8; // valid values: 2, 4, 8 (default), 16 /** The multicast address (mcast address and port) this member uses */ IpAddress mcast_addr=null; /** The multicast address used for sending and receiving packets */ String mcast_addr_name="228.8.8.8"; /** The multicast port used for sending and receiving packets */ int mcast_port=7600; /** The multicast receiver thread */ Thread mcast_receiver=null; private final static String MCAST_RECEIVER_THREAD_NAME = "UDP mcast"; /** The unicast receiver thread */ UcastReceiver ucast_receiver=null; /** Whether to enable IP multicasting. If false, multiple unicast datagram * packets are sent rather than one multicast packet */ boolean ip_mcast=true; /** The time-to-live (TTL) for multicast datagram packets */ int ip_ttl=8; /** Send buffer size of the multicast datagram socket */ int mcast_send_buf_size=32000; /** Receive buffer size of the multicast datagram socket */ int mcast_recv_buf_size=64000; /** Send buffer size of the unicast datagram socket */ int ucast_send_buf_size=32000; /** Receive buffer size of the unicast datagram socket */ int ucast_recv_buf_size=64000; /** Usually, src addresses are nulled, and the receiver simply sets them to the address of the sender. However, * for multiple addresses on a Windows loopback device, this doesn't work * (see http://jira.jboss.com/jira/browse/JGRP-79 and the JGroups wiki for details). This must be the same * value for all members of the same group. Default is true, for performance reasons */ // private boolean null_src_addresses=true; /** * Creates the UDP protocol, and initializes the * state variables, does however not start any sockets or threads. */ public UDP() { } /** * Setup the Protocol instance acording to the configuration string. * The following properties are read by the UDP protocol: *
      *
    • param mcast_addr - the multicast address to use default is 228.8.8.8 *
    • param mcast_port - (int) the port that the multicast is sent on default is 7600 *
    • param ip_mcast - (boolean) flag whether to use IP multicast - default is true *
    • param ip_ttl - Set the default time-to-live for multicast packets sent out on this socket. default is 32 *
    * @return true if no other properties are left. * false if the properties still have data in them, ie , * properties are left over and not handled by the protocol stack */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("num_last_ports"); if(str != null) { props.remove("num_last_ports"); log.warn("num_last_ports has been deprecated, property will be ignored"); } str=Util.getProperty(new String[]{Global.UDP_MCAST_ADDR, "jboss.partition.udpGroup"}, props, "mcast_addr", false, "228.8.8.8"); if(str != null) mcast_addr_name=str; str=Util.getProperty(new String[]{Global.UDP_MCAST_PORT, "jboss.partition.udpPort"}, props, "mcast_port", false, "7600"); if(str != null) mcast_port=Integer.parseInt(str); str=props.getProperty("ip_mcast"); if(str != null) { ip_mcast=Boolean.valueOf(str).booleanValue(); props.remove("ip_mcast"); } str=Util.getProperty(new String[]{Global.UDP_IP_TTL}, props, "ip_ttl", false, "64"); if(str != null) { ip_ttl=Integer.parseInt(str); props.remove("ip_ttl"); } str=props.getProperty("tos"); if(str != null) { tos=Integer.parseInt(str); props.remove("tos"); } str=props.getProperty("mcast_send_buf_size"); if(str != null) { mcast_send_buf_size=Integer.parseInt(str); props.remove("mcast_send_buf_size"); } str=props.getProperty("mcast_recv_buf_size"); if(str != null) { mcast_recv_buf_size=Integer.parseInt(str); props.remove("mcast_recv_buf_size"); } str=props.getProperty("ucast_send_buf_size"); if(str != null) { ucast_send_buf_size=Integer.parseInt(str); props.remove("ucast_send_buf_size"); } str=props.getProperty("ucast_recv_buf_size"); if(str != null) { ucast_recv_buf_size=Integer.parseInt(str); props.remove("ucast_recv_buf_size"); } str=props.getProperty("null_src_addresses"); if(str != null) { // null_src_addresses=Boolean.valueOf(str).booleanValue(); props.remove("null_src_addresses"); log.error("null_src_addresses has been deprecated, property will be ignored"); } //https://jira.jboss.org/jira/browse/JGRP-865 //https://jira.jboss.org/jira/browse/JGRP-606 str=props.getProperty("end_port"); if(str != null) { port_range=Integer.parseInt(str) - bind_port; props.remove("end_port"); } Util.checkBufferSize("UDP.mcast_send_buf_size", mcast_send_buf_size); Util.checkBufferSize("UDP.mcast_recv_buf_size", mcast_recv_buf_size); Util.checkBufferSize("UDP.ucast_send_buf_size", ucast_send_buf_size); Util.checkBufferSize("UDP.ucast_recv_buf_size", ucast_recv_buf_size); return props.isEmpty(); } public void setMcastPort(int p) { mcast_port=p; } /* ----------------------- Receiving of MCAST UDP packets ------------------------ */ public void run() { DatagramPacket packet; byte receive_buf[]=new byte[65535]; int offset, len, sender_port; byte[] data; InetAddress sender_addr; Address sender; // moved out of loop to avoid excessive object creations (bela March 8 2001) packet=new DatagramPacket(receive_buf, receive_buf.length); while(mcast_receiver != null && mcast_sock != null) { try { packet.setData(receive_buf, 0, receive_buf.length); mcast_sock.receive(packet); sender_addr=packet.getAddress(); sender_port=packet.getPort(); offset=packet.getOffset(); len=packet.getLength(); data=packet.getData(); sender=new IpAddress(sender_addr, sender_port); if(len > receive_buf.length) { if(log.isErrorEnabled()) log.error("size of the received packet (" + len + ") is bigger than " + "allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); } receive(mcast_addr, sender, data, offset, len); } catch(SocketException sock_ex) { if(log.isTraceEnabled()) log.trace("multicast socket is closed, exception=" + sock_ex); break; } catch(InterruptedIOException io_ex) { // thread was interrupted } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failure in multicast receive()", ex); // Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) } } if(log.isDebugEnabled()) log.debug("multicast thread terminated"); } public String getInfo() { StringBuilder sb=new StringBuilder(); sb.append("group_addr=").append(mcast_addr_name).append(':').append(mcast_port).append("\n"); return sb.toString(); } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { if(ip_mcast && mcast_addr != null) { _send(mcast_addr.getIpAddress(), mcast_addr.getPort(), true, data, offset, length); } else { List
    mbrs; synchronized(members) { mbrs=new ArrayList
    (members); } for(Address mbr: mbrs) { _send(((IpAddress)mbr).getIpAddress(), ((IpAddress)mbr).getPort(), false, data, offset, length); } } } public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { _send(((IpAddress)dest).getIpAddress(), ((IpAddress)dest).getPort(), false, data, offset, length); } public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { if(multicast) msg.setDest(null); else msg.setDest(dest); } public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { if(multicast) msg.setDest(null); else msg.setDest(dest); } private void _send(InetAddress dest, int port, boolean mcast, byte[] data, int offset, int length) throws Exception { DatagramPacket packet=new DatagramPacket(data, offset, length, dest, port); try { if(mcast) { if(mcast_send_sockets != null) { MulticastSocket s; for(int i=0; i < mcast_send_sockets.length; i++) { s=mcast_send_sockets[i]; try { s.send(packet); } catch(Exception e) { log.error("failed sending packet on socket " + s); } } } else { // DEFAULT path if(mcast_sock != null) mcast_sock.send(packet); } } else { if(sock != null) sock.send(packet); } } catch(Exception ex) { throw new Exception("dest=" + dest + ":" + port + " (" + length + " bytes)", ex); } } /* ------------------------------------------------------------------------------- */ /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return "UDP"; } /** * Creates the unicast and multicast sockets and starts the unicast and multicast receiver threads */ public void start() throws Exception { if(log.isDebugEnabled()) log.debug("creating sockets and starting threads"); try { createSockets(); } catch(Exception ex) { String tmp="problem creating sockets (bind_addr=" + bind_addr + ", mcast_addr=" + mcast_addr + ")"; throw new Exception(tmp, ex); } super.start(); // startThreads(); // moved to handleConnect() } public void stop() { if(log.isDebugEnabled()) log.debug("closing sockets and stopping threads"); stopThreads(); // will close sockets, closeSockets() is not really needed anymore, but... closeSockets(); // ... we'll leave it in there for now (doesn't do anything if already closed) super.stop(); } protected void handleConnect() throws Exception { if(singleton_name != null && singleton_name.length() > 0) { if(connect_count == 0) { startThreads(); } super.handleConnect(); } else startThreads(); } protected void handleDisconnect() { if(singleton_name != null && singleton_name.length() > 0) { super.handleDisconnect(); if(connect_count == 0) { stopThreads(); } } else stopThreads(); } /*--------------------------- End of Protocol interface -------------------------- */ /* ------------------------------ Private Methods -------------------------------- */ /** * Create UDP sender and receiver sockets. Currently there are 2 sockets * (sending and receiving). This is due to Linux's non-BSD compatibility * in the JDK port (see DESIGN). */ private void createSockets() throws Exception { InetAddress tmp_addr; // bind_addr not set, try to assign one by default. This is needed on Windows // changed by bela Feb 12 2003: by default multicast sockets will be bound to all network interfaces // CHANGED *BACK* by bela March 13 2003: binding to all interfaces did not result in a correct // local_addr. As a matter of fact, comparison between e.g. 0.0.0.0:1234 (on hostA) and // 0.0.0.0:1.2.3.4 (on hostB) would fail ! // if(bind_addr == null) { // InetAddress[] interfaces=InetAddress.getAllByName(InetAddress.getLocalHost().getHostAddress()); // if(interfaces != null && interfaces.length > 0) // bind_addr=interfaces[0]; // } if(bind_addr == null && !use_local_host) { bind_addr=Util.getFirstNonLoopbackAddress(); } if(bind_addr == null) bind_addr=InetAddress.getLocalHost(); if(bind_addr != null) if(log.isDebugEnabled()) log.debug("sockets will use interface " + bind_addr.getHostAddress()); // 2. Create socket for receiving unicast UDP packets. The address and port // of this socket will be our local address (local_addr) if(bind_port > 0) { sock=createDatagramSocketWithBindPort(); } else { sock=createEphemeralDatagramSocket(); } if(tos > 0) { try { sock.setTrafficClass(tos); } catch(SocketException e) { log.warn("traffic class of " + tos + " could not be set, will be ignored"); if(log.isDebugEnabled()) log.debug("Cause of failure to set traffic class:", e); } } if(sock == null) throw new Exception("UDP.createSocket(): sock is null"); local_addr=createLocalAddress(); if(additional_data != null) ((IpAddress)local_addr).setAdditionalData(additional_data); // 3. Create socket for receiving IP multicast packets if(ip_mcast) { // 3a. Create mcast receiver socket tmp_addr=InetAddress.getByName(mcast_addr_name); // https://jira.jboss.org/jira/browse/JGRP-777 - this doesn't work on MacOS, and we don't have // cross talking on Windows anyway, so we just do it for Linux. (How about Solaris ?) if(can_bind_to_mcast_addr) mcast_sock=Util.createMulticastSocket(tmp_addr, mcast_port, log); else mcast_sock=new MulticastSocket(mcast_port); mcast_sock.setTimeToLive(ip_ttl); mcast_addr=new IpAddress(tmp_addr, mcast_port); if(tos > 0) { try { mcast_sock.setTrafficClass(tos); } catch(SocketException e) { log.warn("traffic class of " + tos + " could not be set, will be ignored", e); } } if(receive_on_all_interfaces || (receive_interfaces != null && !receive_interfaces.isEmpty())) { List interfaces; if(receive_interfaces != null) interfaces=receive_interfaces; else interfaces=Util.getAllAvailableInterfaces(); bindToInterfaces(interfaces, mcast_sock, mcast_addr.getIpAddress()); } else { if(bind_addr != null) mcast_sock.setInterface(bind_addr); mcast_sock.joinGroup(tmp_addr); } // 3b. Create mcast sender socket if(send_on_all_interfaces || (send_interfaces != null && !send_interfaces.isEmpty())) { List interfaces; if(send_interfaces != null) interfaces=send_interfaces; else interfaces=Util.getAllAvailableInterfaces(); mcast_send_sockets=new MulticastSocket[interfaces.size()]; int index=0; for(NetworkInterface intf: interfaces) { mcast_send_sockets[index]=new MulticastSocket(); mcast_send_sockets[index].setNetworkInterface(intf); mcast_send_sockets[index].setTimeToLive(ip_ttl); if(tos > 0) { try { mcast_send_sockets[index].setTrafficClass(tos); } catch(SocketException e) { log.warn("traffic class of " + tos + " could not be set, will be ignored", e); } } index++; } } } setBufferSizes(); if(log.isDebugEnabled()) log.debug("socket information:\n" + dumpSocketInfo()); } protected Address createLocalAddress() { return new IpAddress(sock.getLocalAddress(), sock.getLocalPort()); } /** * * @param interfaces List. Guaranteed to have no duplicates * @param s * @param mcastAddr * @throws IOException */ private void bindToInterfaces(List interfaces, MulticastSocket s, InetAddress mcastAddr) throws IOException { SocketAddress tmp_mcast_addr=new InetSocketAddress(mcastAddr, mcast_port); for(NetworkInterface intf: interfaces) { s.joinGroup(tmp_mcast_addr, intf); if(log.isTraceEnabled()) log.trace("joined " + tmp_mcast_addr + " on " + intf.getName()); } } /** Creates a DatagramSocket with a random port. Because in certain operating systems, ports are reused, * we keep a list of the n last used ports, and avoid port reuse */ protected DatagramSocket createEphemeralDatagramSocket() throws SocketException { DatagramSocket tmp; int localPort=0; while(true) { try { tmp=new DatagramSocket(localPort, bind_addr); // first time localPort is 0 } catch(SocketException socket_ex) { // Vladimir May 30th 2007 // special handling for Linux 2.6 kernel which sometimes throws BindException while we probe for a random port localPort++; continue; } localPort=tmp.getLocalPort(); if(last_ports_used.contains(localPort)) { if(log.isDebugEnabled()) log.debug("local port " + localPort + " already seen in this session; will try to get other port"); try {tmp.close();} catch(Throwable e) {} localPort++; } else { last_ports_used.add(localPort); break; } } return tmp; } /** * Creates a DatagramSocket when bind_port > 0. Attempts to allocate the socket with port == bind_port, and * increments until it finds a valid port, or until port_range has been exceeded * @return DatagramSocket The newly created socket * @throws Exception */ protected DatagramSocket createDatagramSocketWithBindPort() throws Exception { DatagramSocket tmp=null; // 27-6-2003 bgooren, find available port in range (start_port, start_port+port_range) int rcv_port=bind_port, max_port=bind_port + port_range; if(pm != null && bind_port > 0) { rcv_port=pm.getNextAvailablePort(rcv_port); } while(rcv_port <= max_port) { try { tmp=new DatagramSocket(rcv_port, bind_addr); return tmp; } catch(SocketException bind_ex) { // Cannot listen on this port rcv_port++; } catch(SecurityException sec_ex) { // Not allowed to listen on this port rcv_port++; } } // Cannot listen at all, throw an Exception if(rcv_port >= max_port + 1) { // +1 due to the increment above throw new Exception("failed to open a port in range " + bind_port + '-' + max_port); } return tmp; } private String dumpSocketInfo() throws Exception { StringBuilder sb=new StringBuilder(128); sb.append("local_addr=").append(local_addr); sb.append(", mcast_addr=").append(mcast_addr); sb.append(", bind_addr=").append(bind_addr); sb.append(", ttl=").append(ip_ttl); if(sock != null) { sb.append("\nsock: bound to "); sb.append(sock.getLocalAddress().getHostAddress()).append(':').append(sock.getLocalPort()); sb.append(", receive buffer size=").append(sock.getReceiveBufferSize()); sb.append(", send buffer size=").append(sock.getSendBufferSize()); } if(mcast_sock != null) { sb.append("\nmcast_sock: bound to "); sb.append(mcast_sock.getInterface().getHostAddress()).append(':').append(mcast_sock.getLocalPort()); sb.append(", send buffer size=").append(mcast_sock.getSendBufferSize()); sb.append(", receive buffer size=").append(mcast_sock.getReceiveBufferSize()); } if(mcast_send_sockets != null) { sb.append("\n").append(mcast_send_sockets.length).append(" mcast send sockets:\n"); MulticastSocket s; for(int i=0; i < mcast_send_sockets.length; i++) { s=mcast_send_sockets[i]; sb.append(s.getInterface().getHostAddress()).append(':').append(s.getLocalPort()); sb.append(", send buffer size=").append(s.getSendBufferSize()); sb.append(", receive buffer size=").append(s.getReceiveBufferSize()).append("\n"); } } return sb.toString(); } void setBufferSizes() { if(sock != null) setBufferSize(sock, ucast_send_buf_size, ucast_recv_buf_size); if(mcast_sock != null) setBufferSize(mcast_sock, mcast_send_buf_size, mcast_recv_buf_size); if(mcast_send_sockets != null) { for(int i=0; i < mcast_send_sockets.length; i++) { setBufferSize(mcast_send_sockets[i], mcast_send_buf_size, mcast_recv_buf_size); } } } private void setBufferSize(DatagramSocket sock, int send_buf_size, int recv_buf_size) { try { sock.setSendBufferSize(send_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting send buffer size of " + send_buf_size + " in " + sock + ": " + ex); } try { sock.setReceiveBufferSize(recv_buf_size); } catch(Throwable ex) { if(log.isWarnEnabled()) log.warn("failed setting receive buffer size of " + recv_buf_size + " in " + sock + ": " + ex); } } /** * Closed UDP unicast and multicast sockets */ void closeSockets() { // 1. Close multicast socket closeMulticastSocket(); // 2. Close socket closeSocket(); } void closeMulticastSocket() { if(mcast_sock != null) { try { if(mcast_addr != null) { mcast_sock.leaveGroup(mcast_addr.getIpAddress()); } mcast_sock.close(); // this will cause the mcast receiver thread to break out of its loop mcast_sock=null; if(log.isDebugEnabled()) log.debug("multicast socket closed"); } catch(IOException ex) { } mcast_addr=null; } if(mcast_send_sockets != null) { MulticastSocket s; for(int i=0; i < mcast_send_sockets.length; i++) { s=mcast_send_sockets[i]; s.close(); if(log.isDebugEnabled()) log.debug("multicast send socket " + s + " closed"); } mcast_send_sockets=null; } } private void closeSocket() { if(sock != null) { if(pm != null && bind_port > 0) { int port=local_addr != null? ((IpAddress)local_addr).getPort() : sock.getLocalPort(); pm.updatePort(port); } sock.close(); sock=null; if(log.isDebugEnabled()) log.debug("socket closed"); } } /** * Starts the unicast and multicast receiver threads */ void startThreads() throws Exception { if(ucast_receiver == null) { //start the listener thread of the ucast_recv_sock ucast_receiver=new UcastReceiver(); ucast_receiver.start(); global_thread_factory.renameThread(UcastReceiver.UCAST_RECEIVER_THREAD_NAME, ucast_receiver.getThread()); if(log.isDebugEnabled()) log.debug("created unicast receiver thread " + ucast_receiver.getThread()); } if(ip_mcast) { if(mcast_receiver != null) { if(mcast_receiver.isAlive()) { if(log.isDebugEnabled()) log.debug("did not create new multicastreceiver thread as existing " + "multicast receiver thread is still running"); } else mcast_receiver=null; // will be created just below... } if(mcast_receiver == null) { mcast_receiver=global_thread_factory.newThread(this,MCAST_RECEIVER_THREAD_NAME); mcast_receiver.setPriority(Thread.MAX_PRIORITY); // needed ???? mcast_receiver.start(); if(log.isDebugEnabled()) log.debug("created multicast receiver thread " + mcast_receiver); } } } /** * Stops unicast and multicast receiver threads */ void stopThreads() { Thread tmp; // 1. Stop the multicast receiver thread if(mcast_receiver != null) { if(mcast_receiver.isAlive()) { tmp=mcast_receiver; mcast_receiver=null; closeMulticastSocket(); // will cause the multicast thread to terminate tmp.interrupt(); try { tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } tmp=null; } mcast_receiver=null; } // 2. Stop the unicast receiver thread if(ucast_receiver != null) { ucast_receiver.stop(); ucast_receiver=null; } } protected void setThreadNames() { super.setThreadNames(); global_thread_factory.renameThread(MCAST_RECEIVER_THREAD_NAME, mcast_receiver); if(ucast_receiver != null) global_thread_factory.renameThread(UcastReceiver.UCAST_RECEIVER_THREAD_NAME, ucast_receiver.getThread()); } protected void unsetThreadNames() { super.unsetThreadNames(); if(mcast_receiver != null) mcast_receiver.setName(MCAST_RECEIVER_THREAD_NAME); if(ucast_receiver != null && ucast_receiver.getThread() != null) ucast_receiver.getThread().setName(UcastReceiver.UCAST_RECEIVER_THREAD_NAME); } protected void handleConfigEvent(Map map) { boolean set_buffers=false; super.handleConfigEvent(map); if(map == null) return; if(map.containsKey("send_buf_size")) { mcast_send_buf_size=((Integer)map.get("send_buf_size")).intValue(); ucast_send_buf_size=mcast_send_buf_size; set_buffers=true; } if(map.containsKey("recv_buf_size")) { mcast_recv_buf_size=((Integer)map.get("recv_buf_size")).intValue(); ucast_recv_buf_size=mcast_recv_buf_size; set_buffers=true; } if(set_buffers) setBufferSizes(); } /* ----------------------------- End of Private Methods ---------------------------------------- */ /* ----------------------------- Inner Classes ---------------------------------------- */ public class UcastReceiver implements Runnable { public static final String UCAST_RECEIVER_THREAD_NAME = "UDP ucast"; boolean running=true; Thread thread=null; public Thread getThread(){ return thread; } public void start() { if(thread == null) { thread=global_thread_factory.newThread(this,UCAST_RECEIVER_THREAD_NAME); // thread.setDaemon(true); running=true; thread.start(); } } public void stop() { Thread tmp; if(thread != null && thread.isAlive()) { running=false; tmp=thread; thread=null; closeSocket(); // this will cause the thread to break out of its loop tmp.interrupt(); try { tmp.join(Global.THREAD_SHUTDOWN_WAIT_TIME); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again } tmp=null; } thread=null; } public void run() { DatagramPacket packet; byte receive_buf[]=new byte[65535]; int offset, len; byte[] data; InetAddress sender_addr; int sender_port; Address sender; // moved out of loop to avoid excessive object creations (bela March 8 2001) packet=new DatagramPacket(receive_buf, receive_buf.length); while(running && thread != null && sock != null) { try { packet.setData(receive_buf, 0, receive_buf.length); sock.receive(packet); sender_addr=packet.getAddress(); sender_port=packet.getPort(); offset=packet.getOffset(); len=packet.getLength(); data=packet.getData(); sender=new IpAddress(sender_addr, sender_port); if(len > receive_buf.length) { if(log.isErrorEnabled()) log.error("size of the received packet (" + len + ") is bigger than allocated buffer (" + receive_buf.length + "): will not be able to handle packet. " + "Use the FRAG protocol and make its frag_size lower than " + receive_buf.length); } receive(local_addr, sender, data, offset, len); } catch(SocketException sock_ex) { if(log.isDebugEnabled()) log.debug("unicast receiver socket is closed, exception=" + sock_ex); break; } catch(InterruptedIOException io_ex) { // thread was interrupted } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("[" + local_addr + "] failed receiving unicast packet", ex); // Util.sleep(100); // so we don't get into 100% cpu spinning (should NEVER happen !) } } if(log.isDebugEnabled()) log.debug("unicast receiver thread terminated"); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FC.java0000644000175000017500000010651111366547366024570 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.BoundedList; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Simple flow control protocol based on a credit system. Each sender has a number of credits (bytes * to send). When the credits have been exhausted, the sender blocks. Each receiver also keeps track of * how many credits it has received from a sender. When credits for a sender fall below a threshold, * the receiver sends more credits to the sender. Works for both unicast and multicast messages. *

    * Note that this protocol must be located towards the top of the stack, or all down_threads from JChannel to this * protocol must be set to false ! This is in order to block JChannel.send()/JChannel.down(). *
    This is the second simplified implementation of the same model. The algorithm is sketched out in * doc/FlowControl.txt *
    * Changes (Brian) April 2006: *

      *
    1. Receivers now send credits to a sender when more than min_credits have been received (rather than when min_credits * are left) *
    2. Receivers don't send the full credits (max_credits), but rather tha actual number of bytes received *
        * @author Bela Ban * @version $Id: FC.java,v 1.90.2.6 2009/03/30 14:05:22 belaban Exp $ */ public class FC extends Protocol { /** * Map: keys are members, values are credits left. For each send, the * number of credits is decremented by the message size. A HashMap rather than a ConcurrentHashMap is * currently used as there might be null values */ @GuardedBy("sent_lock") private final Map sent=new HashMap(11); // final Map sent=new ConcurrentHashMap(11); /** * Map: keys are members, values are credits left (in bytes). * For each receive, the credits for the sender are decremented by the size of the received message. * When the credits are 0, we refill and send a CREDIT message to the sender. Sender blocks until CREDIT * is received after reaching min_credits credits. */ @GuardedBy("received_lock") private final Map received=new ConcurrentHashMap(11); /** * List of members from whom we expect credits */ @GuardedBy("sent_lock") private final Set
        creditors=new HashSet
        (11); /** Peers who have asked for credit that we didn't have */ private final Set
        pending_requesters=new HashSet
        (11); /** * Max number of bytes to send per receiver until an ack must * be received before continuing sending */ private long max_credits=500000; /** * Max time (in milliseconds) to block. If credit hasn't been received after max_block_time, we send * a REPLENISHMENT request to the members from which we expect credits. A value <= 0 means to * wait forever. Note that when max_block_time elapses, we do not send the message, instead we send only * a credit request. If we want a message to get sent after N milliseconds, use max_block_times instead. */ private long max_block_time=5000; /** * Defines the max number of milliseconds for a message to block before being sent, based on the length of * the message. The property is defined as a comma-separated list of values (separated by ':'), where the key * is the size in bytes and the value is the number of milliseconds to block. * Example: max_block_times="50:1,500:3,1500:5,10000:10,100000:100". This means that messages up to 50 bytes wait * 1 ms max until they get sent, messages up to 500 bytes 3 ms, and so on. * If a message's length (size of the payload in bytes) is for example 15'000 bytes, * FC blocks it for a max of 100 ms. */ private Map max_block_times=null; /** Keeps track of the end time after which a message should not get blocked anymore */ private static final ThreadLocal end_time=new ThreadLocal(); /** * If credits fall below this limit, we send more credits to the sender. (We also send when * credits are exhausted (0 credits left)) */ private double min_threshold=0.25; /** * Computed as max_credits times min_theshold. If explicitly set, this will * override the above computation */ private long min_credits=0; /** * Whether FC is still running, this is set to false when the protocol terminates (on stop()) */ private boolean running=true; private boolean frag_size_received=false; /** * the lowest credits of any destination (sent_msgs) */ @GuardedBy("sent_lock") private long lowest_credit=max_credits; /** Lock protecting sent credits table and some other vars (creditors for example) */ private final Lock sent_lock=new ReentrantLock(); /** Lock protecting received credits table */ private final Lock received_lock=new ReentrantLock(); /** Mutex to block on down() */ private final Condition credits_available=sent_lock.newCondition(); /** * Whether an up thread that comes back down should be allowed to * bypass blocking if all credits are exhausted. Avoids JGRP-465. * Set to false by default in 2.5 because we have OOB messages for credit replenishments - this flag should not be set * to true if the concurrent stack is used */ private boolean ignore_synchronous_response=true; /** * Thread that carries messages through up() and shouldn't be blocked * in down() if ignore_synchronous_response==true. JGRP-465. */ private final ThreadLocal ignore_thread=new ThreadLocal() { protected Boolean initialValue() { return false; } }; private static final String name="FC"; /** Last time a credit request was sent. Used to prevent credit request storms */ @GuardedBy("sent_lock") private long last_credit_request=0; private int num_blockings=0; private int num_credit_requests_received=0, num_credit_requests_sent=0; private int num_credit_responses_sent=0, num_credit_responses_received=0; private long total_time_blocking=0; private final BoundedList last_blockings=new BoundedList(50); private final static FcHeader REPLENISH_HDR=new FcHeader(FcHeader.REPLENISH); private final static FcHeader CREDIT_REQUEST_HDR=new FcHeader(FcHeader.CREDIT_REQUEST); public final String getName() { return name; } public void resetStats() { super.resetStats(); num_blockings=0; num_credit_responses_sent=num_credit_responses_received=num_credit_requests_received=num_credit_requests_sent=0; total_time_blocking=0; last_blockings.clear(); } public long getMaxCredits() { return max_credits; } public void setMaxCredits(long max_credits) { this.max_credits=max_credits; } public double getMinThreshold() { return min_threshold; } public void setMinThreshold(double min_threshold) { this.min_threshold=min_threshold; } public long getMinCredits() { return min_credits; } public void setMinCredits(long min_credits) { this.min_credits=min_credits; } public int getNumberOfBlockings() { return num_blockings; } public long getMaxBlockTime() { return max_block_time; } public void setMaxBlockTime(long t) { max_block_time=t; } public long getTotalTimeBlocked() { return total_time_blocking; } public double getAverageTimeBlocked() { return num_blockings == 0? 0.0 : total_time_blocking / (double)num_blockings; } public int getNumberOfCreditRequestsReceived() { return num_credit_requests_received; } public int getNumberOfCreditRequestsSent() { return num_credit_requests_sent; } public int getNumberOfCreditResponsesReceived() { return num_credit_responses_received; } public int getNumberOfCreditResponsesSent() { return num_credit_responses_sent; } public String printSenderCredits() { return printMap(sent); } public String printReceiverCredits() { return printMap(received); } public String printCredits() { StringBuilder sb=new StringBuilder(); sb.append("senders:\n").append(printMap(sent)).append("\n\nreceivers:\n").append(printMap(received)); return sb.toString(); } public Map dumpStats() { Map retval=super.dumpStats(); if(retval == null) retval=new HashMap(); retval.put("senders", printMap(sent)); retval.put("receivers", printMap(received)); retval.put("num_blockings", this.num_blockings); retval.put("avg_time_blocked", getAverageTimeBlocked()); retval.put("num_replenishments", this.num_credit_responses_received); retval.put("total_time_blocked", total_time_blocking); retval.put("num_credit_requests", (long)num_credit_requests_sent); return retval; } public String showLastBlockingTimes() { return last_blockings.toString(); } private long getMaxBlockTime(long length) { if(max_block_times == null) return 0; Long retval=null; for(Map.Entry entry: max_block_times.entrySet()) { retval=entry.getValue(); if(length <= entry.getKey()) break; } return retval != null? retval : 0; } /** * Allows to unblock a blocked sender from an external program, e.g. JMX */ public void unblock() { sent_lock.lock(); try { if(log.isTraceEnabled()) log.trace("unblocking the sender and replenishing all members, creditors are " + creditors); for(Map.Entry entry: sent.entrySet()) { entry.setValue(max_credits); } lowest_credit=computeLowestCredit(sent); creditors.clear(); credits_available.signalAll(); } finally { sent_lock.unlock(); } } public boolean setProperties(Properties props) { String str; boolean min_credits_set=false; super.setProperties(props); str=props.getProperty("max_credits"); if(str != null) { max_credits=Long.parseLong(str); props.remove("max_credits"); } str=props.getProperty("min_threshold"); if(str != null) { min_threshold=Double.parseDouble(str); props.remove("min_threshold"); } str=props.getProperty("min_credits"); if(str != null) { min_credits=Long.parseLong(str); props.remove("min_credits"); min_credits_set=true; } if(!min_credits_set) min_credits=(long)((double)max_credits * min_threshold); str=props.getProperty("max_block_time"); if(str != null) { max_block_time=Long.parseLong(str); props.remove("max_block_time"); } str=props.getProperty("max_block_times"); if(str != null) { Long prev_key=null, prev_val=null; List vals=Util.parseCommaDelimitedStrings(str); max_block_times=new TreeMap(); for(String tmp: vals) { int index=tmp.indexOf(':'); if(index == -1) throw new IllegalArgumentException("element '" + tmp + "' is missing a ':' separator"); Long key=Long.parseLong(tmp.substring(0, index).trim()); Long val=Long.parseLong(tmp.substring(index +1).trim()); // sanity checks: if(key < 0 || val < 0) throw new IllegalArgumentException("keys and values must be >= 0"); if(prev_key != null) { if(key <= prev_key) throw new IllegalArgumentException("keys are not sorted: " + vals); } prev_key=key; if(prev_val != null) { if(val <= prev_val) throw new IllegalArgumentException("values are not sorted: " + vals); } prev_val=val; max_block_times.put(key, val); } if(log.isDebugEnabled()) log.debug("max_block_times: " + max_block_times); props.remove("max_block_times"); } Util.checkBufferSize("FC.max_credits", max_credits); str=props.getProperty("ignore_synchronous_response"); if(str != null) { ignore_synchronous_response=Boolean.valueOf(str); props.remove("ignore_synchronous_response"); } if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void start() throws Exception { super.start(); if(!frag_size_received) { log.warn("No fragmentation protocol was found. When flow control (e.g. FC or SFC) is used, we recommend " + "a fragmentation protocol, due to http://jira.jboss.com/jira/browse/JGRP-590"); } sent_lock.lock(); try { running=true; lowest_credit=max_credits; } finally { sent_lock.unlock(); } } public void stop() { super.stop(); sent_lock.lock(); try { running=false; ignore_thread.set(false); credits_available.signalAll(); // notify all threads waiting on the mutex that we are done } finally { sent_lock.unlock(); } } public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: return handleDownMessage(evt); case Event.INFO: handleInfo((Map)evt.getArg()); return null; case Event.VIEW_CHANGE: handleViewChange(((View)evt.getArg()).getMembers()); break; } return down_prot.down(evt); // this could potentially use the lower protocol's thread which may block } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: // JGRP-465. We only deal with msgs to avoid having to use a concurrent collection; ignore views, // suspicions, etc which can come up on unusual threads Message msg=(Message)evt.getArg(); FcHeader hdr=(FcHeader)msg.getHeader(name); if(hdr != null) { switch(hdr.type) { case FcHeader.REPLENISH: num_credit_responses_received++; handleCredit(msg.getSrc(), (Number)msg.getObject()); break; case FcHeader.CREDIT_REQUEST: num_credit_requests_received++; Address sender=msg.getSrc(); Long sent_credits=(Long)msg.getObject(); handleCreditRequest(received, received_lock, sender, sent_credits); break; default: log.error("header type " + hdr.type + " not known"); break; } return null; // don't pass message up } Address sender=msg.getSrc(); long new_credits=adjustCredit(received, received_lock, sender, msg.getLength()); // JGRP-928: changed ignore_thread to a ThreadLocal: multiple threads can access it with the // introduction of the concurrent stack if(ignore_synchronous_response) ignore_thread.set(true); try { return up_prot.up(evt); } finally { if(ignore_synchronous_response) ignore_thread.set(false); // need to revert because the thread is placed back into the pool if(new_credits > 0) { if(log.isTraceEnabled()) log.trace("sending " + new_credits + " credits to " + sender); sendCredit(sender, new_credits); } } case Event.VIEW_CHANGE: handleViewChange(((View)evt.getArg()).getMembers()); break; case Event.INFO: Map map=(Map)evt.getArg(); handleInfo(map); break; } return up_prot.up(evt); } private void handleInfo(Map info) { if(info != null) { Integer frag_size=(Integer)info.get("frag_size"); if(frag_size != null) { if(frag_size > max_credits) { log.warn("The fragmentation size of the fragmentation protocol is " + frag_size + ", which is greater than the max credits. While this is not incorrect, " + "it may lead to long blockings. Frag size should be less than max_credits " + "(http://jira.jboss.com/jira/browse/JGRP-590)"); } frag_size_received=true; } } } private Object handleDownMessage(Event evt) { Message msg=(Message)evt.getArg(); int length=msg.getLength(); Address dest=msg.getDest(); if(max_block_times != null) { long tmp=getMaxBlockTime(length); if(tmp > 0) end_time.set(System.currentTimeMillis() + tmp); } sent_lock.lock(); try { if(length > lowest_credit) { // then block and loop asking for credits until enough credits are available if(ignore_synchronous_response && ignore_thread.get()) { // JGRP-465 if(log.isTraceEnabled()) log.trace("bypassing blocking to avoid deadlocking " + Thread.currentThread()); } else { determineCreditors(dest, length); long start_blocking=System.currentTimeMillis(); num_blockings++; // we count overall blockings, not blockings for *all* threads if(log.isTraceEnabled()) log.trace("Starting blocking. lowest_credit=" + lowest_credit + "; msg length =" + length); while(length > lowest_credit && running) { try { long block_time=max_block_time; if(max_block_times != null) { Long tmp=end_time.get(); if(tmp != null) { // Negative value means we don't wait at all ! If the end_time already elapsed // (because we waited for other threads to get processed), the message will not // block at all and get sent immediately block_time=tmp - start_blocking; } } boolean rc=credits_available.await(block_time, TimeUnit.MILLISECONDS); if(rc || length <= lowest_credit || !running) break; // if we use max_block_times, then we do *not* send credit requests, even if we run // into timeouts: in this case, it is up to the receivers to send new credits if(!rc && max_block_times != null) break; long wait_time=System.currentTimeMillis() - last_credit_request; if(wait_time >= max_block_time) { // we have to set this var now, because we release the lock below (for sending a // credit request), so all blocked threads would send a credit request, leading to // a credit request storm last_credit_request=System.currentTimeMillis(); // we need to send the credit requests down *without* holding the sent_lock, otherwise we might // run into the deadlock described in http://jira.jboss.com/jira/browse/JGRP-292 Map sent_copy=new HashMap(sent); sent_copy.keySet().retainAll(creditors); sent_lock.unlock(); try { // System.out.println(new Date() + " --> credit request"); for(Map.Entry entry: sent_copy.entrySet()) { sendCreditRequest(entry.getKey(), entry.getValue()); } } finally { sent_lock.lock(); } } } catch(InterruptedException e) { // set the interrupted flag again, so the caller's thread can handle the interrupt as well // bela June 15 2007: don't do this as this will trigger an infinite loop !! // (http://jira.jboss.com/jira/browse/JGRP-536) // Thread.currentThread().interrupt(); } } // if(!running) // don't send the message if not running anymore // return null; long block_time=System.currentTimeMillis() - start_blocking; if(log.isTraceEnabled()) log.trace("total time blocked: " + block_time + " ms"); total_time_blocking+=block_time; last_blockings.add(block_time); } } long tmp=decrementCredit(sent, dest, length); if(tmp != -1) lowest_credit=Math.min(tmp, lowest_credit); } finally { sent_lock.unlock(); } // send message - either after regular processing, or after blocking (when enough credits available again) return down_prot.down(evt); } /** * Checks whether one member (unicast msg) or all members (multicast msg) have enough credits. Add those * that don't to the creditors list. Called with sent_lock held * @param dest * @param length */ private void determineCreditors(Address dest, int length) { boolean multicast=dest == null || dest.isMulticastAddress(); Address mbr; Long credits; if(multicast) { for(Map.Entry entry: sent.entrySet()) { mbr=entry.getKey(); credits=entry.getValue(); if(credits <= length) creditors.add(mbr); } } else { credits=sent.get(dest); if(credits != null && credits <= length) creditors.add(dest); } } /** * Decrements credits from a single member, or all members in sent_msgs, depending on whether it is a multicast * or unicast message. No need to acquire mutex (must already be held when this method is called) * @param dest * @param credits * @return The lowest number of credits left, or -1 if a unicast member was not found */ private long decrementCredit(Map m, Address dest, long credits) { boolean multicast=dest == null || dest.isMulticastAddress(); long lowest=max_credits, new_credit; Long val; if(multicast) { if(m.isEmpty()) return -1; for(Map.Entry entry: m.entrySet()) { val=entry.getValue(); new_credit=val - credits; entry.setValue(new_credit); lowest=Math.min(new_credit, lowest); } return lowest; } else { val=m.get(dest); if(val != null) { lowest=val; lowest-=credits; m.put(dest, lowest); if(log.isTraceEnabled()) log.trace("sender " + dest + " minus " + credits + " credits, " + lowest + " remaining"); return lowest; } } return -1; } private void handleCredit(Address sender, Number increase) { if(sender == null) return; StringBuilder sb=null; sent_lock.lock(); try { Long old_credit=sent.get(sender); if(old_credit == null) return; Long new_credit=Math.min(max_credits, old_credit + increase.longValue()); if(log.isTraceEnabled()) { sb=new StringBuilder(); sb.append("received credit from ").append(sender).append(", old credit was ").append(old_credit) .append(", new credits are ").append(new_credit).append(".\nCreditors before are: ").append(creditors); } sent.put(sender, new_credit); lowest_credit=computeLowestCredit(sent); // boolean was_empty=true; if(!creditors.isEmpty()) { // we are blocked because we expect credit from one or more members // was_empty=false; creditors.remove(sender); if(log.isTraceEnabled()) { sb.append("\nCreditors after removal of ").append(sender).append(" are: ").append(creditors); log.trace(sb); } } if(creditors.isEmpty()) {// && !was_empty) { credits_available.signalAll(); } } finally { sent_lock.unlock(); } } private static long computeLowestCredit(Map m) { Collection credits=m.values(); // List of Longs (credits) return Collections.min(credits); } /** * Check whether sender has enough credits left. If not, send him some more * @param map The hashmap to use * @param lock The lock which can be used to lock map * @param sender The address of the sender * @param length The number of bytes received by this message. We don't care about the size of the headers for * the purpose of flow control * @return long Number of credits to be sent. Greater than 0 if credits needs to be sent, 0 otherwise */ private long adjustCredit(Map map, final Lock lock, Address sender, int length) { if(sender == null) { if(log.isErrorEnabled()) log.error("src is null"); return 0; } if(length == 0) return 0; // no effect lock.lock(); try { long remaining_cred=decrementCredit(map, sender, length); if(log.isTraceEnabled()) log.trace("sender " + sender + " minus " + length + " credits, " + remaining_cred + " remaining"); if(remaining_cred == -1) return 0; long credit_response=max_credits - remaining_cred; if(credit_response >= min_credits) { map.put(sender, max_credits); return credit_response; // this will trigger sending of new credits as we have received more than min_credits bytes from src } } finally { lock.unlock(); } return 0; } /** * @param map The map to modify * @param lock The lock to lock map * @param sender The sender who requests credits * @param left_credits Number of bytes that the sender has left to send messages to us */ private void handleCreditRequest(Map map, Lock lock, Address sender, Long left_credits) { if(sender == null) return; long credit_response=0; lock.lock(); try { Long old_credit=map.get(sender); if(old_credit != null) { credit_response=Math.min(max_credits, max_credits - old_credit); } if(credit_response > 0) { if(log.isTraceEnabled()) log.trace("received credit request from " + sender + ": sending " + credit_response + " credits"); map.put(sender, max_credits); pending_requesters.remove(sender); } else { if(pending_requesters.contains(sender)) { // a sender might have negative credits, e.g. -20000. If we subtracted -20000 from max_credits, // we'd end up with max_credits + 20000, and send too many credits back. So if the sender's // credits is negative, we simply send max_credits back long credits_left=Math.max(0, left_credits.longValue()); credit_response=max_credits - credits_left; // credit_response = max_credits; map.put(sender, max_credits); pending_requesters.remove(sender); if(log.isWarnEnabled()) log.warn("Received two credit requests from " + sender + " without any intervening messages; sending " + credit_response + " credits"); } else { pending_requesters.add(sender); if(log.isTraceEnabled()) log.trace("received credit request from " + sender + " but have no credits available"); } } } finally { lock.unlock(); } if(credit_response > 0) sendCredit(sender, credit_response); } private void sendCredit(Address dest, long credit) { if(log.isTraceEnabled()) log.trace("replentished " + dest + " with " + credit + " credits"); Number number; if(credit < Integer.MAX_VALUE) number=(int)credit; else number=credit; Message msg=new Message(dest, null, number); msg.setFlag(Message.OOB); msg.putHeader(name, REPLENISH_HDR); down_prot.down(new Event(Event.MSG, msg)); num_credit_responses_sent++; } /** * We cannot send this request as OOB messages, as the credit request needs to queue up behind the regular messages; * if a receiver cannot process the regular messages, that is a sign that the sender should be throttled ! * @param dest The member to which we send the credit request * @param credits_left The number of bytes (of credits) left for dest */ private void sendCreditRequest(final Address dest, Long credits_left) { if(log.isTraceEnabled()) log.trace("sending credit request to " + dest); Message msg=new Message(dest, null, credits_left); msg.putHeader(name, CREDIT_REQUEST_HDR); down_prot.down(new Event(Event.MSG, msg)); num_credit_requests_sent++; } private void handleViewChange(Vector
        mbrs) { Address addr; if(mbrs == null) return; if(log.isTraceEnabled()) log.trace("new membership: " + mbrs); sent_lock.lock(); received_lock.lock(); try { // add members not in membership to received and sent hashmap (with full credits) for(int i=0; i < mbrs.size(); i++) { addr=mbrs.elementAt(i); if(!received.containsKey(addr)) received.put(addr, max_credits); if(!sent.containsKey(addr)) sent.put(addr, max_credits); } // remove members that left for(Iterator
        it=received.keySet().iterator(); it.hasNext();) { addr=it.next(); if(!mbrs.contains(addr)) it.remove(); } // remove members that left for(Iterator
        it=sent.keySet().iterator(); it.hasNext();) { addr=it.next(); if(!mbrs.contains(addr)) it.remove(); // modified the underlying map } // remove all creditors which are not in the new view /*for(Address creditor: creditors) { if(!mbrs.contains(creditor)) creditors.remove(creditor); }*/ // fixed http://jira.jboss.com/jira/browse/JGRP-754 (CCME) for(Iterator
        it=creditors.iterator(); it.hasNext();) { Address creditor=it.next(); if(!mbrs.contains(creditor)) it.remove(); } if(log.isTraceEnabled()) log.trace("creditors are " + creditors); if(creditors.isEmpty()) { lowest_credit=computeLowestCredit(sent); credits_available.signalAll(); } } finally { sent_lock.unlock(); received_lock.unlock(); } } private static String printMap(Map m) { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: m.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } public static class FcHeader extends Header implements Streamable { public static final byte REPLENISH=1; public static final byte CREDIT_REQUEST=2; // the sender of the message is the requester byte type=REPLENISH; private static final long serialVersionUID=8226510881574318828L; public FcHeader() { } public FcHeader(byte type) { this.type=type; } public int size() { return Global.BYTE_SIZE; } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); } public String toString() { switch(type) { case REPLENISH: return "REPLENISH"; case CREDIT_REQUEST: return "CREDIT_REQUEST"; default: return ""; } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TpHeader.java0000644000175000017500000000250211366547366025767 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.util.Streamable; import java.io.*; /** * Generic transport header, used by TP. * @author Bela Ban * @version $Id: TpHeader.java,v 1.4 2007/05/01 10:55:10 belaban Exp $ */ public class TpHeader extends Header implements Streamable { public String channel_name=null; int size=0; public TpHeader() { } // used for externalization public TpHeader(String n) { channel_name=n; if(channel_name != null) size=channel_name.length()+2; // +2 for writeUTF() } public String toString() { return "[channel_name=" + channel_name + ']'; } public int size() { return size; } public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(channel_name); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { channel_name=in.readUTF(); } public void writeTo(DataOutputStream out) throws IOException { out.writeUTF(channel_name); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { channel_name=in.readUTF(); if(channel_name != null) size=channel_name.length()+2; // +2 for writeUTF() } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/BARRIER.java0000644000175000017500000001716011366547366025367 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Event; import org.jgroups.stack.Protocol; import org.jgroups.util.TimeScheduler; import java.util.Properties; import java.util.Set; import java.util.HashSet; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * All messages up the stack have to go through a barrier (read lock, RL). By default, the barrier is open. * When a CLOSE_BARRIER event is received, we close the barrier by acquiring a write lock (WL). This succeeds when all * previous messages have completed (by releasing their RLs). Thus, when we have acquired the WL, we know that there * are no pending messages processed.
        * When an OPEN_BARRIER event is received, we simply open the barrier again and let all messages pass in the up * direction. This is done by releasing the WL. * @author Bela Ban * @version $Id: BARRIER.java,v 1.6.4.5 2009/09/10 10:57:07 belaban Exp $ */ public class BARRIER extends Protocol { long max_close_time=60000; // how long can the barrier stay closed (in ms) ? 0 means forever final Lock lock=new ReentrantLock(); final AtomicBoolean barrier_closed=new AtomicBoolean(false); /** signals to waiting threads that the barrier is open again */ Condition barrier_opened=lock.newCondition(); Condition no_msgs_pending=lock.newCondition(); ConcurrentMap in_flight_threads=new ConcurrentHashMap(); Future barrier_opener_future=null; TimeScheduler timer; private static final Object NULL=new Object(); public String getName() { return "BARRIER"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("max_close_time"); if(str != null) { max_close_time=Long.parseLong(str); props.remove("max_close_time"); } if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } public boolean isClosed() { return barrier_closed.get(); } public boolean isOpenerScheduled() { return barrier_opener_future != null && !barrier_opener_future.isDone() && !barrier_opener_future.isCancelled(); } public long getMaxCloseTime() { return max_close_time; } public int getNumberOfInFlightThreads() { return in_flight_threads.size(); } public void init() throws Exception { super.init(); timer=getTransport().getTimer(); } public void stop() { super.stop(); openBarrier(); } public void destroy() { super.destroy(); openBarrier(); } public Object down(Event evt) { switch(evt.getType()) { case Event.CLOSE_BARRIER: closeBarrier(); return null; case Event.OPEN_BARRIER: openBarrier(); return null; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Thread current_thread=Thread.currentThread(); in_flight_threads.put(current_thread, NULL); if(barrier_closed.get()) { lock.lock(); try { // Feb 28 2008 (Gray Watson): remove myself because barrier is closed in_flight_threads.remove(current_thread); while(barrier_closed.get()) { try { barrier_opened.await(); } catch(InterruptedException e) { } } } finally { // Feb 28 2008 (Gray Watson): barrier is now open, put myself back in_flight in_flight_threads.put(current_thread, NULL); lock.unlock(); } } try { return up_prot.up(evt); } finally { lock.lock(); try { if(in_flight_threads.remove(current_thread) == NULL && barrier_closed.get() && in_flight_threads.isEmpty()) { no_msgs_pending.signalAll(); } } finally { lock.unlock(); } } case Event.CLOSE_BARRIER: closeBarrier(); return null; case Event.OPEN_BARRIER: openBarrier(); return null; } return up_prot.up(evt); } /** Close the barrier. Temporarily remove all threads which are waiting or blocked, re-insert them after the call */ private void closeBarrier() { if(!barrier_closed.compareAndSet(false, true)) return; // barrier was already closed Set threads=new HashSet(); lock.lock(); try { // wait until all pending (= in-progress, runnable threads) msgs have returned in_flight_threads.remove(Thread.currentThread()); while(!in_flight_threads.isEmpty()) { for(Iterator it=in_flight_threads.keySet().iterator(); it.hasNext();) { Thread thread=it.next(); Thread.State state=thread.getState(); if(state != Thread.State.RUNNABLE && state != Thread.State.NEW) { threads.add(thread); it.remove(); } } if(!in_flight_threads.isEmpty()) { try { no_msgs_pending.await(1000, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { } } } } finally { for(Thread thread: threads) in_flight_threads.put(thread, NULL); lock.unlock(); } if(log.isTraceEnabled()) log.trace("barrier was closed"); if(max_close_time > 0) scheduleBarrierOpener(); } private void openBarrier() { lock.lock(); try { if(!barrier_closed.compareAndSet(true, false)) return; // barrier was already open barrier_opened.signalAll(); } finally { lock.unlock(); } if(log.isTraceEnabled()) log.trace("barrier was opened"); cancelBarrierOpener(); // cancels if running } private void scheduleBarrierOpener() { if(barrier_opener_future == null || barrier_opener_future.isDone()) { barrier_opener_future=timer.schedule(new Runnable() {public void run() {openBarrier();}}, max_close_time, TimeUnit.MILLISECONDS ); } } private void cancelBarrierOpener() { if(barrier_opener_future != null) { barrier_opener_future.cancel(true); barrier_opener_future=null; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TCPPING.java0000644000175000017500000001313211366547366025400 0ustar twernertwerner// $Id: TCPPING.java,v 1.32.4.4 2009/09/29 04:36:30 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Global; import org.jgroups.util.Util; import org.jgroups.util.Promise; import org.jgroups.stack.IpAddress; import java.util.*; import java.net.UnknownHostException; /** * The TCPPING protocol layer retrieves the initial membership in answer to the GMS's * FIND_INITIAL_MBRS event. The initial membership is retrieved by directly contacting other group * members, sending point-to-point mebership requests. The responses should allow us to determine * the coordinator whom we have to contact in case we want to join the group. When we are a server * (after having received the BECOME_SERVER event), we'll respond to TCPPING requests with a TCPPING * response. *

        * The FIND_INITIAL_MBRS event will eventually be answered with a FIND_INITIAL_MBRS_OK event up * the stack. *

        * The TCPPING protocol requires a static conifiguration, which assumes that you to know in advance * where to find other members of your group. For dynamic discovery, use the PING protocol, which * uses multicast discovery, or the TCPGOSSIP protocol, which contacts a Gossip Router to acquire * the initial membership. * * @author Bela Ban */ public class TCPPING extends Discovery { int port_range=1; // number of ports to be probed for initial membership /** List */ List

        initial_hosts=null; // hosts to be contacted for the initial membership final static String name="TCPPING"; public String getName() { return name; } /** * Returns the list of initial hosts as configured by the user via XML. Note that the returned list is mutable, so * careful with changes ! * @return List
        list of initial hosts. This variable is only set after the channel has been created and * set Properties() has been called */ public List
        getInitialHosts() { return initial_hosts; } public boolean setProperties(Properties props) { String str; this.props.putAll(props); // redundant str=props.getProperty("port_range"); // if member cannot be contacted on base port, if(str != null) { // how many times can we increment the port port_range=Integer.parseInt(str); if (port_range < 1) { port_range = 1; } props.remove("port_range"); } str=Util.getProperty(new String[]{Global.TCPPING_INITIAL_HOSTS}, props, "initial_hosts", false, null); if(str != null) { props.remove("initial_hosts"); try { initial_hosts=createInitialHosts(str); } catch(UnknownHostException e) { log.error("failed creating initial list of hosts", e); return false; } } return super.setProperties(props); } public void localAddressSet(Address addr) { // Add own address to initial_hosts if not present: we must always be able to ping ourself ! if(initial_hosts != null && addr != null) { if(initial_hosts.contains(addr)) { initial_hosts.remove(addr); if(log.isDebugEnabled()) log.debug("[SET_LOCAL_ADDRESS]: removing my own address (" + addr + ") from initial_hosts; initial_hosts=" + initial_hosts); } } } public void sendGetMembersRequest(Promise promise) throws Exception { for(Iterator
        it = initial_hosts.iterator();it.hasNext();){ final Address addr = it.next(); final Message msg = new Message(addr, null, null); msg.setFlag(Message.OOB); msg.putHeader(name, new PingHeader(PingHeader.GET_MBRS_REQ, null)); if(log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + msg.getDest()); down_prot.down(new Event(Event.MSG, msg)); timer.execute(new Runnable() { public void run() { try{ down_prot.down(new Event(Event.MSG, msg)); }catch(Exception ex){ if(log.isErrorEnabled()) log.error("failed sending discovery request to " + addr, ex); } } }); } } /* -------------------------- Private methods ---------------------------- */ /** * Input is "daddy[8880],sindhu[8880],camille[5555]. Return List of IpAddresses */ private List
        createInitialHosts(String l) throws UnknownHostException { StringTokenizer tok=new StringTokenizer(l, ","); String t; IpAddress addr; List
        retval=new ArrayList
        (); while(tok.hasMoreTokens()) { try { t=tok.nextToken().trim(); String host=t.substring(0, t.indexOf('[')); host=host.trim(); int port=Integer.parseInt(t.substring(t.indexOf('[') + 1, t.indexOf(']'))); for(int i=port; i < port + port_range; i++) { addr=new IpAddress(host, i); retval.add(addr); } } catch(NumberFormatException e) { if(log.isErrorEnabled()) log.error("exeption is " + e); } } return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/VIEW_SYNC.java0000644000175000017500000003122711366547366025707 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.annotations.GuardedBy; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.io.*; import java.util.Properties; import java.util.Vector; import java.util.concurrent.Future; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Periodically sends the view to the group. When a view is received which is greater than the current view, we * install it. Otherwise we simply discard it. This is used to solve the problem for unreliable view * dissemination outlined in JGroups/doc/ReliableViewInstallation.txt. This protocol is supposed to be just below GMS. * @author Bela Ban * @version $Id: VIEW_SYNC.java,v 1.24.2.3 2009/09/29 04:29:43 belaban Exp $ */ public class VIEW_SYNC extends Protocol { Address local_addr=null; final Vector
        mbrs=new Vector
        (); View my_view=null; ViewId my_vid=null; /** Sends a VIEW_SYNC message to the group every 20 seconds on average. 0 disables sending of VIEW_SYNC messages */ long avg_send_interval=60000; private int num_views_sent=0; private int num_view_requests_sent=0; private int num_view_responses_seen=0; private int num_views_non_local=0; private int num_views_equal=0; private int num_views_less=0; private int num_views_adjusted=0; private long last_view_request_sent=0; @GuardedBy("view_task_lock") private Future view_send_task_future=null; // bcasts periodic view sync message (added to timer below) private final Lock view_task_lock=new ReentrantLock(); TimeScheduler timer=null; static final String name="VIEW_SYNC"; public String getName() { return name; } public long getAverageSendInterval() { return avg_send_interval; } public void setAverageSendInterval(long gossip_interval) { avg_send_interval=gossip_interval; } public int getNumViewsSent() { return num_views_sent; } public int getNumViewRequestsSent() { return num_view_requests_sent; } public int getNumViewResponsesSeen() { return num_view_requests_sent; } public int getNumViewsNonLocal() { return num_views_non_local; } public int getNumViewsLess() { return num_views_less; } public int getNumViewsEqual() { return num_views_equal; } public long getLastViewRequestSent() { return last_view_request_sent; } public int getNumViewsAdjusted() { return num_views_adjusted; } public void resetStats() { super.resetStats(); num_views_adjusted=num_views_sent=0; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("avg_send_interval"); if(str != null) { avg_send_interval=Long.parseLong(str); props.remove("avg_send_interval"); } if(!props.isEmpty()) { log.error("these properties are not recognized: " + props); return false; } return true; } public void start() throws Exception { timer=getTransport().getTimer(); if(timer == null) throw new Exception("timer cannot be retrieved from protocol stack"); } public void stop() { stopViewSender(); } /** Sends a VIEW_SYNC_REQ to all members, every member replies with a VIEW multicast */ public void sendViewRequest() { Message msg=new Message(null); msg.setFlag(Message.OOB); ViewSyncHeader hdr=new ViewSyncHeader(ViewSyncHeader.VIEW_SYNC_REQ, null); msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, msg)); num_view_requests_sent++; last_view_request_sent=System.currentTimeMillis(); } // public void sendFakeViewForTestingOnly() { // ViewId fake_vid=new ViewId(local_addr, my_vid.getId() +2); // View fake_view=new View(fake_vid, new Vector(my_view.getMembers())); // System.out.println("sending fake view " + fake_view); // my_view=fake_view; // my_vid=fake_vid; // sendView(); // } public Object up(Event evt) { Message msg; ViewSyncHeader hdr; int type=evt.getType(); switch(type) { case Event.MSG: msg=(Message)evt.getArg(); hdr=(ViewSyncHeader)msg.getHeader(name); if(hdr == null) break; Address sender=msg.getSrc(); switch(hdr.type) { case ViewSyncHeader.VIEW_SYNC: handleView(hdr.view, sender); break; case ViewSyncHeader.VIEW_SYNC_REQ: if(!sender.equals(local_addr)) sendView(); break; default: if(log.isErrorEnabled()) log.error("ViewSyncHeader type " + hdr.type + " not known"); } return null; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); handleViewChange(view); break; case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; } return up_prot.up(evt); } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: View v=(View)evt.getArg(); handleViewChange(v); break; } return down_prot.down(evt); } /* --------------------------------------- Private Methods ---------------------------------------- */ private void handleView(View v, Address sender) { num_view_responses_seen++; Vector
        members=v.getMembers(); if(!members.contains(local_addr)) { if(log.isWarnEnabled()) log.warn("discarding view as I (" + local_addr + ") am not member of view (" + v + ")"); num_views_non_local++; return; } ViewId vid=v.getVid(); int rc=vid.compareTo(my_vid); if(rc > 0) { // foreign view is greater than my own view; update my own view ! if(log.isTraceEnabled()) log.trace("view from " + sender + " (" + vid + ") is greater than my own view (" + my_vid + ");" + " will update my own view"); Message view_change=new Message(local_addr, local_addr, null); org.jgroups.protocols.pbcast.GMS.GmsHeader hdr; hdr=new org.jgroups.protocols.pbcast.GMS.GmsHeader(org.jgroups.protocols.pbcast.GMS.GmsHeader.VIEW, v); view_change.putHeader(GMS.name, hdr); up_prot.up(new Event(Event.MSG, view_change)); num_views_adjusted++; } else if (rc == 0) { if (log.isTraceEnabled()) log.trace("view from " + sender + " (" + vid + ") is same as my own view; ignoring"); num_views_equal++; } else { if (log.isTraceEnabled()) log.trace("view from " + sender + " (" + vid + ") is less than my own view (" + my_vid + "); ignoring"); num_views_less++; } } private void handleViewChange(View view) { Vector
        tmp=view.getMembers(); if(tmp != null) { mbrs.clear(); mbrs.addAll(tmp); } my_view=(View)view.clone(); my_vid=my_view.getVid(); if(my_view.size() > 1) { startViewSender(); } else { stopViewSender(); } } private void sendView() { View tmp=(View)(my_view != null? my_view.clone() : null); if(tmp == null) return; Message msg=new Message(null); // send to the group msg.setFlag(Message.OOB); ViewSyncHeader hdr=new ViewSyncHeader(ViewSyncHeader.VIEW_SYNC, tmp); msg.putHeader(name, hdr); down_prot.down(new Event(Event.MSG, msg)); num_views_sent++; } /** Starts with view_task_lock held, no need to acquire it again */ void startViewSender() { try { view_task_lock.lock(); if(view_send_task_future == null || view_send_task_future.isDone()) { ViewSendTask view_send_task=new ViewSendTask(); view_send_task_future=timer.scheduleWithDynamicInterval(view_send_task); if(log.isTraceEnabled()) log.trace("view send task started"); } } finally { view_task_lock.unlock(); } } void stopViewSender() { try { view_task_lock.lock(); if(view_send_task_future != null) { view_send_task_future.cancel(false); view_send_task_future=null; if(log.isTraceEnabled()) log.trace("view send task stopped"); } } finally { view_task_lock.unlock(); } } /* ------------------------------------End of Private Methods ------------------------------------- */ public static class ViewSyncHeader extends Header implements Streamable { public static final int VIEW_SYNC = 1; // contains a view public static final int VIEW_SYNC_REQ = 2; // request to all members to send their views int type=0; View view=null; public ViewSyncHeader() { } public ViewSyncHeader(int type, View view) { this.type=type; this.view=view; } public int getType() { return type; } public View getView() { return view; } static String type2String(int t) { switch(t) { case VIEW_SYNC: return "VIEW_SYNC"; case VIEW_SYNC_REQ: return "VIEW_SYNC_REQ"; default: return ""; } } public String toString() { StringBuilder sb=new StringBuilder("[").append(type2String(type)).append("]"); if(view != null) sb.append(", view= ").append(view); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(type); if(view == null) { out.writeBoolean(false); return; } out.writeBoolean(true); view.writeExternal(out); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readInt(); boolean available=in.readBoolean(); if(available) { view=new View(); view.readExternal(in); } } public int size() { int retval=Global.INT_SIZE + Global.BYTE_SIZE + Global.BYTE_SIZE; // type + view type + presence for digest if(view != null) retval+=view.serializedSize(); return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeInt(type); // 0 == null, 1 == View, 2 == MergeView byte b=(byte)(view == null? 0 : (view instanceof MergeView? 2 : 1)); out.writeByte(b); Util.writeStreamable(view, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readInt(); byte b=in.readByte(); Class clazz=b == 2? MergeView.class : View.class; view=(View)Util.readStreamable(clazz, in); } } /** Periodically multicasts a View_SYNC message */ private class ViewSendTask implements TimeScheduler.Task { public long nextInterval() { long interval=computeSleepTime(); if(interval <= 0) return 10000; else return interval; } public void run() { sendView(); } long computeSleepTime() { int num_mbrs=Math.max(mbrs.size(), 1); return getRandom((num_mbrs * avg_send_interval * 2)); } long getRandom(long range) { return (long)((Math.random() * range) % range); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TCPGOSSIP.java0000644000175000017500000001530611366547366025654 0ustar twernertwerner// $Id: TCPGOSSIP.java,v 1.26.2.5 2009/04/27 08:35:47 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.util.Promise; import org.jgroups.stack.GossipClient; import org.jgroups.stack.IpAddress; import java.util.*; import java.net.UnknownHostException; /** * The TCPGOSSIP protocol layer retrieves the initial membership (used by the GMS when started * by sending event FIND_INITIAL_MBRS down the stack). * We do this by contacting one or more GossipRouters, which must be running at well-known * addresses:ports. The responses should allow us to determine the coordinator whom we have to * contact, e.g. in case we want to join the group. When we are a server (after having * received the BECOME_SERVER event), we'll respond to TCPGOSSIP requests with a TCPGOSSIP * response.

        The FIND_INITIAL_MBRS event will eventually be answered with a * FIND_INITIAL_MBRS_OK event up the stack. * * @author Bela Ban */ public class TCPGOSSIP extends Discovery { Vector initial_hosts=null; // (list of IpAddresses) hosts to be contacted for the initial membership GossipClient gossip_client=null; // accesses the GossipRouter(s) to find initial mbrship // we need to refresh the registration with the GossipRouter(s) periodically, // so that our entries are not purged from the cache long gossip_refresh_rate=20000; int sock_conn_timeout=1000; // max time in millis for a socket creation int sock_read_timeout=3000; // max time in millis for a socket read final static String name="TCPGOSSIP"; public String getName() { return name; } public boolean setProperties(Properties props) { String str; str=props.getProperty("gossip_refresh_rate"); // wait for at most n members if(str != null) { gossip_refresh_rate=Integer.parseInt(str); props.remove("gossip_refresh_rate"); } str=props.getProperty("sock_conn_timeout"); // wait for at most n members if(str != null) { sock_conn_timeout=Integer.parseInt(str); props.remove("sock_conn_timeout"); } str=props.getProperty("sock_read_timeout"); // wait for at most n members if(str != null) { sock_read_timeout=Integer.parseInt(str); props.remove("sock_read_timeout"); } str=props.getProperty("initial_hosts"); if(str != null) { props.remove("initial_hosts"); try { initial_hosts=createInitialHosts(str); } catch(UnknownHostException ex) { if(log.isErrorEnabled()) log.error("failed creating initial hosts", ex); return false; } } if(initial_hosts == null || initial_hosts.isEmpty()) { if(log.isErrorEnabled()) log.error("initial_hosts must contain the address of at least one GossipRouter"); return false; } if(timeout <= sock_conn_timeout) { log.warn("timeout should be greater than sock_conn_timeout"); } return super.setProperties(props); } public void start() throws Exception { super.start(); if(gossip_client == null) { gossip_client=new GossipClient(initial_hosts, gossip_refresh_rate, sock_conn_timeout); gossip_client.setSocketReadTimeout(sock_read_timeout); } } public void stop() { super.stop(); if(gossip_client != null) { gossip_client.stop(); gossip_client=null; } } public void destroy() { if(gossip_client != null) { gossip_client.destroy(); gossip_client=null; } } public void handleConnect() { if(group_addr == null || local_addr == null) { if(log.isErrorEnabled()) log.error("group_addr or local_addr is null, cannot register with GossipRouter(s)"); } else { if(log.isTraceEnabled()) log.trace("registering " + local_addr + " under " + group_addr + " with GossipRouter"); gossip_client.register(group_addr, local_addr); } } public void handleDisconnect() { if(group_addr != null && local_addr != null) { gossip_client.unregister(group_addr, local_addr); } } public void sendGetMembersRequest(Promise promise) throws Exception{ Message msg, copy; PingHeader hdr; List tmp_mbrs; Address mbr_addr; if(group_addr == null) { if(log.isErrorEnabled()) log.error("[FIND_INITIAL_MBRS]: group_addr is null, cannot get mbrship"); return; } if(log.isTraceEnabled()) log.trace("fetching members from GossipRouter(s)"); tmp_mbrs=gossip_client.getMembers(group_addr); if(tmp_mbrs == null || tmp_mbrs.isEmpty()) { if(log.isErrorEnabled()) log.error("[FIND_INITIAL_MBRS]: gossip client found no members"); return; } if(log.isTraceEnabled()) log.trace("consolidated mbrs from GossipRouter(s) are " + tmp_mbrs); // 1. 'Mcast' GET_MBRS_REQ message hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); msg=new Message(null); msg.setFlag(Message.OOB); msg.putHeader(name, hdr); for(Iterator it=tmp_mbrs.iterator(); it.hasNext();) { mbr_addr=(Address)it.next(); copy=msg.copy(); copy.setDest(mbr_addr); if(log.isTraceEnabled()) log.trace("[FIND_INITIAL_MBRS] sending PING request to " + copy.getDest()); down_prot.down(new Event(Event.MSG, copy)); } } /* -------------------------- Private methods ---------------------------- */ /** * Input is "daddy[8880],sindhu[8880],camille[5555]. Return list of IpAddresses */ private Vector

        createInitialHosts(String l) throws UnknownHostException { Vector
        tmp=new Vector
        (); String host; int port; IpAddress addr; StringTokenizer tok=new StringTokenizer(l, ","); String t; while(tok.hasMoreTokens()) { try { t=tok.nextToken(); host=t.substring(0, t.indexOf('[')); port=Integer.parseInt(t.substring(t.indexOf('[') + 1, t.indexOf(']'))); addr=new IpAddress(host, port); tmp.addElement(addr); } catch(NumberFormatException e) { if(log.isErrorEnabled()) log.error("exeption is " + e); } } return tmp; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/GMS.java.rmi0000644000175000017500000001140611366547366025512 0ustar twernertwerner /* Todo: - Discard messages from members not in our current view (?) */ package JavaGroups.JavaStack.Protocols; import java.util.*; import JavaGroups.*; import JavaGroups.JavaStack.*; public class GMS extends Protocol implements Transportable { private final int type=Conf.GMS_MSG; private Properties props=null; private GmsImpl gms_impl=null; private MethodInvoker method_invoker=new MethodInvoker(this, false); private String channel_name=null; private Queue rsp_queue=new Queue(); private String gossip_host=null; private int gossip_port=0; private GossipClient gossip=null; private boolean gossiping=false; public GMS() { method_invoker.SetMethodLookup(new MethodLookupClos()); gms_impl=new GmsImpl(this); method_invoker.AddTargetObject(gms_impl); } public ProtocolStack GetProtocolStack() {return stack;} public String GetName() {return "GMS";} public boolean UseGossip() {return gossiping;} public Vector FindInitialMembers(String channel_name) { if(gossip != null) return gossip.Get(channel_name); return new Vector(); } public void Join(Oid new_member) { gms_impl.Join(new_member); } public void Leave(Oid member) { gms_impl.Leave(member); } /*-------------------------- Interface Transportable -------------------------------*/ /** Used e.g. by MethodInvoker to return a response. Tag the message with our type. */ public void Send(Message msg) throws Exception { if(msg.GetDest() == null) msg.SetDest(new Oid(channel_name, type)); else ((Oid)msg.GetDest()).SetType(type); Down(new Event(Event.MSG, msg)); } /** Remove a message from the rsp_queue */ public Message Receive(long timeout) throws Exception { try { return (Message)rsp_queue.Remove(timeout); } catch(TimeoutException tex) { throw tex; } catch(QueueClosed closed) { return null; } catch(Exception e) { System.err.println("GMS.Receive(): " + e); } return null; } /*----------------------------------------------------------------------------------*/ /*------------------------------- Interface Protocol -------------------------------*/ /** * In case of a request, forward the message to the method invoker. In case of a response, * put it on the response queue, to be retrieved by later Receive() calls. */ public void Up(Event evt) { int msg_type; Message msg; Header hdr; if(evt.GetType() != Event.MSG) { PassUp(evt); return; } msg=(Message)evt.GetArg(); hdr=msg.RemoveHeader(); try { msg_type=((Oid)msg.GetDest()).GetType(); if(msg_type == type) { if(msg.IsResponse()) rsp_queue.Add(msg); else method_invoker.Receive(msg); return; } PassUp(evt); } catch(Exception e) { System.err.println(e); } } public void Down(Event evt) { Message msg; if(evt.GetType() != Event.MSG) { HandleDownEvent(evt); return; } msg=(Message)evt.GetArg(); msg.AddHeader(new Header(gms_impl.GetViewId())); PassDown(evt); } private void HandleDownEvent(Event evt) { switch(evt.GetType()) { case Event.JOIN: gms_impl.StartJoin(); break; case Event.LEAVE: gms_impl.StartLeave((Oid)evt.GetArg()); PassUp(new Event(Event.LEAVE_OK)); break; default: PassDown(evt); } } public void StartWork() { Address local_addr; if(stack != null) channel_name=stack.GetChannelName(); if(gms_impl == null) { System.err.println("GMS.StartWork(): gms_impl is null"); return; } if(gossiping) { if(gossip == null) { gossip=new GossipClient(stack.GetChannelName(), gossip_host, gossip_port); local_addr=stack != null ? stack.GetLocalAddress() : null; if(local_addr != null) gossip.SetAddress(new Oid(local_addr, stack.GetChannelName())); else System.err.println("GMS.StartWork(): starting gossip client, but local " + "address is null !"); gossip.Start(); } } } public void StopWork() { if(gossip != null) { gossip.Stop(); gossip=null; } } /** Setup the Protocol instance acording to the configuration string */ public void SetProperties(Properties props) { String str; this.props=props; str=props.getProperty("gossip_host"); if(str != null) gossip_host=new String(str); str=props.getProperty("gossip_port"); if(str != null) gossip_port=new Integer(str).intValue(); if(gossip_host != null && gossip_port != 0) gossiping=true; } public String toString() { return "Protocol GMS"; } /*----------------------------------------------------------------------------------*/ } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/SHARED_LOOPBACK.java0000644000175000017500000001117111366547366026515 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.stack.IpAddress; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; /** * Loopback transport shared by all channels within the same VM. Property for testing is that no messages are lost. Allows * us to test various protocols (with ProtocolTester) at maximum speed. * @author Bela Ban * @version $Id: SHARED_LOOPBACK.java,v 1.4.2.2 2009/09/16 10:38:38 belaban Exp $ */ public class SHARED_LOOPBACK extends TP { private static int next_port=10000; /** Map of cluster names and address-protocol mappings. Used for routing messages to all or single members */ private static final Map> routing_table=new ConcurrentHashMap>(); public SHARED_LOOPBACK() { } public boolean setProperties(Properties props) { super.setProperties(props); if(props.size() != 0) return false; return true; } public String toString() { return "SHARED_LOOPBACK(local address: " + local_addr + ')'; } public void sendToAllMembers(byte[] data, int offset, int length) throws Exception { Map dests=routing_table.get(channel_name); if(dests == null) { if(log.isWarnEnabled()) log.warn("no destination found for " + channel_name); return; } for(Map.Entry entry: dests.entrySet()) { Address dest=entry.getKey(); SHARED_LOOPBACK target=entry.getValue(); try { target.receive(dest, local_addr, data, offset, length); } catch(Throwable t) { log.error("failed sending message to " + dest, t); } } } public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception { Map dests=routing_table.get(channel_name); if(dests == null) { if(log.isWarnEnabled()) log.warn("no destination found for " + channel_name); return; } SHARED_LOOPBACK target=dests.get(dest); if(target == null) { if(log.isWarnEnabled()) log.warn("destination address " + dest + " not found"); return; } target.receive(dest, local_addr, data, offset, length); } public String getInfo() { return toString(); } public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) { msg.setDest(dest); } public void postUnmarshallingList(Message msg, Address dest, boolean multicast) { msg.setDest(dest); } /*------------------------------ Protocol interface ------------------------------ */ public String getName() { return "SHARED_LOOPBACK"; } // public boolean setProperties(Properties props) { // super.setProperties(props); // if(!props.isEmpty()) { // log.error("the following properties are not recognized: " + props); // return false; // } // return true; // } public void init() throws Exception { local_addr=new IpAddress("127.0.0.1", next_port++); super.init(); } public void start() throws Exception { super.start(); } public void stop() { super.stop(); } public Object down(Event evt) { Object retval=super.down(evt); switch(evt.getType()) { case Event.CONNECT: case Event.CONNECT_WITH_STATE_TRANSFER: register(channel_name, local_addr, this); break; case Event.DISCONNECT: unregister(channel_name, local_addr); break; } return retval; } private void register(String channel_name, Address local_addr, SHARED_LOOPBACK shared_loopback) { Map map=routing_table.get(channel_name); if(map == null) { map=new ConcurrentHashMap(); routing_table.put(channel_name, map); } map.put(local_addr, shared_loopback); } private void unregister(String channel_name, Address local_addr) { Map map=routing_table.get(channel_name); if(map != null) { map.remove(local_addr); if(map.isEmpty()) { routing_table.remove(channel_name); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/MERGEFAST.java0000644000175000017500000000626711366547366025624 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Vector; /** * The coordinator attaches a small header to each (or every nth) message. If another coordinator in the * same group sees the message, it will initiate the merge protocol immediately by sending a MERGE * event up the stack. * @author Bela Ban, Aug 25 2003 */ public class MERGEFAST extends Protocol { Address local_addr=null; boolean is_coord=false; static final String name="MERGEFAST"; public String getName() { return name; } public Object down(Event evt) { if(is_coord == true && evt.getType() == Event.MSG && local_addr != null) { Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest == null || dest.isMulticastAddress()) { msg.putHeader(getName(), new MergefastHeader(local_addr)); } } if(evt.getType() == Event.VIEW_CHANGE) { handleViewChange((View)evt.getArg()); } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.MSG: if(is_coord == false) // only handle message if we are coordinator break; Message msg=(Message)evt.getArg(); MergefastHeader hdr=(MergefastHeader)msg.getHeader(name); up_prot.up(evt); if(hdr != null && local_addr != null) { Address other_coord=hdr.coord; if(!local_addr.equals(other_coord)) { sendUpMerge(new Address[]{local_addr, other_coord}); } } return null; // event was already passed up case Event.VIEW_CHANGE: handleViewChange((View)evt.getArg()); break; } return up_prot.up(evt); } void handleViewChange(View v) { Vector mbrs; if(local_addr == null) return; mbrs=v.getMembers(); is_coord=mbrs != null && !mbrs.isEmpty() && local_addr.equals(mbrs.firstElement()); } /** * @todo avoid sending up too many MERGE events. */ void sendUpMerge(Address[] addresses) { Vector v=new Vector(11); for(int i=0; i < addresses.length; i++) { Address addr=addresses[i]; v.add(addr); } up_prot.up(new Event(Event.MERGE, v)); } public static class MergefastHeader extends Header { Address coord=null; public MergefastHeader() { } public MergefastHeader(Address coord) { this.coord=coord; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(coord); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { coord=(Address)in.readObject(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/TCP_NIO.java0000644000175000017500000001403711366547366025434 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.blocks.ConnectionTableNIO; import org.jgroups.blocks.BasicConnectionTable; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import org.jgroups.util.PortsManager; import java.net.InetAddress; import java.util.Properties; import java.util.Collection; /** * Transport using NIO * @author Scott Marlow * @author Alex Fu * @author Bela Ban * @version $Id: TCP_NIO.java,v 1.17.2.2 2008/05/22 13:23:07 belaban Exp $ */ public class TCP_NIO extends BasicTCP implements BasicConnectionTable.Receiver { /* * (non-Javadoc) * * @see org.jgroups.protocols.TCP#getConnectionTable(long, long) */ protected ConnectionTableNIO getConnectionTable(long ri, long cet, InetAddress b_addr, InetAddress bc_addr, int s_port, int e_port, PortsManager pm) throws Exception { ConnectionTableNIO retval=null; if (ri == 0 && cet == 0) { retval = new ConnectionTableNIO(this, b_addr, bc_addr, s_port, e_port, pm, false ); } else { if (ri == 0) { ri = 5000; if(log.isWarnEnabled()) log.warn("reaper_interval was 0, set it to " + ri); } if (cet == 0) { cet = 1000 * 60 * 5; if(log.isWarnEnabled()) log.warn("conn_expire_time was 0, set it to " + cet); } retval = new ConnectionTableNIO(this, b_addr, bc_addr, s_port, e_port, pm, ri, cet, false); } retval.setThreadFactory(getThreadFactory()); retval.setProcessorMaxThreads(getProcessorMaxThreads()); retval.setProcessorQueueSize(getProcessorQueueSize()); retval.setProcessorMinThreads(getProcessorMinThreads()); retval.setProcessorKeepAliveTime(getProcessorKeepAliveTime()); retval.setProcessorThreads(getProcessorThreads()); retval.start(); return retval; } public String printConnections() {return ct.toString();} public void send(Address dest, byte[] data, int offset, int length) throws Exception { ct.send(dest, data, offset, length); } public void start() throws Exception { ct=getConnectionTable(reaper_interval,conn_expire_time,bind_addr,external_addr,start_port,end_port,pm); ct.setUseSendQueues(use_send_queues); // ct.addConnectionListener(this); ct.setReceiveBufferSize(recv_buf_size); ct.setSendBufferSize(send_buf_size); ct.setSocketConnectionTimeout(sock_conn_timeout); ct.setPeerAddressReadTimeout(peer_addr_read_timeout); ct.setTcpNodelay(tcp_nodelay); ct.setLinger(linger); local_addr=ct.getLocalAddress(); if(additional_data != null && local_addr instanceof IpAddress) ((IpAddress)local_addr).setAdditionalData(additional_data); super.start(); } public void retainAll(Collection
        members) { ct.retainAll(members); } public void stop() { ct.stop(); super.stop(); } public String getName() { return "TCP_NIO"; } public int getReaderThreads() { return m_reader_threads; } public int getWriterThreads() { return m_writer_threads; } public int getProcessorThreads() { return m_processor_threads; } public int getProcessorMinThreads() { return m_processor_minThreads;} public int getProcessorMaxThreads() { return m_processor_maxThreads;} public int getProcessorQueueSize() { return m_processor_queueSize; } public long getProcessorKeepAliveTime() { return m_processor_keepAliveTime; } public int getOpenConnections() {return ct.getNumConnections();} /** Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { String str; str=props.getProperty("reader_threads"); if(str != null) { m_reader_threads=Integer.parseInt(str); props.remove("reader_threads"); } str=props.getProperty("writer_threads"); if(str != null) { m_writer_threads=Integer.parseInt(str); props.remove("writer_threads"); } str=props.getProperty("processor_threads"); if(str != null) { m_processor_threads=Integer.parseInt(str); props.remove("processor_threads"); } str=props.getProperty("processor_minThreads"); if(str != null) { m_processor_minThreads=Integer.parseInt(str); props.remove("processor_minThreads"); } str=props.getProperty("processor_maxThreads"); if(str != null) { m_processor_maxThreads =Integer.parseInt(str); props.remove("processor_maxThreads"); } str=props.getProperty("processor_queueSize"); if(str != null) { m_processor_queueSize=Integer.parseInt(str); props.remove("processor_queueSize"); } str=props.getProperty("processor_keepAliveTime"); if(str != null) { m_processor_keepAliveTime=Long.parseLong(str); props.remove("processor_keepAliveTime"); } return super.setProperties(props); } private int m_reader_threads = 3; private int m_writer_threads = 3; private int m_processor_threads = 5; // PooledExecutor.createThreads() private int m_processor_minThreads = 5; // PooledExecutor.setMinimumPoolSize() private int m_processor_maxThreads = 5; // PooledExecutor.setMaxThreads() private int m_processor_queueSize=100; // Number of queued requests that can be pending waiting // for a background thread to run the request. private long m_processor_keepAliveTime = Long.MAX_VALUE; // PooledExecutor.setKeepAliveTime( milliseconds); // negative value used to mean (before 2.5 release) to wait forever, // instead set to Long.MAX_VALUE to keep alive forever private ConnectionTableNIO ct; }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/FRAG2.java0000644000175000017500000005453511366547366025111 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.stack.Protocol; import org.jgroups.util.Range; import org.jgroups.util.Util; import java.util.*; import java.util.concurrent.atomic.AtomicLong; /** * Fragmentation layer. Fragments messages larger than frag_size into smaller packets. * Reassembles fragmented packets into bigger ones. The fragmentation number is prepended * to the messages as a header (and removed at the receiving side).

        * Each fragment is identified by (a) the sender (part of the message to which the header is appended), * (b) the fragmentation ID (which is unique per FRAG2 layer (monotonically increasing) and (c) the * fragement ID which ranges from 0 to number_of_fragments-1.

        * Requirement: lossless delivery (e.g. NAK, ACK). No requirement on ordering. Works for both unicast and * multicast messages.
        * Compared to FRAG, this protocol does not need to serialize the message in order to break it into * smaller fragments: it looks only at the message's buffer, which is a byte[] array anyway. We assume that the * size addition for headers and src and dest address is minimal when the transport finally has to serialize the * message, so we add a constant (200 bytes). * @author Bela Ban * @version $Id: FRAG2.java,v 1.36.2.2 2009/04/23 08:17:07 belaban Exp $ */ public class FRAG2 extends Protocol { /** The max number of bytes in a message. If a message's buffer is bigger, it will be fragmented */ int frag_size=1500; /*the fragmentation list contains a fragmentation table per sender *this way it becomes easier to clean up if a sender (member) leaves or crashes */ private final FragmentationList fragment_list=new FragmentationList(); private int curr_id=1; private final Vector members=new Vector(11); private static final String name="FRAG2"; AtomicLong num_sent_msgs=new AtomicLong(0); AtomicLong num_received_msgs=new AtomicLong(0); AtomicLong num_sent_frags=new AtomicLong(0); AtomicLong num_received_frags=new AtomicLong(0); public final String getName() { return name; } public int getFragSize() {return frag_size;} public void setFragSize(int s) {frag_size=s;} /** @deprecated overhead was removed in 2.6.10 */ public int getOverhead() {return 0;} /** @deprecated overhead was removed in 2.6.10 */ public void setOverhead(int o) {} public long getNumberOfSentMessages() {return num_sent_msgs.get();} public long getNumberOfSentFragments() {return num_sent_frags.get();} public long getNumberOfReceivedMessages() {return num_received_msgs.get();} public long getNumberOfReceivedFragments() {return num_received_frags.get();} synchronized int getNextId() { return curr_id++; } /** Setup the Protocol instance acording to the configuration string */ public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("frag_size"); if(str != null) { frag_size=Integer.parseInt(str); props.remove("frag_size"); } str=props.getProperty("overhead"); if(str != null) { props.remove("overhead"); log.warn("overhead is ignored (was removed in 2.6.10)"); } int old_frag_size=frag_size; if(frag_size <=0) { log.error("frag_size=" + old_frag_size + ", new frag_size=" + frag_size + ": new frag_size is invalid"); return false; } if(log.isDebugEnabled()) log.debug("frag_size=" + old_frag_size + ", new frag_size=" + frag_size); if(!props.isEmpty()) { log.error("FRAG2.setProperties(): the following properties are not recognized: " + props); return false; } return true; } public void init() throws Exception { super.init(); Map info=new HashMap(1); info.put("frag_size", frag_size); up_prot.up(new Event(Event.INFO, info)); down_prot.down(new Event(Event.INFO, info)); } public void resetStats() { super.resetStats(); num_sent_msgs.set(0); num_sent_frags.set(0); num_received_frags.set(0); num_received_msgs.set(0); } /** * Fragment a packet if larger than frag_size (add a header). Otherwise just pass down. Only * add a header if framentation is needed ! */ public Object down(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); long size=msg.getLength(); num_sent_msgs.incrementAndGet(); if(size > frag_size) { if(log.isTraceEnabled()) { log.trace(new StringBuilder("message's buffer size is ").append(size) .append(", will fragment ").append("(frag_size=").append(frag_size).append(')')); } fragment(msg); // Fragment and pass down return null; } break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; case Event.CONFIG: Object ret=down_prot.down(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); return ret; } return down_prot.down(evt); // Pass on to the layer below us } /** * If event is a message, if it is fragmented, re-assemble fragments into big message and pass up * the stack. */ public Object up(Event evt) { switch(evt.getType()) { case Event.MSG: Message msg=(Message)evt.getArg(); FragHeader hdr=(FragHeader)msg.getHeader(name); if(hdr != null) { // needs to be defragmented unfragment(msg, hdr); // Unfragment and possibly pass up return null; } else { num_received_msgs.incrementAndGet(); } break; case Event.VIEW_CHANGE: handleView((View)evt.getArg()); break; case Event.CONFIG: Object ret=up_prot.up(evt); if(log.isDebugEnabled()) log.debug("received CONFIG event: " + evt.getArg()); handleConfigEvent((Map)evt.getArg()); return ret; } return up_prot.up(evt); // Pass up to the layer above us by default } private void handleView(View view) { //don't do anything if this dude is sending out the view change //we are receiving a view change, //in here we check for the Vector new_mbrs=view.getMembers(), left_mbrs; Address mbr; left_mbrs=Util.determineLeftMembers(members, new_mbrs); members.clear(); members.addAll(new_mbrs); for(int i=0; i < left_mbrs.size(); i++) { mbr=(Address)left_mbrs.elementAt(i); //the new view doesn't contain the sender, he must have left, //hence we will clear all his fragmentation tables fragment_list.remove(mbr); if(log.isTraceEnabled()) log.trace("[VIEW_CHANGE] removed " + mbr + " from fragmentation table"); } } /** Send all fragments as separate messages (with same ID !). Example:

             Given the generated ID is 2344, number of fragments=3, message {dst,src,buf}
             would be fragmented into:
        
             [2344,3,0]{dst,src,buf1},
             [2344,3,1]{dst,src,buf2} and
             [2344,3,2]{dst,src,buf3}
             
        */ void fragment(Message msg) { byte[] buffer; List fragments; Event evt; FragHeader hdr; Message frag_msg; Address dest=msg.getDest(); long id=getNextId(); // used as seqnos int num_frags; Range r; try { buffer=msg.getBuffer(); fragments=Util.computeFragOffsets(buffer, frag_size); num_frags=fragments.size(); num_sent_frags.addAndGet(num_frags); if(log.isTraceEnabled()) { StringBuilder sb=new StringBuilder("fragmenting packet to "); sb.append((dest != null ? dest.toString() : "")).append(" (size=").append(buffer.length); sb.append(") into ").append(num_frags).append(" fragment(s) [frag_size=").append(frag_size).append(']'); log.trace(sb.toString()); } for(int i=0; i < fragments.size(); i++) { r=(Range)fragments.get(i); // Copy the original msg (needed because we need to copy the headers too) frag_msg=msg.copy(false); // don't copy the buffer, only src, dest and headers frag_msg.setBuffer(buffer, (int)r.low, (int)r.high); hdr=new FragHeader(id, i, num_frags); frag_msg.putHeader(name, hdr); evt=new Event(Event.MSG, frag_msg); down_prot.down(evt); } } catch(Exception e) { if(log.isErrorEnabled()) log.error("fragmentation failure", e); } } /** 1. Get all the fragment buffers 2. When all are received -> Assemble them into one big buffer 3. Read headers and byte buffer from big buffer 4. Set headers and buffer in msg 5. Pass msg up the stack */ private void unfragment(Message msg, FragHeader hdr) { FragmentationTable frag_table; Address sender=msg.getSrc(); Message assembled_msg; frag_table=fragment_list.get(sender); if(frag_table == null) { frag_table=new FragmentationTable(sender); try { fragment_list.add(sender, frag_table); } catch(IllegalArgumentException x) { // the entry has already been added, probably in parallel from another thread frag_table=fragment_list.get(sender); } } num_received_frags.incrementAndGet(); assembled_msg=frag_table.add(hdr.id, hdr.frag_id, hdr.num_frags, msg); if(assembled_msg != null) { try { if(log.isTraceEnabled()) log.trace("assembled_msg is " + assembled_msg); assembled_msg.setSrc(sender); // needed ? YES, because fragments have a null src !! num_received_msgs.incrementAndGet(); up_prot.up(new Event(Event.MSG, assembled_msg)); } catch(Exception e) { if(log.isErrorEnabled()) log.error("unfragmentation failed", e); } } } void handleConfigEvent(Map map) { if(map == null) return; if(map.containsKey("frag_size")) { frag_size=((Integer)map.get("frag_size")).intValue(); if(log.isDebugEnabled()) log.debug("setting frag_size=" + frag_size); } } /** * A fragmentation list keeps a list of fragmentation tables * sorted by an Address ( the sender ). * This way, if the sender disappears or leaves the group half way * sending the content, we can simply remove this members fragmentation * table and clean up the memory of the receiver. * We do not have to do the same for the sender, since the sender doesn't keep a fragmentation table */ static class FragmentationList { /* * HashMap, initialize the hashtable to hold all the fragmentation * tables (11 is the best growth capacity to start with) */ private final HashMap frag_tables=new HashMap(11); /** * Adds a fragmentation table for this particular sender * If this sender already has a fragmentation table, an IllegalArgumentException * will be thrown. * @param sender - the address of the sender, cannot be null * @param table - the fragmentation table of this sender, cannot be null * @exception IllegalArgumentException if an entry for this sender already exist */ public void add(Address sender, FragmentationTable table) throws IllegalArgumentException { FragmentationTable healthCheck; synchronized(frag_tables) { healthCheck=(FragmentationTable)frag_tables.get(sender); if(healthCheck == null) { frag_tables.put(sender, table); } else { throw new IllegalArgumentException("Sender <" + sender + "> already exists in the fragementation list."); } } } /** * returns a fragmentation table for this sender * returns null if the sender doesn't have a fragmentation table * @return the fragmentation table for this sender, or null if no table exist */ public FragmentationTable get(Address sender) { synchronized(frag_tables) { return (FragmentationTable)frag_tables.get(sender); } } /** * returns true if this sender already holds a * fragmentation for this sender, false otherwise * @param sender - the sender, cannot be null * @return true if this sender already has a fragmentation table */ public boolean containsSender(Address sender) { synchronized(frag_tables) { return frag_tables.containsKey(sender); } } /** * removes the fragmentation table from the list. * after this operation, the fragementation list will no longer * hold a reference to this sender's fragmentation table * @param sender - the sender who's fragmentation table you wish to remove, cannot be null * @return true if the table was removed, false if the sender doesn't have an entry */ public boolean remove(Address sender) { synchronized(frag_tables) { boolean result=containsSender(sender); frag_tables.remove(sender); return result; } } /** * returns a list of all the senders that have fragmentation tables * opened. * @return an array of all the senders in the fragmentation list */ public Address[] getSenders() { Address[] result; int index=0; synchronized(frag_tables) { result=new Address[frag_tables.size()]; for(Iterator it=frag_tables.keySet().iterator(); it.hasNext();) { result[index++]=(Address)it.next(); } } return result; } public String toString() { Map.Entry entry; StringBuilder buf=new StringBuilder("Fragmentation list contains "); synchronized(frag_tables) { buf.append(frag_tables.size()).append(" tables\n"); for(Iterator it=frag_tables.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); buf.append(entry.getKey()).append(": " ).append(entry.getValue()).append("\n"); } } return buf.toString(); } } /** * Keeps track of the fragments that are received. * Reassembles fragements into entire messages when all fragments have been received. * The fragmentation holds a an array of byte arrays for a unique sender * The first dimension of the array is the order of the fragmentation, in case the arrive out of order */ static class FragmentationTable { private final Address sender; /* the hashtable that holds the fragmentation entries for this sender*/ private final Hashtable h=new Hashtable(11); // keys: frag_ids, vals: Entrys FragmentationTable(Address sender) { this.sender=sender; } /** * inner class represents an entry for a message * each entry holds an array of byte arrays sorted * once all the byte buffer entries have been filled * the fragmentation is considered complete. */ static class Entry { //the total number of fragment in this message int tot_frags=0; // each fragment is a byte buffer Message fragments[]=null; //the number of fragments we have received int number_of_frags_recvd=0; // the message ID long msg_id=-1; /** * Creates a new entry * @param tot_frags the number of fragments to expect for this message */ Entry(long msg_id, int tot_frags) { this.msg_id=msg_id; this.tot_frags=tot_frags; fragments=new Message[tot_frags]; for(int i=0; i < tot_frags; i++) fragments[i]=null; } /** * adds on fragmentation buffer to the message * @param frag_id the number of the fragment being added 0..(tot_num_of_frags - 1) * @param frag the byte buffer containing the data for this fragmentation, should not be null */ public void set(int frag_id, Message frag) { // don't count an already received fragment (should not happen though because the // reliable transmission protocol(s) below should weed out duplicates if(fragments[frag_id] == null) { fragments[frag_id]=frag; number_of_frags_recvd++; } } /** returns true if this fragmentation is complete * ie, all fragmentations have been received for this buffer * */ public boolean isComplete() { /*first make the simple check*/ if(number_of_frags_recvd < tot_frags) { return false; } /*then double check just in case*/ for(int i=0; i < fragments.length; i++) { if(fragments[i] == null) return false; } /*all fragmentations have been received*/ return true; } /** * Assembles all the fragments into one buffer. Takes all Messages, and combines their buffers into one * buffer. * This method does not check if the fragmentation is complete (use {@link #isComplete()} to verify * before calling this method) * @return the complete message in one buffer * */ public Message assembleMessage() { Message retval; byte[] combined_buffer, tmp; int combined_length=0, length, offset; Message fragment; int index=0; for(int i=0; i < fragments.length; i++) { fragment=fragments[i]; combined_length+=fragment.getLength(); } combined_buffer=new byte[combined_length]; for(int i=0; i < fragments.length; i++) { fragment=fragments[i]; tmp=fragment.getRawBuffer(); length=fragment.getLength(); offset=fragment.getOffset(); System.arraycopy(tmp, offset, combined_buffer, index, length); index+=length; } retval=fragments[0].copy(false); retval.setBuffer(combined_buffer); return retval; } /** * debug only */ public String toString() { StringBuilder ret=new StringBuilder(); ret.append("[tot_frags=").append(tot_frags).append(", number_of_frags_recvd=").append(number_of_frags_recvd).append(']'); return ret.toString(); } public int hashCode() { return super.hashCode(); } } /** * Creates a new entry if not yet present. Adds the fragment. * If all fragements for a given message have been received, * an entire message is reassembled and returned. * Otherwise null is returned. * @param id - the message ID, unique for a sender * @param frag_id the index of this fragmentation (0..tot_frags-1) * @param tot_frags the total number of fragmentations expected * @param fragment - the byte buffer for this fragment */ public synchronized Message add(long id, int frag_id, int tot_frags, Message fragment) { Message retval=null; Entry e=(Entry)h.get(new Long(id)); if(e == null) { // Create new entry if not yet present e=new Entry(id, tot_frags); h.put(new Long(id), e); } e.set(frag_id, fragment); if(e.isComplete()) { retval=e.assembleMessage(); h.remove(new Long(id)); } return retval; } public void reset() { } public String toString() { StringBuilder buf=new StringBuilder("Fragmentation Table Sender:").append(sender).append("\n\t"); java.util.Enumeration e=this.h.elements(); while(e.hasMoreElements()) { Entry entry=(Entry)e.nextElement(); int count=0; for(int i=0; i < entry.fragments.length; i++) { if(entry.fragments[i] != null) { count++; } } buf.append("Message ID:").append(entry.msg_id).append("\n\t"); buf.append("Total Frags:").append(entry.tot_frags).append("\n\t"); buf.append("Frags Received:").append(count).append("\n\n"); } return buf.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/DISCARD.java0000644000175000017500000003130511366547366025347 0ustar twernertwerner// $Id: DISCARD.java,v 1.17.2.5 2009/03/31 12:28:52 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.Event; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.util.*; /** * Discards up or down messages based on a percentage; e.g., setting property 'up' to 0.1 causes 10% * of all up messages to be discarded. Setting 'down' or 'up' to 0 causes no loss, whereas 1 discards * all messages (not very useful). */ public class DISCARD extends Protocol { double up=0.0; // probability of dropping up msgs double down=0.0; // probability of dropping down msgs boolean excludeItself=true; // if true don't discard messages sent/received in this stack Address localAddress; int num_down=0, num_up=0; final Set
        ignoredMembers = new HashSet
        (); boolean discard_all=false; // number of subsequent unicasts to drop in the down direction int drop_down_unicasts=0; // number of subsequent multicasts to drop in the down direction int drop_down_multicasts=0; private DiscardDialog discard_dialog=null; protected boolean use_gui=false; /** * All protocol names have to be unique ! */ public String getName() { return "DISCARD"; } public boolean isExcludeItself() { return excludeItself; } public void setLocalAddress(Address localAddress){ this.localAddress =localAddress; if(discard_dialog != null) discard_dialog.setTitle(localAddress != null? localAddress.toString() : "n/a"); } public void setExcludeItself(boolean excludeItself) { this.excludeItself=excludeItself; } public double getUpDiscardRate() { return up; } public void setUpDiscardRate(double up) { this.up=up; } public double getDownDiscardRate() { return down; } public void setDownDiscardRate(double down) { this.down=down; } public int getDropDownUnicasts() { return drop_down_unicasts; } /** * Drop the next N unicasts down the stack * @param drop_down_unicasts */ public void setDropDownUnicasts(int drop_down_unicasts) { this.drop_down_unicasts=drop_down_unicasts; } public int getDropDownMulticasts() { return drop_down_multicasts; } public void setDropDownMulticasts(int drop_down_multicasts) { this.drop_down_multicasts=drop_down_multicasts; } /** Messages from this sender will get dropped */ public void addIgnoreMember(Address sender) {ignoredMembers.add(sender);} public void removeIgnoredMember(Address member) {ignoredMembers.remove(member);} public void resetIgnoredMembers() {ignoredMembers.clear();} public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("up"); if(str != null) { up=Double.parseDouble(str); props.remove("up"); } str=props.getProperty("down"); if(str != null) { down=Double.parseDouble(str); props.remove("down"); } str=props.getProperty("excludeitself"); if(str != null) { excludeItself=Boolean.valueOf(str).booleanValue(); props.remove("excludeitself"); } str=props.getProperty("use_gui"); if(str != null) { use_gui=Boolean.valueOf(str).booleanValue(); props.remove("use_gui"); } if(!props.isEmpty()) { log.error("DISCARD.setProperties(): these properties are not recognized: " + props); return false; } return true; } public void start() throws Exception { super.start(); if(use_gui) { discard_dialog=new DiscardDialog(); discard_dialog.init(); } } public void stop() { super.stop(); if(discard_dialog != null) discard_dialog.dispose(); } public Object up(Event evt) { Message msg; double r; if(evt.getType() == Event.SET_LOCAL_ADDRESS) { localAddress=(Address)evt.getArg(); if(discard_dialog != null) discard_dialog.setTitle(localAddress != null? localAddress.toString() : "n/a"); } if(evt.getType() == Event.MSG) { msg=(Message)evt.getArg(); Address sender=msg.getSrc(); if(discard_all && !sender.equals(localAddress)) { return null; } DiscardHeader dh = (DiscardHeader) msg.getHeader(getName()); if (dh != null) { ignoredMembers.clear(); ignoredMembers.addAll(dh.dropMessages); if (log.isTraceEnabled()) log.trace("will potentially drop messages from " + ignoredMembers); } else { boolean dropMessage=ignoredMembers.contains(sender); if (dropMessage) { if (log.isTraceEnabled()) log.trace("dropping message from " + sender); num_up++; return null; } if (up > 0) { r = Math.random(); if (r < up) { if (excludeItself && sender.equals(localAddress)) { if (log.isTraceEnabled()) log.trace("excluding itself"); } else { if (log.isTraceEnabled()) log.trace("dropping message from " + sender); num_up++; return null; } } } } } return up_prot.up(evt); } public Object down(Event evt) { Message msg; double r; switch(evt.getType()) { case Event.MSG: msg=(Message)evt.getArg(); Address dest=msg.getDest(); boolean multicast=dest == null || dest.isMulticastAddress(); if(msg.getSrc() == null) msg.setSrc(localAddress); if(discard_all) { if(dest == null || dest.isMulticastAddress() || dest.equals(localAddress)) { //System.out.println("[" + localAddress + "] down(): looping back " + msg + ", hdrs:\n" + msg.getHeaders()); loopback(msg); } return null; } if(!multicast && drop_down_unicasts > 0) { drop_down_unicasts=Math.max(0, drop_down_unicasts -1); return null; } if(multicast && drop_down_multicasts > 0) { drop_down_multicasts=Math.max(0, drop_down_multicasts -1); return null; } if(down > 0) { r=Math.random(); if(r < down) { if(excludeItself && msg.getSrc().equals(localAddress)) { if(log.isTraceEnabled()) log.trace("excluding itself"); } else { if(log.isTraceEnabled()) log.trace("dropping message"); num_down++; return null; } } } break; case Event.VIEW_CHANGE: View view=(View)evt.getArg(); Vector
        mbrs=view.getMembers(); ignoredMembers.retainAll(mbrs); // remove all non members if(discard_dialog != null) discard_dialog.handleView(mbrs); break; } return down_prot.down(evt); } private void loopback(Message msg) { final Message rsp=msg.copy(true); if(rsp.getSrc() == null) rsp.setSrc(localAddress); // pretty inefficient: creates one thread per message, okay for testing only Thread thread=new Thread(new Runnable() { public void run() { up_prot.up(new Event(Event.MSG, rsp)); } }); thread.start(); } public void resetStats() { super.resetStats(); num_down=num_up=0; } public Map dumpStats() { Map m=new HashMap(2); m.put("num_dropped_down", new Integer(num_down)); m.put("num_dropped_up", new Integer(num_up)); return m; } public static class DiscardHeader extends Header implements Streamable { private final Set
        dropMessages; private static final long serialVersionUID=-2149735838082082084L; public DiscardHeader() { this.dropMessages= new HashSet
        (); } public DiscardHeader(Set
        ignoredAddresses) { super(); this.dropMessages= ignoredAddresses; } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { int size = in.readShort(); if (size > 0) { dropMessages.clear(); for (int i = 0; i < size; i++) { dropMessages.add(Util.readAddress(in)); } } } public void writeTo(DataOutputStream out) throws IOException { if (dropMessages != null && !dropMessages.isEmpty()) { out.writeShort(dropMessages.size()); for (Address addr: dropMessages) { Util.writeAddress(addr, out); } } else { out.writeShort(0); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { Set
        tmp = (Set
        ) in.readObject(); dropMessages.clear(); dropMessages.addAll(tmp); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(dropMessages); } } private class DiscardDialog extends JFrame implements ActionListener { private JButton start_discarding_button=new JButton("start discarding"); private JButton stop_discarding_button=new JButton("stop discarding"); JPanel checkboxes=new JPanel(); private DiscardDialog() { } void init() { getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); checkboxes.setLayout(new BoxLayout(checkboxes, BoxLayout.Y_AXIS)); getContentPane().add(start_discarding_button); getContentPane().add(stop_discarding_button); start_discarding_button.addActionListener(this); stop_discarding_button.addActionListener(this); getContentPane().add(checkboxes); pack(); setVisible(true); setTitle(localAddress != null? localAddress.toString() : "n/a"); } public void actionPerformed(ActionEvent e) { String command=e.getActionCommand(); if(command.startsWith("start")) { discard_all=true; } else if(command.startsWith("stop")) { discard_all=false; Component[] comps=checkboxes.getComponents(); for(Component c: comps) { if(c instanceof JCheckBox) { ((JCheckBox)c).setSelected(false); } } } } void handleView(Collection
        mbrs) { checkboxes.removeAll(); for(final Address addr: mbrs) { final MyCheckBox box=new MyCheckBox("discard traffic from " + addr, addr); box.setAlignmentX(LEFT_ALIGNMENT); box.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if(box.isSelected()) { ignoredMembers.add(addr); } else { ignoredMembers.remove(addr); } } }); checkboxes.add(box); } for(Component comp: checkboxes.getComponents()) { MyCheckBox box=(MyCheckBox)comp; if(ignoredMembers.contains(box.mbr)) box.setSelected(true); } pack(); } } private static class MyCheckBox extends JCheckBox { final Address mbr; public MyCheckBox(String name, Address member) { super(name); this.mbr=member; } public float getAlignmentX() { return LEFT_ALIGNMENT; } public String toString() { return super.toString() + " [mbr=" + mbr + "]"; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/STATS.java0000644000175000017500000001345211366547366025177 0ustar twernertwernerpackage org.jgroups.protocols; import org.jgroups.stack.Protocol; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.Address; import org.jgroups.View; import java.util.*; /** * Provides various stats * @author Bela Ban * @version $Id: STATS.java,v 1.8 2007/01/12 14:20:33 belaban Exp $ */ public class STATS extends Protocol { long sent_msgs, sent_bytes, sent_ucasts, sent_mcasts, received_ucasts, received_mcasts; long received_msgs, received_bytes, sent_ucast_bytes, sent_mcast_bytes, received_ucast_bytes, received_mcast_bytes; /** HashMap, maintains stats per target destination */ HashMap sent=new HashMap(); /** HashMap, maintains stats per receiver */ HashMap received=new HashMap(); static final short UP=1; static final short DOWN=2; public String getName() { return "STATS"; } public boolean setProperties(Properties props) { super.setProperties(props); if(!props.isEmpty()) { log.error("the following properties are not recognized: " + props); return false; } return true; } public void resetStats() { sent_msgs=sent_bytes=sent_ucasts=sent_mcasts=received_ucasts=received_mcasts=0; received_msgs=received_bytes=sent_ucast_bytes=sent_mcast_bytes=received_ucast_bytes=received_mcast_bytes=0; sent.clear(); received.clear(); } public long getSentMessages() {return sent_msgs;} public long getSentBytes() {return sent_bytes;} public long getSentUnicastMessages() {return sent_ucasts;} public long getSentUnicastBytes() {return sent_ucast_bytes;} public long getSentMcastMessages() {return sent_mcasts;} public long getSentMcastBytes() {return sent_mcast_bytes;} public long getReceivedMessages() {return received_msgs;} public long getReceivedBytes() {return received_bytes;} public long getReceivedUnicastMessages() {return received_ucasts;} public long getReceivedUnicastBytes() {return received_ucast_bytes;} public long getReceivedMcastMessages() {return received_mcasts;} public long getReceivedMcastBytes() {return received_mcast_bytes;} public Object up(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); updateStats(msg, UP); } else if(evt.getType() == Event.VIEW_CHANGE) { handleViewChange((View)evt.getArg()); } return up_prot.up(evt); } public Object down(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); updateStats(msg, DOWN); } else if(evt.getType() == Event.VIEW_CHANGE) { handleViewChange((View)evt.getArg()); } return down_prot.down(evt); } public String printStats() { Map.Entry entry; Object key, val; StringBuilder sb=new StringBuilder(); sb.append("sent:\n"); for(Iterator it=sent.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); if(key == null) key=""; val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } sb.append("\nreceived:\n"); for(Iterator it=received.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } return sb.toString(); } private void handleViewChange(View view) { Vector members=view.getMembers(); Set tmp=new LinkedHashSet(members); tmp.add(null); // for null destination (= mcast) sent.keySet().retainAll(tmp); received.keySet().retainAll(tmp); } private void updateStats(Message msg, short direction) { int length; HashMap map; boolean mcast; Address dest, src; if(msg == null) return; length=msg.getLength(); dest=msg.getDest(); src=msg.getSrc(); mcast=dest == null || dest.isMulticastAddress(); if(direction == UP) { // received received_msgs++; received_bytes+=length; if(mcast) { received_mcasts++; received_mcast_bytes+=length; } else { received_ucasts++; received_ucast_bytes+=length; } } else { // sent sent_msgs++; sent_bytes+=length; if(mcast) { sent_mcasts++; sent_mcast_bytes+=length; } else { sent_ucasts++; sent_ucast_bytes+=length; } } Address key=direction == UP? src : dest; map=direction == UP? received : sent; Entry entry=(Entry)map.get(key); if(entry == null) { entry=new Entry(); map.put(key, entry); } entry.msgs++; entry.bytes+=length; if(mcast) { entry.mcasts++; entry.mcast_bytes+=length; } else { entry.ucasts++; entry.ucast_bytes+=length; } } static class Entry { long msgs, bytes, ucasts, mcasts, ucast_bytes, mcast_bytes; public String toString() { StringBuilder sb=new StringBuilder(); sb.append(msgs).append(" (").append(bytes).append(" bytes)"); sb.append(": ").append(ucasts).append(" ucasts (").append(ucast_bytes).append(" bytes), "); sb.append(mcasts).append(" mcasts (").append(mcast_bytes).append(" bytes)"); return sb.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/PingHeader.java0000644000175000017500000000360511366547366026306 0ustar twernertwerner// $Id: PingHeader.java,v 1.11 2007/05/01 10:55:10 belaban Exp $ package org.jgroups.protocols; import org.jgroups.Header; import org.jgroups.Global; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; public class PingHeader extends Header implements Streamable { public static final byte GET_MBRS_REQ=1; // arg = null public static final byte GET_MBRS_RSP=2; // arg = PingRsp(local_addr, coord_addr) public byte type=0; public PingRsp arg=null; public PingHeader() { } // for externalization public PingHeader(byte type, PingRsp arg) { this.type=type; this.arg=arg; } public int size() { int retval=Global.BYTE_SIZE *2; // type and presence if(arg != null) { retval+=arg.size(); } return retval; } public String toString() { return "[PING: type=" + type2Str(type) + ", arg=" + arg + ']'; } String type2Str(byte t) { switch(t) { case GET_MBRS_REQ: return "GET_MBRS_REQ"; case GET_MBRS_RSP: return "GET_MBRS_RSP"; default: return ""; } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeObject(arg); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); arg=(PingRsp)in.readObject(); } public void writeTo(DataOutputStream outstream) throws IOException { outstream.writeByte(type); Util.writeStreamable(arg, outstream); } public void readFrom(DataInputStream instream) throws IOException, IllegalAccessException, InstantiationException { type=instream.readByte(); arg=(PingRsp)Util.readStreamable(PingRsp.class, instream); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/protocols/HTOTAL.java0000644000175000017500000001413611366547366025274 0ustar twernertwerner// $Id: HTOTAL.java,v 1.8 2007/04/27 07:59:19 belaban Exp $ package org.jgroups.protocols; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.Properties; import java.util.Vector; /** * Implementation of UTO-TCP as designed by EPFL. Implements chaining algorithm: each sender sends the message * to a coordinator who then forwards it to its neighbor on the right, who then forwards it to its neighbor to the right * etc.

        * This protocol has not yet been completed and is experimental at best ! * @author Bela Ban * @version $Id: HTOTAL.java,v 1.8 2007/04/27 07:59:19 belaban Exp $ */ public class HTOTAL extends Protocol { Address coord=null; Address neighbor=null; // to whom do we forward the message (member to the right, or null if we're at the tail) Address local_addr=null; Vector mbrs=new Vector(); boolean is_coord=false; private boolean use_multipoint_forwarding=false; public HTOTAL() { } public final String getName() { return "HTOTAL"; } public boolean setProperties(Properties props) { String str; super.setProperties(props); str=props.getProperty("use_multipoint_forwarding"); if(str != null) { use_multipoint_forwarding=Boolean.valueOf(str).booleanValue(); props.remove("use_multipoint_forwarding"); } if(!props.isEmpty()) { log.error("TCP.setProperties(): the following properties are not recognized: " + props); return false; } return true; } public Object down(Event evt) { switch(evt.getType()) { case Event.VIEW_CHANGE: determineCoordinatorAndNextMember((View)evt.getArg()); break; case Event.MSG: Message msg=(Message)evt.getArg(); Address dest=msg.getDest(); if(dest == null || dest.isMulticastAddress()) { // only process multipoint messages if(coord == null) log.error("coordinator is null, cannot send message to coordinator"); else { msg.setSrc(local_addr); forwardTo(coord, msg); } return null; // handled here, don't pass down by default } break; } return down_prot.down(evt); } public Object up(Event evt) { switch(evt.getType()) { case Event.SET_LOCAL_ADDRESS: local_addr=(Address)evt.getArg(); break; case Event.VIEW_CHANGE: determineCoordinatorAndNextMember((View)evt.getArg()); break; case Event.MSG: Message msg=(Message)evt.getArg(); HTotalHeader hdr=(HTotalHeader)msg.getHeader(getName()); if(hdr == null) break; // probably a unicast message, just pass it up Message copy=msg.copy(false); // do not copy the buffer if(use_multipoint_forwarding) { copy.setDest(null); down_prot.down(new Event(Event.MSG, copy)); } else { if(neighbor != null) { forwardTo(neighbor, copy); } } msg.setDest(hdr.dest); // set destination to be the original destination msg.setSrc(hdr.src); // set sender to be the original sender (important for retransmission requests) return up_prot.up(evt); // <-- we modify msg directly inside evt } return up_prot.up(evt); } private void forwardTo(Address destination, Message msg) { HTotalHeader hdr=(HTotalHeader)msg.getHeader(getName()); if(hdr == null) { hdr=new HTotalHeader(msg.getDest(), msg.getSrc()); msg.putHeader(getName(), hdr); } msg.setDest(destination); if(log.isTraceEnabled()) log.trace("forwarding message to " + destination + ", hdr=" + hdr); down_prot.down(new Event(Event.MSG, msg)); } private void determineCoordinatorAndNextMember(View v) { Object tmp; Address retval=null; mbrs.clear(); mbrs.addAll(v.getMembers()); coord=(Address)(mbrs != null && !mbrs.isEmpty()? mbrs.firstElement() : null); is_coord=coord != null && local_addr != null && coord.equals(local_addr); if(mbrs == null || mbrs.size() < 2 || local_addr == null) neighbor=null; else { for(int i=0; i < mbrs.size(); i++) { tmp=mbrs.elementAt(i); if(local_addr.equals(tmp)) { if(i + 1 >= mbrs.size()) retval=null; // we don't wrap, last member is null else retval=(Address)mbrs.elementAt(i + 1); break; } } } neighbor=retval; if(log.isTraceEnabled()) log.trace("coord=" + coord + ", neighbor=" + neighbor); } public static class HTotalHeader extends Header implements Streamable { Address dest, src; public HTotalHeader() { } public HTotalHeader(Address dest, Address src) { this.dest=dest; this.src=src; } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(dest); out.writeObject(src); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { dest=(Address)in.readObject(); src=(Address)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { Util.writeAddress(dest, out); Util.writeAddress(src, out); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { dest=Util.readAddress(in); src=Util.readAddress(in); } public String toString() { return "dest=" + dest + ", src=" + src; } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ExitEvent.java0000644000175000017500000000033611366547366024165 0ustar twernertwerner// $Id: ExitEvent.java,v 1.2 2005/07/17 11:38:05 chrislott Exp $ package org.jgroups; /** * Trivial object that represents an exit event. */ public class ExitEvent { public String toString() {return "ExitEvent";} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ChannelException.java0000644000175000017500000000104011366547366025472 0ustar twernertwerner// $Id: ChannelException.java,v 1.7 2006/11/13 17:42:11 bstansberry Exp $ package org.jgroups; /** * This class represents the super class for all exception types thrown by * JGroups. */ public class ChannelException extends Exception { private static final long serialVersionUID = 6041194633384856098L; public ChannelException() { super(); } public ChannelException(String reason) { super(reason); } public ChannelException(String reason, Throwable cause) { super(reason, cause); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Global.java0000644000175000017500000000764411366547366023463 0ustar twernertwernerpackage org.jgroups; /** * Globals used by JGroups packages. * * @author Bela Ban Mar 29, 2004 * @version $Id: Global.java,v 1.23.2.4 2009/09/14 16:17:00 belaban Exp $ */ public class Global { /** Allows for conditional compilation; e.g., if(log.isTraceEnabled()) if(log.isInfoEnabled()) log.info(...) would be removed from the code (if recompiled) when this flag is set to false. Therefore, code that should be removed from the final product should use if(log.isTraceEnabled()) rather than . */ public static final boolean debug=false; public static final String THREAD_PREFIX=" (channel="; public static final int BYTE_SIZE = Byte.SIZE / 8; // 1 public static final int SHORT_SIZE = Short.SIZE / 8; // 2 public static final int INT_SIZE = Integer.SIZE / 8; // 4 public static final int LONG_SIZE = Long.SIZE / 8; // 8 public static final Object NULL=new Object(); public static final String BIND_ADDR="jgroups.bind_addr"; public static final String BIND_ADDR_OLD="bind.address"; public static final String BIND_INTERFACE="jgroups.bind_interface"; public static final String IGNORE_BIND_ADDRESS_PROPERTY="jgroups.ignore.bind_addr"; public static final String IGNORE_BIND_ADDRESS_PROPERTY_OLD="ignore.bind.address"; public static final String MARSHALLING_COMPAT="jgroups.marshalling.compatible"; public static final String TCPPING_INITIAL_HOSTS="jgroups.tcpping.initial_hosts"; public static final String UDP_MCAST_ADDR="jgroups.udp.mcast_addr"; public static final String UDP_MCAST_PORT="jgroups.udp.mcast_port"; public static final String UDP_IP_TTL="jgroups.udp.ip_ttl"; public static final String MPING_MCAST_ADDR="jgroups.mping.mcast_addr"; public static final String MPING_MCAST_PORT="jgroups.mping.mcast_port"; public static final String MPING_IP_TTL="jgroups.mping.ip_ttl"; public static final String MAGIC_NUMBER_FILE="jgroups.conf.magic_number_file"; public static final String RESOLVE_DNS="jgroups.resolve_dns"; public static final String CHANNEL_LOCAL_ADDR_TIMEOUT="jgroups.channel.local_addr_timeout"; public static final String SCHEDULER_MAX_THREADS="jgroups.scheduler.max_threads"; public static final String TIMER_NUM_THREADS="jgroups.timer.num_threads"; public static final String MUX_ENABLED="jgroups.mux.enabled"; public static final String MUX_MIN_THREADS="jgroups.mux.min_threads"; public static final String MUX_MAX_THREADS="jgroups.mux.max_threads"; public static final String MUX_KEEPALIVE="jgroups.mux.keepalive_time"; public static final long DEFAULT_FIRST_UNICAST_SEQNO=1; public static final String SINGLETON_NAME="singleton_name"; public static final long THREADPOOL_SHUTDOWN_WAIT_TIME=3000; public static final long THREAD_SHUTDOWN_WAIT_TIME=300; public static final String DUMMY="dummy-"; public static final int IPV4_SIZE=4; public static final int IPV6_SIZE=16; public static boolean getPropertyAsBoolean(String property, boolean defaultValue) { boolean result=defaultValue; try { String tmp=System.getProperty(property); if(tmp != null) result=Boolean.parseBoolean(tmp); } catch(Throwable t) { } return result; } public static long getPropertyAsLong(String property, long defaultValue) { long result=defaultValue; try { String tmp=System.getProperty(property); if(tmp != null) result=Long.parseLong(tmp); } catch(Throwable t) { } return result; } public static int getPropertyAsInteger(String property, int defaultValue) { int result=defaultValue; try { String tmp=System.getProperty(property); if(tmp != null) result=Integer.parseInt(tmp); } catch(Throwable t) { } return result; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Transport.java0000644000175000017500000000135111366547366024244 0ustar twernertwerner// $Id: Transport.java,v 1.2 2005/07/17 11:38:05 chrislott Exp $ package org.jgroups; /** * Defines a very small subset of the functionality of a channel, * essentially only the methods for sending and receiving messages. * Many building blocks require nothing else than a * bare-bones facility to send and receive messages; therefore the Transport * interface was created. It increases the genericness and portability of * building blocks: being so simple, the Transport interface can easily be * ported to a different toolkit, without requiring any modifications to * building blocks. */ public interface Transport { void send(Message msg) throws Exception; Object receive(long timeout) throws Exception; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ExtendedMembershipListener.java0000644000175000017500000000146211366547366027535 0ustar twernertwernerpackage org.jgroups; /** * @author Bela Ban * @version $Id: ExtendedMembershipListener.java,v 1.2.6.1 2009/06/03 13:49:28 vlada Exp $ */ public interface ExtendedMembershipListener extends MembershipListener { /** * Called after the FLUSH protocol has unblocked previously blocked senders, and * messages can be sent again. This callback only needs to be implemented if we require a * notification of that. * *

        * Note that during new view installation we provide guarantee that unblock invocation strictly * follows view installation at some node A belonging to that view . However, some other message * M may squeeze in between view and unblock callbacks. * * For more details see https://jira.jboss.org/jira/browse/JGRP-986 */ void unblock(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/GetStateEvent.java0000644000175000017500000000113011366547366024765 0ustar twernertwerner// $Id: GetStateEvent.java,v 1.6 2006/03/17 09:04:55 belaban Exp $ package org.jgroups; /** * Represents a GetState event. * Gives access to the requestor. */ public class GetStateEvent { Object requestor=null; String state_id=null; public GetStateEvent(Object requestor, String state_id) { this.requestor=requestor; this.state_id=state_id; } public Object getRequestor() {return requestor;} public String getStateId() {return state_id;} public String toString() {return "GetStateEvent[requestor=" + requestor + ", state_id=" + state_id + ']';} } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ReceiverAdapter.java0000644000175000017500000000073411366547366025321 0ustar twernertwernerpackage org.jgroups; /** * @author Bela Ban * @version $Id: ReceiverAdapter.java,v 1.1 2005/11/08 10:43:38 belaban Exp $ */ public class ReceiverAdapter implements Receiver { public void receive(Message msg) { } public byte[] getState() { return null; } public void setState(byte[] state) { } public void viewAccepted(View new_view) { } public void suspect(Address suspected_mbr) { } public void block() { } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/0000755000175000017500000000000011621261110022151 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/Protocol.java0000644000175000017500000000251311366547366024650 0ustar twernertwernerpackage org.jgroups.jmx; import java.util.Properties; import java.util.Map; /** * @author Bela Ban * @version $Id: Protocol.java,v 1.12 2007/04/27 07:59:24 belaban Exp $ */ public class Protocol implements ProtocolMBean { org.jgroups.stack.Protocol prot; public Protocol() { } public Protocol(org.jgroups.stack.Protocol p) { this.prot=p; } public String getName() { return prot.getName(); } public void attachProtocol(org.jgroups.stack.Protocol p) { this.prot=p; } public String getPropertiesAsString() { return prot.getProperties().toString(); } public void setProperties(Properties p) { prot.setProperties(p); } public boolean getStatsEnabled() { return prot.statsEnabled(); } public void setStatsEnabled(boolean flag) { prot.enableStats(flag); } public void resetStats() { prot.resetStats(); } public String printStats() { return prot.printStats(); } public Map dumpStats() { return prot.dumpStats(); } public void create() throws Exception { prot.init(); } public void start() throws Exception { prot.start(); } public void stop() { prot.stop(); } public void destroy() { prot.destroy(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/JChannel.java0000644000175000017500000002717311366547366024542 0ustar twernertwernerpackage org.jgroups.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import java.io.Serializable; import java.util.Map; /** * @author Bela Ban * @version $Id: JChannel.java,v 1.17 2007/06/06 11:02:32 belaban Exp $ */ public class JChannel implements JChannelMBean { /** Ref to the original JGroups channel */ org.jgroups.JChannel channel; String props=null; String group_name="TestGroup"; String object_name=null; Log log=LogFactory.getLog(getClass()); /*flag to indicate whether to receive blocks, if this is set to true, receive_views is set to true*/ private boolean receive_blocks=false; /*flag to indicate whether to receive local messages *if this is set to false, the JChannel will not receive messages sent by itself*/ private boolean receive_local_msgs=true; /*flag to indicate whether the channel will reconnect (reopen) when the exit message is received*/ private boolean auto_reconnect=false; /*flag t indicate whether the state is supposed to be retrieved after the channel is reconnected *setting this to true, automatically forces auto_reconnect to true*/ private boolean auto_getstate=false; private String mbean_server_name=null; public JChannel() { } public JChannel(org.jgroups.JChannel channel) { this.channel=channel; setValues(); } protected final void setValues() { if(channel != null) { props=getProperties(); group_name=channel.getClusterName(); receive_blocks=getReceiveBlockEvents(); receive_local_msgs=getReceiveLocalMessages(); auto_reconnect=getAutoReconnect(); auto_getstate=getAutoGetState(); } } //void jbossInternalLifecycle(String method) throws Exception; public org.jgroups.JChannel getChannel() { return channel; } public String getVersion() { return Version.printDescription(); } public String getMBeanServerName() { return mbean_server_name; } public void setMBeanServerName(String n) { this.mbean_server_name=n; } public String getProperties() { props=channel.getProperties(); return props; } public void setProperties(String props) { this.props=props; } public String getObjectName() { return object_name; } public void setObjectName(String name) { object_name=name; } public int getNumberOfTasksInTimer() { return channel.getNumberOfTasksInTimer(); } public String dumpTimerQueue() { return channel.dumpTimerQueue(); } public void setClusterConfig(Element config) { StringBuilder buffer=new StringBuilder(); NodeList stack=config.getChildNodes(); int length=stack.getLength(); for(int s=0; s < length; s++) { org.w3c.dom.Node node=stack.item(s); if(node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) continue; Element tag=(Element)node; String protocol=tag.getTagName(); buffer.append(protocol); NamedNodeMap attrs=tag.getAttributes(); int attrLength=attrs.getLength(); if(attrLength > 0) buffer.append('('); for(int a=0; a < attrLength; a++) { Attr attr=(Attr)attrs.item(a); String name=attr.getName(); String value=attr.getValue(); buffer.append(name); buffer.append('='); buffer.append(value); if(a < attrLength - 1) buffer.append(';'); } if(attrLength > 0) buffer.append(')'); buffer.append(':'); } // Remove the trailing ':' buffer.setLength(buffer.length() - 1); setProperties(buffer.toString()); if(log.isDebugEnabled()) log.debug("setting cluster properties from xml to: " + props); } public String getGroupName() { if(channel != null) group_name=channel.getClusterName(); return group_name; } public void setGroupName(String group_name) { this.group_name=group_name; } public String getClusterName() { return getGroupName(); } public void setClusterName(String cluster_name) { setGroupName(cluster_name); } public boolean getReceiveBlockEvents() { if(channel != null) receive_blocks=((Boolean)channel.getOpt(Channel.BLOCK)).booleanValue(); return receive_blocks; } public void setReceiveBlockEvents(boolean flag) { this.receive_blocks=flag; if(channel != null) channel.setOpt(Channel.BLOCK, Boolean.valueOf(flag)); } public boolean getReceiveLocalMessages() { if(channel != null) receive_local_msgs=((Boolean)channel.getOpt(Channel.LOCAL)).booleanValue(); return receive_local_msgs; } public void setReceiveLocalMessages(boolean flag) { this.receive_local_msgs=flag; if(channel != null) channel.setOpt(Channel.LOCAL, Boolean.valueOf(flag)); } public boolean getAutoReconnect() { if(channel != null) auto_reconnect=((Boolean)channel.getOpt(Channel.AUTO_RECONNECT)).booleanValue(); return auto_reconnect; } public void setAutoReconnect(boolean flag) { this.auto_reconnect=flag; if(channel != null) { channel.setOpt(Channel.AUTO_RECONNECT, Boolean.valueOf(flag)); } } public boolean getAutoGetState() { if(channel != null) auto_getstate=((Boolean)channel.getOpt(Channel.AUTO_GETSTATE)).booleanValue(); return auto_getstate; } public void setAutoGetState(boolean flag) { this.auto_getstate=flag; if(channel != null) channel.setOpt(Channel.AUTO_GETSTATE, Boolean.valueOf(flag)); } public boolean getStatsEnabled() { return channel.statsEnabled(); } public void setStatsEnabled(boolean flag) { channel.enableStats(flag); } public Map dumpStats() { return channel.dumpStats(); } public void resetStats() { channel.resetStats(); } public long getSentMessages() {return channel.getSentMessages();} public long getSentBytes() {return channel.getSentBytes();} public long getReceivedMessages() {return channel.getReceivedMessages();} public long getReceivedBytes() {return channel.getReceivedBytes();} public int getTimerThreads() { return channel.getTimerThreads(); } public void create() throws Exception { if(channel != null) channel.close(); channel=new org.jgroups.JChannel(props); setOptions(); setValues(); MBeanServer server=(MBeanServer)MBeanServerFactory.findMBeanServer(mbean_server_name).get(0); JmxConfigurator.registerProtocols(server, channel, getObjectName()); } public void start() throws Exception { channel.connect(group_name); } public void stop() { if(channel != null) channel.disconnect(); } public void destroy() { MBeanServer server=(MBeanServer)MBeanServerFactory.findMBeanServer(mbean_server_name).get(0); JmxConfigurator.unregisterProtocols(server, channel, getObjectName()); if(channel != null) { channel.close(); channel=null; } } // public void jbossInternalLifecycle(String method) throws Exception { // System.out.println("method: " + method); // if (method == null) // throw new IllegalArgumentException("Null method name"); // // if (method.equals("create")) // create(); // else if (method.equals("start")) // start(); // else if (method.equals("stop")) // stop(); // else if (method.equals("destroy")) // destroy(); // else // throw new IllegalArgumentException("Unknown lifecyle method " + method); // } public View getView() { return channel.getView(); } public String getViewAsString() { View v=channel.getView(); return v != null ? v.toString() : "n/a"; } public Address getLocalAddress() { return channel.getLocalAddress(); } public String getLocalAddressAsString() { Address addr=getLocalAddress(); return addr != null? addr.toString() : "n/a"; } /** @deprecated Use addChannelListener() instead */ public void setChannelListener(ChannelListener channel_listener) { if(channel != null) channel.addChannelListener(channel_listener); } public void addChannelListener(ChannelListener listener) { if(channel != null) channel.addChannelListener(listener); } public void removeChannelListener(ChannelListener l) { if(channel != null) channel.removeChannelListener(l); } public boolean isOpen() { return channel.isOpen(); } public boolean isConnected() { return channel.isConnected(); } public int getNumMessages() { return channel.getNumMessages(); } public String dumpQueue() { return channel.dumpQueue(); } public String printProtocolSpec(boolean include_properties) { return channel.printProtocolSpec(include_properties); } public String toString(boolean print_details) { return channel.toString(print_details); } public void connect(String channel_name) throws ChannelException { channel.connect(channel_name); } public void disconnect() { channel.disconnect(); } public void close() { channel.close(); } public void shutdown() { channel.shutdown(); } public void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException { channel.send(msg); } public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException { channel.send(dst, src, obj); } public void sendToAll(String msg) throws ChannelNotConnectedException, ChannelClosedException { send(null, null, msg); } public void down(Event evt) { channel.down(evt); } public Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { return channel.receive(timeout); } public Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException { return channel.peek(timeout); } public void blockOk() { channel.blockOk(); } public boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException { return channel.getState(target, timeout); } public void returnState(byte[] state) { channel.returnState(state); } public void returnState(byte[] state, String state_id) { channel.returnState(state, state_id); } private void setOptions() { channel.setOpt(Channel.BLOCK, Boolean.valueOf(this.receive_blocks)); channel.setOpt(Channel.LOCAL, Boolean.valueOf(this.receive_local_msgs)); channel.setOpt(Channel.AUTO_RECONNECT, Boolean.valueOf(this.auto_reconnect)); channel.setOpt(Channel.AUTO_GETSTATE, Boolean.valueOf(this.auto_getstate)); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/JmxConfigurator.java0000644000175000017500000001406411366547366026174 0ustar twernertwernerpackage org.jgroups.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import javax.management.*; import java.util.Vector; import java.util.Set; import java.util.Iterator; /** * @author Bela Ban * @version $Id: JmxConfigurator.java,v 1.10 2006/08/09 13:02:21 belaban Exp $ */ public class JmxConfigurator { static final Log log=LogFactory.getLog(JmxConfigurator.class); /** * Registers an already created channel with the MBeanServer. Creates an org.jgroups.jmx.JChannel which * delegates to the org.jgroups.JChannel and registers it. Optionally, this method will also try to * create one MBean proxy for each protocol in the channel's protocol stack, and register it as well. * @param channel * @param server * @param domain Has to be a JMX ObjectName of the domain, e.g. DefaultDomain:name=JGroups * @param register_protocols * @return org.jgroups.jmx.JChannel for the specified org.jgroups.JChannel */ public static org.jgroups.jmx.JChannel registerChannel(org.jgroups.JChannel channel, MBeanServer server, String domain, String cluster_name, boolean register_protocols) throws Exception { if(cluster_name == null) cluster_name=channel != null? channel.getClusterName() : null; if(cluster_name == null) cluster_name="null"; if(register_protocols) { String tmp=domain + ":type=protocol,cluster=" +cluster_name; registerProtocols(server, channel, tmp); } return registerChannel(channel, server, domain + ":type=channel,cluster=" +cluster_name); } /** * Registers an already created channel with the MBeanServer. Creates an org.jgroups.jmx.JChannel which * delegates to the org.jgroups.JChannel and registers it. * @param channel * @param server * @param name The JMX ObjectName * @return org.jgroups.jmx.JChannel for the specified org.jgroups.JChannel */ public static org.jgroups.jmx.JChannel registerChannel(org.jgroups.JChannel channel, MBeanServer server, String name) throws Exception { JChannel retval=new JChannel(channel); server.registerMBean(retval, new ObjectName(name)); return retval; } public static void unregisterChannel(MBeanServer server, ObjectName name) throws Exception { if(server != null) server.unregisterMBean(name); } public static void unregisterChannel(MBeanServer server, String name) throws Exception { if(server != null) server.unregisterMBean(new ObjectName(name)); } public static org.jgroups.jmx.JChannelFactory registerChannelFactory(org.jgroups.JChannelFactory factory, MBeanServer server, String name) throws Exception { JChannelFactory retval=new JChannelFactory(factory); server.registerMBean(retval, new ObjectName(name)); return retval; } /** * Takes all protocols of an existing stack, creates corresponding MBean proxies and registers them with * the MBean server * @param channel * @param prefix */ public static void registerProtocols(MBeanServer server, org.jgroups.JChannel channel, String prefix) throws Exception { ProtocolStack stack=channel.getProtocolStack(); Vector protocols=stack.getProtocols(); org.jgroups.stack.Protocol prot; org.jgroups.jmx.Protocol p=null; for(int i=0; i < protocols.size(); i++) { prot=(org.jgroups.stack.Protocol)protocols.get(i); try { p=findProtocol(prot); } catch(ClassNotFoundException e) { p=null; } catch(Throwable e) { log.error("failed creating a JMX wrapper instance for " + prot, e); p=null; } if(p == null) p=new org.jgroups.jmx.Protocol(prot); ObjectName prot_name=new ObjectName(prefix + ",protocol=" + prot.getName()); server.registerMBean(p, prot_name); } } public static void unregisterProtocols(MBeanServer server, org.jgroups.JChannel channel, String channel_name) { ProtocolStack stack=channel.getProtocolStack(); Vector protocols=stack.getProtocols(); org.jgroups.stack.Protocol prot; ObjectName prot_name=null; for(int i=0; i < protocols.size(); i++) { prot=(org.jgroups.stack.Protocol)protocols.get(i); try { prot_name=new ObjectName(channel_name + ",protocol=" + prot.getName()); server.unregisterMBean(prot_name); } catch(Throwable e) { log.error("failed to unregister " + prot_name, e); } } } /** * Unregisters object_name and everything under it * @param object_name */ public static void unregister(MBeanServer server, String object_name) throws Exception { Set mbeans=server.queryNames(new ObjectName(object_name), null); if(mbeans != null) { ObjectName name; for(Iterator it=mbeans.iterator(); it.hasNext();) { name=(ObjectName)it.next(); server.unregisterMBean(name); } } } protected static Protocol findProtocol(org.jgroups.stack.Protocol prot) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Protocol p; String prot_name=prot.getClass().getName(); String clname=prot_name.replaceFirst("org.jgroups.", "org.jgroups.jmx."); Class cl=Util.loadClass(clname, JmxConfigurator.class); if(cl != null) { p=(Protocol)cl.newInstance(); p.attachProtocol(prot); return p; } return null; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/package.html0000644000175000017500000000014611366547366024465 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/JChannelFactoryMBean.java0000644000175000017500000000216211366547366026764 0ustar twernertwernerpackage org.jgroups.jmx; import org.jgroups.Channel; /** * @author Bela Ban * @version $Id: JChannelFactoryMBean.java,v 1.3.10.1 2007/11/28 11:39:57 belaban Exp $ */ public interface JChannelFactoryMBean { String getMultiplexerConfig(); void setMultiplexerConfig(String properties) throws Exception; void setMultiplexerConfig(String properties, boolean replace) throws Exception; String getDomain(); void setDomain(String name); boolean isExposeChannels(); void setExposeChannels(boolean flag); boolean isExposeProtocols(); void setExposeProtocols(boolean f); String getConfig(String stack_name) throws Exception; boolean removeConfig(String stack_name); void clearConfigurations(); Channel createMultiplexerChannel(String stack_name, String id) throws Exception; Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception; void create() throws Exception; void start() throws Exception; void stop(); void destroy(); String dumpConfiguration(); String dumpChannels(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/0000755000175000017500000000000011621261110024175 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCPPINGMBean.java0000644000175000017500000000035311366547366027102 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: TCPPINGMBean.java,v 1.1 2005/06/14 10:10:10 belaban Exp $ */ public interface TCPPINGMBean extends DiscoveryMBean { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/0000755000175000017500000000000011621261110025451 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/FLUSHMBean.java0000644000175000017500000000055611366547366030140 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.ProtocolMBean; /** * @author Vladimir Blagojevic * @version $Id$ */ public interface FLUSHMBean extends ProtocolMBean { public double getAverageFlushDuration(); public long getTotalTimeInFlush(); public int getNumberOfFlushes(); boolean startFlush(); void stopFlush(); } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFER.javalibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFER.java0000644000175000017500000000171011366547366032142 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.Protocol; /** * @author Vladimir Blagojevic * @version $Id$ */ public class STREAMING_STATE_TRANSFER extends Protocol implements STREAMING_STATE_TRANSFERMBean { org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER p; public STREAMING_STATE_TRANSFER() { } public STREAMING_STATE_TRANSFER(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.pbcast.STREAMING_STATE_TRANSFER)p; } public int getNumberOfStateRequests() { return p.getNumberOfStateRequests(); } public long getNumberOfStateBytesSent() { return p.getNumberOfStateBytesSent(); } public double getAverageStateSize() { return p.getAverageStateSize(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFER.java0000644000175000017500000000166211366547366030537 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: STATE_TRANSFER.java,v 1.1 2005/06/14 08:36:50 belaban Exp $ */ public class STATE_TRANSFER extends Protocol implements STATE_TRANSFERMBean { org.jgroups.protocols.pbcast.STATE_TRANSFER p; public STATE_TRANSFER() { } public STATE_TRANSFER(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.pbcast.STATE_TRANSFER)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.pbcast.STATE_TRANSFER)p; } public int getNumberOfStateRequests() { return p.getNumberOfStateRequests(); } public long getNumberOfStateBytesSent() { return p.getNumberOfStateBytesSent(); } public double getAverageStateSize() { return p.getAverageStateSize(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/FLUSH.java0000644000175000017500000000166511366547366027237 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.Protocol; /** * @author Vladimir Blagojevic * @version $Id$ */ public class FLUSH extends Protocol implements FLUSHMBean { org.jgroups.protocols.pbcast.FLUSH p; public FLUSH(){} public FLUSH(org.jgroups.stack.Protocol p){ super(p); this.p = (org.jgroups.protocols.pbcast.FLUSH) p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p = (org.jgroups.protocols.pbcast.FLUSH) p; } public double getAverageFlushDuration() { return p.getAverageFlushDuration(); } public long getTotalTimeInFlush() { return p.getTotalTimeInFlush(); } public int getNumberOfFlushes() { return p.getNumberOfFlushes(); } public boolean startFlush() { return p.startFlush(); } public void stopFlush() { p.stopFlush(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/package.html0000644000175000017500000000015511366547366027765 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/GMS.java0000644000175000017500000000416711366547366027004 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: GMS.java,v 1.3 2005/12/23 14:57:05 belaban Exp $ */ public class GMS extends Protocol implements GMSMBean { org.jgroups.protocols.pbcast.GMS p; public GMS() { } public GMS(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.pbcast.GMS)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.pbcast.GMS)p; } public String getView() { return p.getView(); } public String getLocalAddress() { return p.getLocalAddress(); } public String getMembers() { return p.getMembers(); } public int getNumMembers() { return p.getNumMembers(); } public boolean isCoordinator() { return p.isCoordinator(); } public int getNumberOfViews() { return p.getNumberOfViews(); } public long getJoinTimeout() { return p.getJoinTimeout(); } public void setJoinTimeout(long t) { p.setJoinTimeout(t); } public long getJoinRetryTimeout() { return p.getJoinRetryTimeout(); } public void setJoinRetryTimeout(long t) { p.setJoinRetryTimeout(t); } public boolean isShun() { return p.isShun(); } public void setShun(boolean s) { p.setShun(s); } public String printPreviousMembers() { return p.printPreviousMembers(); } public String printPreviousViews() { return p.printPreviousViews(); } public int getViewHandlerQueue() { return p.viewHandlerSize(); } public boolean isViewHandlerSuspended() { return p.isViewHandlerSuspended(); } public String dumpViewHandlerQueue() { return p.dumpViewHandlerQueue(); } public String dumpHistory() { return p.dumpViewHandlerHistory(); } public void suspendViewHandler() { p.suspendViewHandler(); } public void resumeViewHandler() { p.resumeViewHandler(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STABLE.java0000644000175000017500000000266411366547366027330 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: STABLE.java,v 1.4 2007/01/09 11:40:16 belaban Exp $ */ public class STABLE extends Protocol implements STABLEMBean { org.jgroups.protocols.pbcast.STABLE p; public STABLE() { } public STABLE(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.pbcast.STABLE)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.pbcast.STABLE)p; } public long getDesiredAverageGossip() { return p.getDesiredAverageGossip(); } public void setDesiredAverageGossip(long gossip_interval) { p.setDesiredAverageGossip(gossip_interval); } public long getMaxBytes() { return p.getMaxBytes(); } public void setMaxBytes(long max_bytes) { p.setMaxBytes(max_bytes); } public long getBytes() { return p.getBytes(); } public int getStableSent() { return p.getStableSent(); } public int getStableReceived() { return p.getStableReceived(); } public int getStabilitySent() { return p.getStabilitySent(); } public int getStabilityReceived() { return p.getStabilityReceived(); } public void runMessageGarbageCollection() { p.runMessageGarbageCollection(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STATE_TRANSFERMBean.java0000644000175000017500000000055311366547366031440 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: STATE_TRANSFERMBean.java,v 1.1 2005/06/14 08:36:50 belaban Exp $ */ public interface STATE_TRANSFERMBean extends ProtocolMBean { int getNumberOfStateRequests(); long getNumberOfStateBytesSent(); double getAverageStateSize(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STABLEMBean.java0000644000175000017500000000107011366547366030221 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: STABLEMBean.java,v 1.4 2007/01/09 11:40:16 belaban Exp $ */ public interface STABLEMBean extends ProtocolMBean { long getDesiredAverageGossip(); void setDesiredAverageGossip(long gossip_interval); long getMaxBytes(); void setMaxBytes(long max_bytes); long getBytes(); int getStableSent(); int getStableReceived(); int getStabilitySent(); int getStabilityReceived(); void runMessageGarbageCollection(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/GMSMBean.java0000644000175000017500000000150011366547366027673 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: GMSMBean.java,v 1.3 2005/12/23 14:57:05 belaban Exp $ */ public interface GMSMBean extends ProtocolMBean { String getView(); String getLocalAddress(); String getMembers(); int getNumMembers(); boolean isCoordinator(); int getNumberOfViews(); long getJoinTimeout(); void setJoinTimeout(long t); long getJoinRetryTimeout(); void setJoinRetryTimeout(long t); boolean isShun(); void setShun(boolean s); String printPreviousMembers(); String printPreviousViews(); int getViewHandlerQueue(); boolean isViewHandlerSuspended(); String dumpViewHandlerQueue(); String dumpHistory(); void suspendViewHandler(); void resumeViewHandler(); } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFERMBean.javalibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/STREAMING_STATE_TRANSFERMBean0000644000175000017500000000050211366547366032123 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.ProtocolMBean; /** * @author Vladimir Blagojevic * @version $Id$ */ public interface STREAMING_STATE_TRANSFERMBean extends ProtocolMBean { int getNumberOfStateRequests(); long getNumberOfStateBytesSent(); double getAverageStateSize(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/NAKACKMBean.java0000644000175000017500000000310111366547366030174 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: NAKACKMBean.java,v 1.13.2.2 2009/09/11 12:09:18 belaban Exp $ */ public interface NAKACKMBean extends ProtocolMBean { int getGcLag(); void setGcLag(int gc_lag); boolean isUseMcastXmit(); void setUseMcastXmit(boolean use_mcast_xmit); boolean isXmitFromRandomMember(); void setXmitFromRandomMember(boolean xmit_from_random_member); boolean isDiscardDeliveredMsgs(); void setDiscardDeliveredMsgs(boolean discard_delivered_msgs); int getMaxXmitBufSize(); void setMaxXmitBufSize(int max_xmit_buf_size); /** * * @return * @deprecated removed in 2.6 */ long getMaxXmitSize(); /** * * @param max_xmit_size * @deprecated removed in 2.6 */ void setMaxXmitSize(long max_xmit_size); int getXmitTableSize(); long getXmitRequestsReceived(); long getXmitRequestsSent(); long getXmitResponsesReceived(); long getXmitResponsesSent(); long getMissingMessagesReceived(); int getPendingRetransmissionRequests(); long getUndeliveredMessages(); String printXmitTable(); String printMessages(); String printStabilityMessages(); String printMergeHistory(); String printRetransmissionAvgs(); String printRetransmissionTimes(); String printSmoothedRetransmissionAvgs(); String printLossRates(); double getTotalAvgXmitTime(); double getTotalAvgSmoothedXmitTime(); int getAverageLossRate(); int getAverageSmoothedLossRate(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/pbcast/NAKACK.java0000644000175000017500000000730311366547366027301 0ustar twernertwernerpackage org.jgroups.jmx.protocols.pbcast; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: NAKACK.java,v 1.14.2.2 2009/09/11 12:09:18 belaban Exp $ */ public class NAKACK extends Protocol implements NAKACKMBean { org.jgroups.protocols.pbcast.NAKACK p; public NAKACK() { } public NAKACK(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.pbcast.NAKACK)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.pbcast.NAKACK)p; } public int getGcLag() { return p.getGcLag(); } public void setGcLag(int gc_lag) { p.setGcLag(gc_lag); } public boolean isUseMcastXmit() { return p.isUseMcastXmit(); } public void setUseMcastXmit(boolean use_mcast_xmit) { p.setUseMcastXmit(use_mcast_xmit); } public boolean isXmitFromRandomMember() { return p.isXmitFromRandomMember(); } public void setXmitFromRandomMember(boolean xmit_from_random_member) { p.setXmitFromRandomMember(xmit_from_random_member); } public boolean isDiscardDeliveredMsgs() { return p.isDiscardDeliveredMsgs(); } public void setDiscardDeliveredMsgs(boolean discard_delivered_msgs) { p.setDiscardDeliveredMsgs(discard_delivered_msgs); } public int getMaxXmitBufSize() { return p.getMaxXmitBufSize(); } public void setMaxXmitBufSize(int max_xmit_buf_size) { p.setMaxXmitBufSize(max_xmit_buf_size); } /** * * @return * @deprecated removed in 2.6 */ public long getMaxXmitSize() { return -1; } /** * @param max_xmit_size * @deprecated removed in 2.6 */ public void setMaxXmitSize(long max_xmit_size) { } public long getXmitRequestsReceived() { return p.getXmitRequestsReceived(); } public long getXmitRequestsSent() { return p.getXmitRequestsSent(); } public long getXmitResponsesReceived() { return p.getXmitResponsesReceived(); } public long getXmitResponsesSent() { return p.getXmitResponsesSent(); } public long getMissingMessagesReceived() { return p.getMissingMessagesReceived(); } public int getPendingRetransmissionRequests() { return p.getPendingRetransmissionRequests(); } public long getUndeliveredMessages() { return p.getUndeliveredMessages(); } public int getXmitTableSize() { return p.getXmitTableSize(); } public String printXmitTable() { return p.printMessages(); } public String printMessages() { return p.printMessages(); } public String printStabilityMessages() { return p.printStabilityMessages(); } public String printMergeHistory() { return p.printMergeHistory(); } public String printRetransmissionAvgs() { return p.printRetransmissionAvgs(); } public String printRetransmissionTimes() { return p.printRetransmissionTimes(); } public String printSmoothedRetransmissionAvgs() { return p.printSmoothedRetransmissionAvgs(); } public String printLossRates() { return p.printLossRates(); } public double getTotalAvgXmitTime() { return p.getTotalAverageRetransmissionTime(); } public double getTotalAvgSmoothedXmitTime() { return p.getTotalAverageSmoothedRetransmissionTime(); } public int getAverageLossRate() { return (int)(p.getAverageLossRate() * 100); } public int getAverageSmoothedLossRate() { return (int)(p.getAverageSmoothedLossRate() * 100); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/MERGE2.java0000644000175000017500000000155611366547366026022 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: MERGE2.java,v 1.1 2005/06/13 11:55:31 belaban Exp $ */ public class MERGE2 extends Protocol implements MERGE2MBean { org.jgroups.protocols.MERGE2 p; public MERGE2() { } public MERGE2(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.MERGE2)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.MERGE2)p; } public long getMinInterval() { return p.getMinInterval(); } public void setMinInterval(long i) { p.setMinInterval(i); } public long getMaxInterval() { return p.getMaxInterval(); } public void setMaxInterval(long l) { p.setMaxInterval(l); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/UDPMBean.java0000644000175000017500000000026511366547366026430 0ustar twernertwernerpackage org.jgroups.jmx.protocols; /** * @author Bela Ban * @version $Id: UDPMBean.java,v 1.3 2005/08/17 07:32:29 belaban Exp $ */ public interface UDPMBean extends TPMBean { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/UNICASTMBean.java0000644000175000017500000000127211366547366027105 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: UNICASTMBean.java,v 1.8.4.2 2009/09/18 08:03:30 belaban Exp $ */ public interface UNICASTMBean extends ProtocolMBean { String getLocalAddress(); String getMembers(); String printConnections(); long getMessagesSent(); long getMessagesReceived(); long getBytesSent(); long getBytesReceived(); long getAcksSent(); long getAcksReceived(); long getNumRetransmissions(); int getNumUnackedMessages(); String getUnackedMessages(); int getNumberOfMessagesInReceiveWindows(); int getAgeOutCacheSize(); String printAgeOutCache(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/STATSMBean.java0000644000175000017500000000120511366547366026671 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: STATSMBean.java,v 1.1 2005/06/07 10:17:26 belaban Exp $ */ public interface STATSMBean extends ProtocolMBean { long getSentMessages(); long getSentBytes(); long getSentUnicastMessages(); long getSentUnicastBytes(); long getSentMcastMessages(); long getSentMcastBytes(); long getReceivedMessages(); long getReceivedBytes(); long getReceivedUnicastMessages(); long getReceivedUnicastBytes(); long getReceivedMcastMessages(); long getReceivedMcastBytes(); String printStats(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCPMBean.java0000644000175000017500000000111511366547366026421 0ustar twernertwernerpackage org.jgroups.jmx.protocols; /** * @author Bela Ban * @version $Id: TCPMBean.java,v 1.3 2006/10/02 06:47:53 belaban Exp $ */ public interface TCPMBean extends TPMBean { int getOpenConnections(); String getBindAddr(); void setBindAddr(String bind_addr); int getStartPort(); void setStartPort(int start_port); int getEndPort(); void setEndPort(int end_port); long getReaperInterval(); void setReaperInterval(long reaper_interval); long getConnExpireTime(); void setConnExpireTime(long conn_expire_time); String printConnections(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FCMBean.java0000644000175000017500000000156411366547366026273 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: FCMBean.java,v 1.7 2007/05/07 09:55:37 belaban Exp $ */ public interface FCMBean extends ProtocolMBean { long getMaxCredits(); void setMaxCredits(long max_credits); double getMinThreshold(); void setMinThreshold(double min_threshold); long getMinCredits(); void setMinCredits(long min_credits); int getBlockings(); long getTotalTimeBlocked(); long getMaxBlockTime(); void setMaxBlockTime(long t); double getAverageTimeBlocked(); int getCreditRequestsReceived(); int getCreditRequestsSent(); int getCreditResponsesReceived(); int getCreditResponsesSent(); String printSenderCredits(); String printReceiverCredits(); String printCredits(); String showLastBlockingTimes(); void unblock(); }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FDMBean.java0000644000175000017500000000121311366547366026263 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: FDMBean.java,v 1.1 2005/06/13 12:44:06 belaban Exp $ */ public interface FDMBean extends ProtocolMBean { int getNumberOfHeartbeatsSent(); int getNumSuspectEventsGenerated(); String getLocalAddress(); String getMembers(); String getPingableMembers(); String getPingDest(); long getTimeout(); void setTimeout(long timeout); int getMaxTries(); void setMaxTries(int max_tries); int getCurrentNumTries(); boolean isShun(); void setShun(boolean flag); String printSuspectHistory(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/package.html0000644000175000017500000000012511366547366026506 0ustar twernertwerner libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/MERGE2MBean.java0000644000175000017500000000054011366547366026715 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: MERGE2MBean.java,v 1.1 2005/06/13 11:55:31 belaban Exp $ */ public interface MERGE2MBean extends ProtocolMBean { long getMinInterval(); void setMinInterval(long i); long getMaxInterval(); void setMaxInterval(long l); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FD.java0000644000175000017500000000320311366547366025361 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: FD.java,v 1.1 2005/06/13 12:44:06 belaban Exp $ */ public class FD extends Protocol implements FDMBean { org.jgroups.protocols.FD p; public FD() { } public FD(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.FD)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.FD)p; } public int getNumberOfHeartbeatsSent() { return p.getNumberOfHeartbeatsSent(); } public int getNumSuspectEventsGenerated() { return p.getNumSuspectEventsGenerated(); } public long getTimeout() { return p.getTimeout(); } public void setTimeout(long timeout) { p.setTimeout(timeout); } public int getMaxTries() { return p.getMaxTries(); } public void setMaxTries(int max_tries) { p.setMaxTries(max_tries); } public int getCurrentNumTries() { return p.getCurrentNumTries(); } public boolean isShun() { return p.isShun(); } public void setShun(boolean flag) { p.setShun(flag); } public String getLocalAddress() { return p.getLocalAddress(); } public String getMembers() { return p.getMembers(); } public String getPingableMembers() { return p.getPingableMembers(); } public String getPingDest() { return p.getPingDest(); } public String printSuspectHistory() { return p.printSuspectHistory(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FRAG.java0000644000175000017500000000210611366547366025610 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: FRAG.java,v 1.1 2005/06/13 14:29:28 belaban Exp $ */ public class FRAG extends Protocol implements FRAGMBean { org.jgroups.protocols.FRAG p; public FRAG() { } public FRAG(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.FRAG)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.FRAG)p; } public int getFragSize() { return p.getFragSize(); } public void setFragSize(int s) { p.setFragSize(s); } public long getNumberOfSentMessages() { return p.getNumberOfSentMessages(); } public long getNumberOfSentFragments() { return p.getNumberOfSentFragments(); } public long getNumberOfReceivedMessages() { return p.getNumberOfReceivedMessages(); } public long getNumberOfReceivedFragments() { return p.getNumberOfReceivedFragments(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/DiscoveryMBean.java0000644000175000017500000000111611366547366027743 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; import java.util.Vector; /** * @author Bela Ban * @version $Id: DiscoveryMBean.java,v 1.4 2005/08/26 14:19:09 belaban Exp $ */ public interface DiscoveryMBean extends ProtocolMBean { long getTimeout(); void setTimeout(long timeout); int getInitialMembers(); void setInitialMembers(int num_initial_members); int getPingRequests(); void setPingRequests(int num_ping_requests); int getDiscoveryRequestsSent(); Vector findInitialMembers(); String findInitialMembersAsString(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TP.java0000644000175000017500000001065711366547366025426 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.Address; import org.jgroups.stack.Protocol; import java.net.UnknownHostException; import java.util.List; /** * @author Bela Ban * @version $Id: TP.java,v 1.12 2006/12/31 14:23:29 belaban Exp $ */ public class TP extends org.jgroups.jmx.Protocol implements TPMBean { org.jgroups.protocols.TP tp; public TP() { } public TP(Protocol p) { super(p); tp=(org.jgroups.protocols.TP)p; } public void attachProtocol(Protocol p) { super.attachProtocol(p); tp=(org.jgroups.protocols.TP)p; } public long getMessagesSent() { return tp.getNumMessagesSent(); } public long getMessagesReceived() { return tp.getNumMessagesReceived(); } public long getBytesSent() { return tp.getNumBytesSent(); } public long getBytesReceived() { return tp.getNumBytesReceived(); } public Address getLocalAddress() { return tp.getLocalAddress(); } public String getBindAddress() { return tp.getBindAddress(); } public String getChannelName() { return tp.getChannelName(); } public void setBindAddress(String bind_address) throws UnknownHostException { tp.setBindAddress(bind_address); } public boolean isReceiveOnAllInterfaces() { return tp.isReceiveOnAllInterfaces(); } public List getReceiveInterfaces() { return tp.getReceiveInterfaces(); } public boolean isSendOnAllInterfaces() { return tp.isSendOnAllInterfaces(); } public List getSendInterfaces() { return tp.getSendInterfaces(); } public boolean isDiscardIncompatiblePackets() { return tp.isDiscardIncompatiblePackets(); } public void setDiscardIncompatiblePackets(boolean flag) { tp.setDiscardIncompatiblePackets(flag); } public boolean isEnableBundling() { return tp.isEnableBundling(); } public void setEnableBundling(boolean flag) { tp.setEnableBundling(flag); } public int getMaxBundleSize() { return tp.getMaxBundleSize(); } public void setMaxBundleSize(int size) { tp.setMaxBundleSize(size); } public long getMaxBundleTimeout() { return tp.getMaxBundleTimeout(); } public void setMaxBundleTimeout(long timeout) { tp.setMaxBundleTimeout(timeout); } public boolean isLoopback() { return tp.isLoopback(); } public void setLoopback(boolean b) { tp.setLoopback(b); } public boolean isUseIncomingPacketHandler() { return tp.isUseIncomingPacketHandler(); } public int getOOBMinPoolSize() { return tp.getOOBMinPoolSize(); } public void setOOBMinPoolSize(int size) { tp.setOOBMinPoolSize(size); } public int getOOBMaxPoolSize() { return tp.getOOBMaxPoolSize(); } public void setOOBMaxPoolSize(int size) { tp.setOOBMaxPoolSize(size); } public int getOOBPoolSize() { return tp.getOOBPoolSize(); } public long getOOBKeepAliveTime() { return tp.getOOBKeepAliveTime(); } public void setOOBKeepAliveTime(long time) { tp.setOOBKeepAliveTime(time); } public long getOOBMessages() { return tp.getOOBMessages(); } public int getOOBQueueSize() { return tp.getOOBQueueSize(); } public int getOOBMaxQueueSize() { return tp.getOOBMaxQueueSize(); } public int getIncomingMinPoolSize() { return tp.getIncomingMinPoolSize(); } public void setIncomingMinPoolSize(int size) { tp.setIncomingMinPoolSize(size); } public int getIncomingMaxPoolSize() { return tp.getIncomingMaxPoolSize(); } public void setIncomingMaxPoolSize(int size) { tp.setIncomingMaxPoolSize(size); } public int getIncomingPoolSize() { return tp.getIncomingPoolSize(); } public long getIncomingKeepAliveTime() { return tp.getIncomingKeepAliveTime(); } public void setIncomingKeepAliveTime(long time) { tp.setIncomingKeepAliveTime(time); } public long getIncomingMessages() { return tp.getIncomingMessages(); } public int getIncomingQueueSize() { return tp.getIncomingQueueSize(); } public int getIncomingMaxQueueSize() { return tp.getIncomingMaxQueueSize(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FRAGMBean.java0000644000175000017500000000066211366547366026520 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: FRAGMBean.java,v 1.2 2005/06/13 14:53:48 belaban Exp $ */ public interface FRAGMBean extends ProtocolMBean { int getFragSize(); void setFragSize(int s); long getNumberOfSentMessages(); long getNumberOfSentFragments(); long getNumberOfReceivedMessages(); long getNumberOfReceivedFragments(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/PARTITIONMBean.java0000644000175000017500000000056411366547366027353 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; import java.net.InetAddress; import java.util.List; /** * @author Bela Ban * @version $Id: PARTITIONMBean.java,v 1.2 2007/08/27 10:46:45 belaban Exp $ */ public interface PARTITIONMBean extends ProtocolMBean { boolean isPartitionOn(); void startPartition(); void stopPartition(); }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FD_SOCK.java0000644000175000017500000000220311366547366026177 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: FD_SOCK.java,v 1.2 2007/08/30 10:41:41 belaban Exp $ */ public class FD_SOCK extends Protocol implements FD_SOCKMBean { org.jgroups.protocols.FD_SOCK p; public FD_SOCK() { } public FD_SOCK(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.FD_SOCK)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.FD_SOCK)p; } public String getLocalAddress() { return p.getLocalAddress(); } public String getMembers() { return p.getMembers(); } public String getPingableMembers() { return p.getPingableMembers(); } public String getPingDest() { return p.getPingDest(); } public int getNumSuspectEventsGenerated() { return p.getNumSuspectEventsGenerated(); } public String printSuspectHistory() { return p.printSuspectHistory(); } public String printCache() { return p.printCache(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TPMBean.java0000644000175000017500000000345111366547366026323 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; import org.jgroups.Address; import java.net.UnknownHostException; import java.util.List; /** * @author Bela Ban * @version $Id: TPMBean.java,v 1.12 2007/07/02 11:15:21 belaban Exp $ */ public interface TPMBean extends ProtocolMBean { Address getLocalAddress(); String getBindAddress(); String getChannelName(); long getMessagesSent(); long getMessagesReceived(); long getBytesSent(); long getBytesReceived(); void setBindAddress(String bind_address) throws UnknownHostException; boolean isReceiveOnAllInterfaces(); List getReceiveInterfaces(); boolean isSendOnAllInterfaces(); List getSendInterfaces(); boolean isDiscardIncompatiblePackets(); void setDiscardIncompatiblePackets(boolean flag); boolean isEnableBundling(); void setEnableBundling(boolean flag); int getMaxBundleSize(); void setMaxBundleSize(int size); long getMaxBundleTimeout(); void setMaxBundleTimeout(long timeout); boolean isLoopback(); void setLoopback(boolean b); boolean isUseIncomingPacketHandler(); int getOOBMinPoolSize(); void setOOBMinPoolSize(int size); int getOOBMaxPoolSize(); void setOOBMaxPoolSize(int size); int getOOBPoolSize(); long getOOBKeepAliveTime(); void setOOBKeepAliveTime(long time); long getOOBMessages(); int getOOBQueueSize(); int getOOBMaxQueueSize(); int getIncomingMinPoolSize(); void setIncomingMinPoolSize(int size); int getIncomingMaxPoolSize(); void setIncomingMaxPoolSize(int size); int getIncomingPoolSize(); long getIncomingKeepAliveTime(); void setIncomingKeepAliveTime(long time); long getIncomingMessages(); int getIncomingQueueSize(); int getIncomingMaxQueueSize(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/SFC.java0000644000175000017500000000370611366547366025513 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; import java.util.Map; /** * @author Bela Ban * @version $Id: SFC.java,v 1.2 2007/01/09 10:19:23 belaban Exp $ */ public class SFC extends Protocol implements SFCMBean { org.jgroups.protocols.SFC p; public SFC() { } public SFC(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.SFC)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.SFC)p; } public void resetStats() { super.resetStats(); p.resetStats(); } public long getMaxCredits() { return p.getMaxCredits(); } public long getBytesSent() { return p.getBytesSent(); } public long getCredits() { return p.getCredits(); } public long getBlockings() { return p.getBlockings(); } public long getCreditRequestsReceived() { return p.getCreditRequestsReceived(); } public long getCreditRequestsSent() { return p.getCreditRequestsSent(); } public long getReplenishmentsReceived() { return p.getReplenishmentsReceived(); } public long getReplenishmentsSent() { return p.getReplenishmentsSent(); } public long getTotalBlockingTime() { return p.getTotalBlockingTime(); } public double getAverageBlockingTime() { return p.getAverageBlockingTime(); } public Map dumpStats() { return p.dumpStats(); } public String printBlockingTimes() { return p.printBlockingTimes(); } public String printReceived() { return p.printReceived(); } public String printPendingCreditors() { return p.printPendingCreditors(); } public String printPendingRequesters() { return p.printPendingRequesters(); } public void unblock() { p.unblock(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/PARTITION.java0000644000175000017500000000152411366547366026445 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: PARTITION.java,v 1.2 2007/08/27 10:46:45 belaban Exp $ */ public class PARTITION extends Protocol implements PARTITIONMBean { org.jgroups.protocols.PARTITION partiton; public PARTITION() { } public PARTITION(org.jgroups.stack.Protocol p) { super(p); this.partiton=(org.jgroups.protocols.PARTITION)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.partiton=(org.jgroups.protocols.PARTITION)p; } public boolean isPartitionOn() { return partiton.isPartitionOn(); } public void startPartition() { partiton.startPartition(); } public void stopPartition() { partiton.stopPartition(); } }libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/PINGMBean.java0000644000175000017500000000034511366547366026534 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: PINGMBean.java,v 1.1 2005/06/08 15:17:30 belaban Exp $ */ public interface PINGMBean extends DiscoveryMBean { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/UNICAST.java0000644000175000017500000000351411366547366026203 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: UNICAST.java,v 1.8.4.3 2009/09/18 08:03:30 belaban Exp $ */ public class UNICAST extends Protocol implements UNICASTMBean { org.jgroups.protocols.UNICAST p; public UNICAST() { } public UNICAST(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.UNICAST)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.UNICAST)p; } public String getLocalAddress() { return p.getLocalAddress(); } public String getMembers() { return p.getMembers(); } public String printConnections() { return p.printConnections(); } public long getMessagesSent() { return p.getNumMessagesSent(); } public long getMessagesReceived() { return p.getNumMessagesReceived(); } public long getBytesSent() { return p.getNumBytesSent(); } public long getBytesReceived() { return p.getNumBytesReceived(); } public long getAcksSent() { return p.getNumAcksSent(); } public long getAcksReceived() { return p.getNumAcksReceived(); } public long getNumRetransmissions() { return p.getNumberOfRetransmissions(); } public int getNumUnackedMessages() { return p.getNumberOfUnackedMessages(); } public String getUnackedMessages() { return p.printUnackedMessages(); } public int getNumberOfMessagesInReceiveWindows() { return p.getNumberOfMessagesInReceiveWindows(); } public int getAgeOutCacheSize() { return p.getAgeOutCacheSize(); } public String printAgeOutCache() { return p.printAgeOutCache(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FD_ALLMBean.java0000644000175000017500000000120311366547366026752 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: FD_ALLMBean.java,v 1.1 2006/12/22 09:34:53 belaban Exp $ */ public interface FD_ALLMBean extends ProtocolMBean { int getHeartbeatsSent(); int getHeartbeatsReceived(); int getSuspectEventsSent(); String getLocalAddress(); String getMembers(); long getTimeout(); void setTimeout(long timeout); long getInterval(); void setInterval(long interval); boolean isShun(); void setShun(boolean flag); boolean isRunning(); String printSuspectHistory(); String printTimestamps(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/Discovery.java0000644000175000017500000000271411366547366027045 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; import java.util.Vector; /** * @author Bela Ban * @version $Id: Discovery.java,v 1.5.2.1 2007/11/20 08:37:25 belaban Exp $ */ public class Discovery extends Protocol implements DiscoveryMBean { org.jgroups.protocols.Discovery p; public Discovery() { } public Discovery(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.Discovery)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.Discovery)p; } public long getTimeout() { return p.getTimeout(); } public void setTimeout(long timeout) { p.setTimeout(timeout); } public int getInitialMembers() { return p.getNumInitialMembers(); } public void setInitialMembers(int num_initial_members) { p.setNumInitialMembers(num_initial_members); } public int getPingRequests() { return p.getNumPingRequests(); } public void setPingRequests(int num_ping_requests) { p.setNumPingRequests(num_ping_requests); } public int getDiscoveryRequestsSent() { return p.getNumberOfDiscoveryRequestsSent(); } public Vector findInitialMembers() { return new Vector(p.findInitialMembers(null)); } public String findInitialMembersAsString() { return p.findInitialMembersAsString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCP.java0000644000175000017500000000343411366547366025524 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import java.net.UnknownHostException; /** * @author Bela Ban * @version $Id: TCP.java,v 1.4 2006/10/30 20:07:10 bstansberry Exp $ */ public class TCP extends TP implements TCPMBean { org.jgroups.protocols.TCP p; public TCP() { } public TCP(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.TCP)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.TCP)p; } public int getOpenConnections() { return p.getOpenConnections(); } public String getBindAddr() { return p.getBindAddress(); } public void setBindAddr(String bind_addr) { try { p.setBindAddress(bind_addr); } catch(UnknownHostException e) { IllegalArgumentException iae = new IllegalArgumentException("Unknown host " + bind_addr); iae.initCause(e); throw iae; } } public int getStartPort() { return p.getStartPort(); } public void setStartPort(int start_port) { p.setStartPort(start_port); } public int getEndPort() { return p.getEndPort(); } public void setEndPort(int end_port) { p.setEndPort(end_port); } public long getReaperInterval() { return p.getReaperInterval(); } public void setReaperInterval(long reaper_interval) { p.setReaperInterval(reaper_interval); } public long getConnExpireTime() { return p.getConnExpireTime(); } public void setConnExpireTime(long conn_expire_time) { p.setConnExpireTime(conn_expire_time); } public String printConnections() { return p.printConnections(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/VIEW_SYNCMBean.java0000644000175000017500000000121111366547366027376 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: VIEW_SYNCMBean.java,v 1.2.14.1 2008/03/12 09:01:42 belaban Exp $ */ public interface VIEW_SYNCMBean extends ProtocolMBean { long getAverageSendInterval(); void setAverageSendInterval(long send_interval); int getNumViewsSent(); int getNumViewRequestsSent(); int getNumViewResponsesSeen(); int getNumViewsNonLocal(); int getNumViewsEqual(); int getNumViewsLess(); long getLastViewRequestSent(); int getNumViewsAdjusted(); void sendViewRequest(); // void sendFakeViewForTestingOnly(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/SEQUENCER.java0000644000175000017500000000257611366547366026436 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; import java.util.Map; /** * @author Bela Ban * @version $Id: SEQUENCER.java,v 1.1 2006/01/03 14:43:43 belaban Exp $ */ public class SEQUENCER extends Protocol implements SEQUENCERMBean { org.jgroups.protocols.SEQUENCER p; public SEQUENCER() { } public SEQUENCER(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.SEQUENCER)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.SEQUENCER)p; } public boolean isCoord() { return p.isCoordinator(); } public String getCoordinator() { return p.getCoordinator().toString(); } public String getLocalAddress() { return p.getLocalAddress().toString(); } public long getForwarded() { return p.getForwarded(); } public long getBroadcast() { return p.getBroadcast(); } public long getReceivedForwards() { return p.getReceivedForwards(); } public long getReceivedBroadcasts() { return p.getReceivedBroadcasts(); } public void resetStats() { super.resetStats(); } public String printStats() { return super.printStats(); } public Map dumpStats() { return super.dumpStats(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FD_ALL.java0000644000175000017500000000320011366547366026046 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: FD_ALL.java,v 1.1 2006/12/22 09:34:53 belaban Exp $ */ public class FD_ALL extends Protocol implements FD_ALLMBean { org.jgroups.protocols.FD_ALL p; public FD_ALL() { } public FD_ALL(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.FD_ALL)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.FD_ALL)p; } public int getHeartbeatsSent() { return p.getHeartbeatsSent(); } public int getHeartbeatsReceived() { return p.getHeartbeatsReceived(); } public int getSuspectEventsSent() { return p.getSuspectEventsSent(); } public long getTimeout() { return p.getTimeout(); } public void setTimeout(long timeout) { p.setTimeout(timeout); } public long getInterval() { return p.getInterval(); } public void setInterval(long interval) { p.setInterval(interval); } public boolean isShun() { return p.isShun(); } public void setShun(boolean flag) { p.setShun(flag); } public boolean isRunning() { return p.isRunning(); } public String getLocalAddress() { return p.getLocalAddress(); } public String getMembers() { return p.getMembers(); } public String printSuspectHistory() { return p.printSuspectHistory(); } public String printTimestamps() { return p.printTimestamps(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCPGOSSIPMBean.java0000644000175000017500000000035711366547366027355 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: TCPGOSSIPMBean.java,v 1.1 2005/06/14 10:10:10 belaban Exp $ */ public interface TCPGOSSIPMBean extends DiscoveryMBean { } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/MPING.java0000644000175000017500000000326511366547366025752 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.List; /** * @author Bela Ban * @version $Id: MPING.java,v 1.2 2007/07/02 11:16:07 belaban Exp $ */ public class MPING extends PING implements MPINGMBean { org.jgroups.protocols.MPING mping; public MPING() { } public MPING(org.jgroups.stack.Protocol p) { super(p); this.mping=(org.jgroups.protocols.MPING)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.mping=(org.jgroups.protocols.MPING)p; } public InetAddress getBindAddr() { return mping.getBindAddr(); } public void setBindAddr(InetAddress bind_addr) { mping.setBindAddr(bind_addr); } public List getReceiveInterfaces() { return mping.getReceiveInterfaces(); } public List getSendInterfaces() { return mping.getSendInterfaces(); } public boolean isReceiveOnAllInterfaces() { return mping.isReceiveOnAllInterfaces(); } public boolean isSendOnAllInterfaces() { return mping.isSendOnAllInterfaces(); } public int getTTL() { return mping.getTTL(); } public void setTTL(int ip_ttl) { mping.setTTL(ip_ttl); } public InetAddress getMcastAddr() { return mping.getMcastAddr(); } public void setMcastAddr(InetAddress mcast_addr) { mping.setMcastAddr(mcast_addr); } public int getMcastPort() { return mping.getMcastPort(); } public void setMcastPort(int mcast_port) { mping.setMcastPort(mcast_port); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/MPINGMBean.java0000644000175000017500000000125411366547366026651 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; import java.net.InetAddress; import java.util.List; /** * @author Bela Ban * @version $Id: MPINGMBean.java,v 1.2 2007/07/02 11:16:07 belaban Exp $ */ public interface MPINGMBean extends PINGMBean { InetAddress getBindAddr(); void setBindAddr(InetAddress bind_addr); boolean isReceiveOnAllInterfaces(); List getReceiveInterfaces(); boolean isSendOnAllInterfaces(); List getSendInterfaces(); int getTTL(); void setTTL(int ip_ttl); InetAddress getMcastAddr(); void setMcastAddr(InetAddress mcast_addr); int getMcastPort(); void setMcastPort(int mcast_port); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/PING.java0000644000175000017500000000101111366547366025620 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: PING.java,v 1.2 2005/06/14 09:51:08 belaban Exp $ */ public class PING extends Discovery implements PINGMBean { public PING() { } public PING(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.PING)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.PING)p; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/UDP.java0000644000175000017500000000102011366547366025513 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.stack.Protocol; /** * @author Bela Ban * @version $Id: UDP.java,v 1.3 2005/08/17 07:32:29 belaban Exp $ */ public class UDP extends org.jgroups.jmx.protocols.TP implements UDPMBean { org.jgroups.protocols.UDP udp; public UDP() { } public UDP(Protocol p) { super(p); udp=(org.jgroups.protocols.UDP)p; } public void attachProtocol(Protocol p) { super.attachProtocol(p); udp=(org.jgroups.protocols.UDP)p; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FC.java0000644000175000017500000000437211366547366025370 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: FC.java,v 1.6 2007/05/07 09:55:37 belaban Exp $ */ public class FC extends Protocol implements FCMBean { org.jgroups.protocols.FC p; public FC() { } public FC(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.FC)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.FC)p; } public long getMaxCredits() { return p.getMaxCredits(); } public void setMaxCredits(long max_credits) { p.setMaxCredits(max_credits); } public double getMinThreshold() { return p.getMinThreshold(); } public void setMinThreshold(double min_threshold) { p.setMinThreshold(min_threshold); } public long getMinCredits() { return p.getMinCredits(); } public void setMinCredits(long min_credits) { p.setMinCredits(min_credits); } public int getBlockings() { return p.getNumberOfBlockings(); } public long getTotalTimeBlocked() { return p.getTotalTimeBlocked(); } public long getMaxBlockTime() { return p.getMaxBlockTime(); } public void setMaxBlockTime(long t) { p.setMaxBlockTime(t); } public double getAverageTimeBlocked() { return p.getAverageTimeBlocked(); } public int getCreditRequestsReceived() { return p.getNumberOfCreditRequestsReceived(); } public int getCreditRequestsSent() { return p.getNumberOfCreditRequestsSent(); } public int getCreditResponsesReceived() { return p.getNumberOfCreditResponsesReceived(); } public int getCreditResponsesSent() { return p.getNumberOfCreditResponsesSent(); } public String printSenderCredits() { return p.printSenderCredits(); } public String printReceiverCredits() { return p.printReceiverCredits(); } public String printCredits() { return p.printCredits(); } public String showLastBlockingTimes() { return p.showLastBlockingTimes(); } public void unblock() { p.unblock(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/BARRIER.java0000644000175000017500000000144611366547366026165 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * JMX wrapper for BARRIER protocol. * * @author rpike */ public class BARRIER extends Protocol implements BARRIERMBean { private org.jgroups.protocols.BARRIER p; public BARRIER() { } public BARRIER(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.BARRIER)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.BARRIER)p; } public int getInFlightThreadsCount() { return p.getNumberOfInFlightThreads(); } public long getMaxCloseTime() { return p.getMaxCloseTime(); } public boolean isClosed() { return p.isClosed(); } public boolean isOpenerScheduled() { return p.isOpenerScheduled(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/SEQUENCERMBean.java0000644000175000017500000000066511366547366027336 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: SEQUENCERMBean.java,v 1.1 2006/01/03 14:43:43 belaban Exp $ */ public interface SEQUENCERMBean extends ProtocolMBean { boolean isCoord(); String getCoordinator(); String getLocalAddress(); long getForwarded(); long getBroadcast(); long getReceivedForwards(); long getReceivedBroadcasts(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCPPING.java0000644000175000017500000000063211366547366026177 0ustar twernertwernerpackage org.jgroups.jmx.protocols; /** * @author Bela Ban * @version $Id: TCPPING.java,v 1.2 2006/12/14 07:46:59 belaban Exp $ */ public class TCPPING extends Discovery implements TCPPINGMBean { public TCPPING() { } public TCPPING(org.jgroups.stack.Protocol p) { super(p); } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FD_SOCKMBean.java0000644000175000017500000000076511366547366027115 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; import java.util.Enumeration; import java.util.Date; /** * @author Bela Ban * @version $Id: FD_SOCKMBean.java,v 1.2 2007/08/30 10:41:41 belaban Exp $ */ public interface FD_SOCKMBean extends ProtocolMBean { String getLocalAddress(); String getMembers(); String getPingableMembers(); String getPingDest(); int getNumSuspectEventsGenerated(); String printSuspectHistory(); String printCache(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/VIEW_SYNC.java0000644000175000017500000000307411366547366026504 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: VIEW_SYNC.java,v 1.2.14.1 2008/03/12 09:01:42 belaban Exp $ */ public class VIEW_SYNC extends Protocol implements VIEW_SYNCMBean { org.jgroups.protocols.VIEW_SYNC p; public VIEW_SYNC() { } public VIEW_SYNC(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.VIEW_SYNC)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.VIEW_SYNC)p; } public long getAverageSendInterval() { return p.getAverageSendInterval(); } public void setAverageSendInterval(long send_interval) { p.setAverageSendInterval(send_interval); } public int getNumViewsSent() { return p.getNumViewsSent(); } public int getNumViewsAdjusted() { return p.getNumViewsAdjusted(); } public long getLastViewRequestSent() { return p.getLastViewRequestSent(); } public int getNumViewRequestsSent() { return p.getNumViewRequestsSent(); } public int getNumViewResponsesSeen() { return p.getNumViewRequestsSent(); } public int getNumViewsLess() { return p.getNumViewsLess(); } public int getNumViewsEqual() { return p.getNumViewsEqual(); } public int getNumViewsNonLocal() { return p.getNumViewsNonLocal(); } public void sendViewRequest() { p.sendViewRequest(); } // public void sendFakeViewForTestingOnly() { // p.sendFakeViewForTestingOnly(); // } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/SFCMBean.java0000644000175000017500000000126511366547366026414 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: SFCMBean.java,v 1.2 2007/01/09 10:19:22 belaban Exp $ */ public interface SFCMBean extends ProtocolMBean { long getMaxCredits(); long getCredits(); long getBytesSent(); long getBlockings(); long getCreditRequestsReceived(); long getCreditRequestsSent(); long getReplenishmentsReceived(); long getReplenishmentsSent(); long getTotalBlockingTime(); double getAverageBlockingTime(); String printBlockingTimes(); String printReceived(); String printPendingCreditors(); String printPendingRequesters(); void unblock(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCPGOSSIP.java0000644000175000017500000000070611366547366026450 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: TCPGOSSIP.java,v 1.1 2005/06/14 10:10:10 belaban Exp $ */ public class TCPGOSSIP extends Discovery implements TCPGOSSIPMBean { public TCPGOSSIP() { } public TCPGOSSIP(org.jgroups.stack.Protocol p) { super(p); } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCP_NIOMBean.java0000644000175000017500000000064411366547366027134 0ustar twernertwernerpackage org.jgroups.jmx.protocols; /** * @author Scott Marlow * @version $Id: TCP_NIOMBean.java,v 1.2 2007/01/10 19:38:18 smarlownovell Exp $ */ public interface TCP_NIOMBean extends TCPMBean { int getReaderThreads(); int getWriterThreads(); int getProcessorThreads(); int getProcessorMinThreads(); int getProcessorMaxThreads(); int getProcessorQueueSize(); long getProcessorKeepAliveTime(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/TCP_NIO.java0000644000175000017500000000214311366547366026225 0ustar twernertwernerpackage org.jgroups.jmx.protocols; /** * @author Scott Marlow * @version $Id: TCP_NIO.java,v 1.3 2007/07/10 13:02:50 belaban Exp $ */ public class TCP_NIO extends TCP implements TCP_NIOMBean { org.jgroups.protocols.TCP_NIO my_p; public TCP_NIO() { } public TCP_NIO(org.jgroups.stack.Protocol p) { super(p); this.my_p=(org.jgroups.protocols.TCP_NIO)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { this.my_p=(org.jgroups.protocols.TCP_NIO)p; } public int getReaderThreads() { return my_p.getReaderThreads(); } public int getWriterThreads() { return my_p.getWriterThreads(); } public int getProcessorThreads() { return my_p.getProcessorThreads(); } public int getProcessorMinThreads() { return my_p.getProcessorMinThreads(); } public int getProcessorMaxThreads() { return my_p.getProcessorMaxThreads(); } public int getProcessorQueueSize() { return my_p.getProcessorQueueSize(); } public long getProcessorKeepAliveTime() { return my_p.getProcessorKeepAliveTime(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FRAG2.java0000644000175000017500000000233211366547366025673 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.Protocol; /** * @author Bela Ban * @version $Id: FRAG2.java,v 1.1 2005/06/13 14:53:48 belaban Exp $ */ public class FRAG2 extends Protocol implements FRAG2MBean { org.jgroups.protocols.FRAG2 p; public FRAG2() { } public FRAG2(org.jgroups.stack.Protocol p) { super(p); this.p=(org.jgroups.protocols.FRAG2)p; } public void attachProtocol(org.jgroups.stack.Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.FRAG2)p; } public int getFragSize() { return p.getFragSize(); } public void setFragSize(int s) { p.setFragSize(s); } public int getOverhead() { return p.getOverhead(); } public void setOverhead(int o) { p.setOverhead(o); } public long getNumberOfSentMessages() { return p.getNumberOfSentMessages(); } public long getNumberOfSentFragments() { return p.getNumberOfSentFragments(); } public long getNumberOfReceivedMessages() { return p.getNumberOfReceivedMessages(); } public long getNumberOfReceivedFragments() { return p.getNumberOfReceivedFragments(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/FRAG2MBean.java0000644000175000017500000000075011366547366026600 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * @author Bela Ban * @version $Id: FRAG2MBean.java,v 1.1 2005/06/13 14:53:48 belaban Exp $ */ public interface FRAG2MBean extends ProtocolMBean { int getFragSize(); void setFragSize(int s); int getOverhead(); void setOverhead(int o); long getNumberOfSentMessages(); long getNumberOfSentFragments(); long getNumberOfReceivedMessages(); long getNumberOfReceivedFragments(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/STATS.java0000644000175000017500000000324711366547366025776 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.stack.Protocol; import org.jgroups.protocols.*; /** * @author Bela Ban * @version $Id: STATS.java,v 1.1 2005/06/07 10:17:26 belaban Exp $ */ public class STATS extends org.jgroups.jmx.Protocol implements STATSMBean { org.jgroups.protocols.STATS p; public STATS() { } public STATS(Protocol p) { super(p); this.p=(org.jgroups.protocols.STATS)p; } public void attachProtocol(Protocol p) { super.attachProtocol(p); this.p=(org.jgroups.protocols.STATS)p; } public long getSentMessages() { return p.getSentMessages(); } public long getSentBytes() { return p.getSentBytes(); } public long getSentUnicastMessages() { return p.getSentUnicastMessages(); } public long getSentUnicastBytes() { return p.getSentUnicastBytes(); } public long getSentMcastMessages() { return p.getSentMcastMessages(); } public long getSentMcastBytes() { return p.getSentMcastBytes(); } public long getReceivedMessages() { return p.getReceivedMessages(); } public long getReceivedBytes() { return p.getReceivedBytes(); } public long getReceivedUnicastMessages() { return p.getReceivedUnicastMessages(); } public long getReceivedUnicastBytes() { return p.getReceivedUnicastBytes(); } public long getReceivedMcastMessages() { return p.getReceivedMcastMessages(); } public long getReceivedMcastBytes() { return p.getReceivedMcastBytes(); } public String printStats() { return p.printStats(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/protocols/BARRIERMBean.java0000644000175000017500000000127311366547366027066 0ustar twernertwernerpackage org.jgroups.jmx.protocols; import org.jgroups.jmx.ProtocolMBean; /** * JMX interface for BARRIER protocol. * * @author rpike */ public interface BARRIERMBean extends ProtocolMBean { /** Indicates if barrier is currently closed. */ public boolean isClosed(); /** Gets configured max_close_time value (ms). */ public long getMaxCloseTime(); /** Returns true if barrier_opener_future is non-null. */ public boolean isOpenerScheduled(); /** * Returns the current count of in-flight threads. *

        In-flight threads are those currently processing in higher-level protocols. * * @return in-flight threads count */ public int getInFlightThreadsCount(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/JChannelMBean.java0000644000175000017500000000546411366547366025444 0ustar twernertwernerpackage org.jgroups.jmx; import org.jgroups.*; import org.w3c.dom.Element; import java.io.Serializable; import java.util.Map; /** * @author Bela Ban * @version $Id: JChannelMBean.java,v 1.11 2007/02/16 07:32:10 belaban Exp $ */ public interface JChannelMBean { void create() throws Exception; void start() throws Exception; void stop(); void destroy(); //void jbossInternalLifecycle(String method) throws Exception; org.jgroups.JChannel getChannel(); String getProperties(); void setProperties(String props); String getVersion(); String getObjectName(); void setObjectName(String name); int getNumberOfTasksInTimer(); String dumpTimerQueue(); int getTimerThreads(); /** To configure via XML file */ void setClusterConfig(Element el); String getGroupName(); void setGroupName(String group_name); String getClusterName(); void setClusterName(String cluster_name); boolean getReceiveBlockEvents(); void setReceiveBlockEvents(boolean flag); boolean getReceiveLocalMessages(); void setReceiveLocalMessages(boolean flag); boolean getAutoReconnect(); void setAutoReconnect(boolean flag); boolean getAutoGetState(); void setAutoGetState(boolean flag); Map dumpStats(); View getView(); String getViewAsString(); Address getLocalAddress(); String getLocalAddressAsString(); void setChannelListener(ChannelListener channel_listener); boolean getStatsEnabled(); void setStatsEnabled(boolean flag); void resetStats(); long getSentMessages(); long getSentBytes(); long getReceivedMessages(); long getReceivedBytes(); boolean isOpen(); boolean isConnected(); int getNumMessages(); String dumpQueue(); String printProtocolSpec(boolean include_properties); String toString(boolean print_details); void connect(String channel_name) throws ChannelException, ChannelClosedException; void disconnect(); void close(); void shutdown(); void send(Message msg) throws ChannelNotConnectedException, ChannelClosedException; void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException; void sendToAll(String msg) throws ChannelNotConnectedException, ChannelClosedException; /** @param evt * @deprecated */ void down(Event evt); Object receive(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; Object peek(long timeout) throws ChannelNotConnectedException, ChannelClosedException, TimeoutException; void blockOk(); boolean getState(Address target, long timeout) throws ChannelNotConnectedException, ChannelClosedException; void returnState(byte[] state); void returnState(byte[] state, String state_id); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/JChannelFactory.java0000644000175000017500000000671311366547366026067 0ustar twernertwernerpackage org.jgroups.jmx; import org.jgroups.Channel; import javax.management.MBeanRegistration; import javax.management.ObjectName; import javax.management.MBeanServer; /** * @author Bela Ban * @version $Id: JChannelFactory.java,v 1.7.2.1 2007/11/28 11:39:58 belaban Exp $ */ public class JChannelFactory implements JChannelFactoryMBean, MBeanRegistration { org.jgroups.JChannelFactory factory=new org.jgroups.JChannelFactory(); private MBeanServer server=null; public JChannelFactory(org.jgroups.JChannelFactory factory) { this.factory=factory; } public JChannelFactory() { } public void setMultiplexerConfig(String properties) throws Exception { factory.setMultiplexerConfig(properties); } public void setMultiplexerConfig(String properties, boolean replace) throws Exception { factory.setMultiplexerConfig(properties, replace); } public String getMultiplexerConfig() { return factory.getMultiplexerConfig(); } public String getConfig(String stack_name) throws Exception { return factory.getConfig(stack_name); } public boolean removeConfig(String stack_name) { return factory.removeConfig(stack_name); } public void clearConfigurations() { factory.clearConfigurations(); } public String getDomain() { return factory.getDomain(); } public void setDomain(String name) { factory.setDomain(name); } public boolean isExposeChannels() { return factory.isExposeChannels(); } public void setExposeChannels(boolean flag) { factory.setExposeChannels(flag); } public boolean isExposeProtocols() { return factory.isExposeProtocols(); } public void setExposeProtocols(boolean f) { factory.setExposeProtocols(f); } public Channel createMultiplexerChannel(String stack_name, String id) throws Exception { return factory.createMultiplexerChannel(stack_name, id); } public Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception { return factory.createMultiplexerChannel(stack_name, id, register_for_state_transfer, substate_id); } public void create() throws Exception { if(factory == null) factory=new org.jgroups.JChannelFactory(); factory.setServer(server); factory.create(); } public void start() throws Exception { factory.start(); } public void stop() { factory.stop(); } public void destroy() { factory.destroy(); factory=null; } public String dumpConfiguration() { return factory.dumpConfiguration(); } public String dumpChannels() { return factory.dumpChannels(); } public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { this.server=server; if(factory != null) factory.setServer(server); if (name != null) {//we only create a name if it is empty return name; } //if the objectName is not a pattern then at least one key property MUST exist String objectName = getDomain() + " : service=JChannelFactory"; return ObjectName.getInstance(objectName); } public void postRegister(Boolean registrationDone) { } public void preDeregister() throws Exception { } public void postDeregister() { } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/jmx/ProtocolMBean.java0000644000175000017500000000106311366547366025552 0ustar twernertwernerpackage org.jgroups.jmx; import java.util.Map; import java.util.Properties; /** * @author Bela Ban * @version $Id: ProtocolMBean.java,v 1.10 2007/04/27 07:59:24 belaban Exp $ */ public interface ProtocolMBean { String getName(); String getPropertiesAsString(); void setProperties(Properties p); boolean getStatsEnabled(); void setStatsEnabled(boolean flag); void resetStats(); String printStats(); Map dumpStats(); void create() throws Exception; void start() throws Exception; void stop(); void destroy(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Event.java0000644000175000017500000002100111366547366023323 0ustar twernertwerner// $Id: Event.java,v 1.54.2.4 2009/09/08 12:24:03 belaban Exp $ package org.jgroups; /** * Used for inter-stack and intra-stack communication. * @author Bela Ban */ public class Event { public static final int MSG = 1; // arg = Message public static final int CONNECT = 2; // arg = cluster name (string) public static final int DISCONNECT = 4; // arg = member address (Address) public static final int VIEW_CHANGE = 6; // arg = View (or MergeView in case of merge) public static final int SET_LOCAL_ADDRESS = 8; // arg = Address public static final int SUSPECT = 9; // arg = Address of suspected member public static final int BLOCK = 10; // arg = null (used by FLUSH) public static final int FIND_INITIAL_MBRS = 12; // arg = JoinPromise (or null (merge2)) public static final int MERGE = 14; // arg = Vector of Objects public static final int TMP_VIEW = 15; // arg = View public static final int BECOME_SERVER = 16; // sent when client has joined group public static final int GET_APPLSTATE = 17; // get state from appl (arg=StateTransferInfo) public static final int GET_STATE = 19; // arg = StateTransferInfo public static final int GET_STATE_OK = 20; // arg = StateTransferInfo public static final int STATE_RECEIVED = 21; // arg = StateTransferInfo (with state and state_id) public static final int STABLE = 30; // arg = long[] (stable seqnos for mbrs) public static final int GET_DIGEST = 39; // public static final int SET_DIGEST = 41; // arg = Digest public static final int EXIT = 46; // received when member was forced out of the group public static final int PERF = 47; // for performance measurements public static final int HEARD_FROM = 50; // arg = Vector (list of Addresses) public static final int UNSUSPECT = 51; // arg = Address (of unsuspected member) public static final int MERGE_DIGEST = 53; // arg = Digest public static final int CONFIG = 56; // arg = Map (config properties) public static final int SUSPEND_STABLE = 65; // arg = Long (max_suspend_time) public static final int RESUME_STABLE = 66; // arg = null public static final int SUSPEND = 68; // arg = HashMap (used by FLUSH) public static final int RESUME = 70; // arg = null (used by FLUSH) public static final int STATE_TRANSFER_INPUTSTREAM = 71; // arg=java.io.InputStream subclass public static final int STATE_TRANSFER_OUTPUTSTREAM = 72; // arg=java.io.OutputStream subclass public static final int STATE_TRANSFER_INPUTSTREAM_CLOSED = 73; //arg=null public static final int STATE_TRANSFER_OUTPUTSTREAM_CLOSED = 74; //arg=null public static final int UNBLOCK = 75; //arg=null (indicate end of flush round) public static final int CLOSE_BARRIER = 76; // arg = null public static final int OPEN_BARRIER = 77; // arg = null public static final int REBROADCAST = 78; // arg = Digest public static final int SHUTDOWN = 79; // arg = null (shutdown without closing sockets or cleaning up) public static final int CONNECT_WITH_STATE_TRANSFER = 80; // arg = cluster name (string) public static final int START_PARTITION = 82; // arg = null; public static final int STOP_PARTITION = 83; // arg = null; public static final int INFO = 84; // arg = Map public static final int PREPARE_VIEW = 86; // arg = View public static final int USER_DEFINED = 1000; // arg = private final int type; // type of event private final Object arg; // must be serializable if used for inter-stack communication public Event(int type) { this.type=type; this.arg=null; } public Event(int type, Object arg) { this.type=type; this.arg=arg; } public final int getType() { return type; } /** * Sets the new type * @param type * @deprecated in order to make an Event immutable */ public void setType(int type) { throw new IllegalAccessError("setType() has been deprecated, to make Events immutable"); } public Object getArg() { return arg; } public void setArg(Object arg) { throw new IllegalAccessError("setArg() has been deprecated, to make Events immutable"); } public static String type2String(int t) { switch(t) { case MSG: return "MSG"; case CONNECT: return "CONNECT"; case DISCONNECT: return "DISCONNECT"; case VIEW_CHANGE: return "VIEW_CHANGE"; case SET_LOCAL_ADDRESS: return "SET_LOCAL_ADDRESS"; case SUSPECT: return "SUSPECT"; case BLOCK: return "BLOCK"; case FIND_INITIAL_MBRS: return "FIND_INITIAL_MBRS"; case TMP_VIEW: return "TMP_VIEW"; case BECOME_SERVER: return "BECOME_SERVER"; case GET_APPLSTATE: return "GET_APPLSTATE"; case GET_STATE: return "GET_STATE"; case GET_STATE_OK: return "GET_STATE_OK"; case STATE_RECEIVED: return "STATE_RECEIVED"; case STABLE: return "STABLE"; case GET_DIGEST: return "GET_DIGEST"; case SET_DIGEST: return "SET_DIGEST"; case MERGE: return "MERGE"; // Added by gianlucac@tin.it to support partitions merging in GMS case EXIT: return "EXIT"; case PERF: return "PERF"; case HEARD_FROM: return "HEARD_FROM"; case UNSUSPECT: return "UNSUSPECT"; case MERGE_DIGEST: return "MERGE_DIGEST"; case CONFIG: return "CONFIG"; case SUSPEND_STABLE: return "SUSPEND_STABLE"; case RESUME_STABLE: return "RESUME_STABLE"; case SUSPEND: return "SUSPEND"; case RESUME: return "RESUME"; case STATE_TRANSFER_INPUTSTREAM: return "STATE_TRANSFER_INPUTSTREAM"; case STATE_TRANSFER_OUTPUTSTREAM:return "STATE_TRANSFER_OUTPUTSTREAM"; case STATE_TRANSFER_INPUTSTREAM_CLOSED: return "STATE_TRANSFER_INPUTSTREAM_CLOSED"; case STATE_TRANSFER_OUTPUTSTREAM_CLOSED: return "STATE_TRANSFER_OUTPUTSTREAM_CLOSED"; case UNBLOCK: return "UNBLOCK"; case CLOSE_BARRIER: return "CLOSE_BARRIER"; case OPEN_BARRIER: return "OPEN_BARRIER"; case REBROADCAST: return "REBROADCAST"; case SHUTDOWN: return "SHUTDOWN"; case CONNECT_WITH_STATE_TRANSFER: return "CONNECT_WITH_STATE_TRANSFER"; case START_PARTITION: return "START_PARTITION"; case STOP_PARTITION: return "STOP_PARTITION"; case INFO: return "INFO"; case PREPARE_VIEW: return "PREPARE_VIEW"; case USER_DEFINED: return "USER_DEFINED"; default: return "UNDEFINED(" + t + ")"; } } public static final Event GET_DIGEST_EVT = new Event(Event.GET_DIGEST); public String toString() { StringBuilder ret=new StringBuilder(64); ret.append("Event[type=" + type2String(type) + ", arg=" + arg + ']'); if(type == MSG) ret.append(" (headers=").append(((Message)arg).printHeaders()).append(")"); return ret.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/MergeView.java0000644000175000017500000001042711366547366024146 0ustar twernertwerner// $Id: MergeView.java,v 1.8 2007/03/12 10:51:46 belaban Exp $ package org.jgroups; import java.io.*; import java.util.Vector; /** * A view that is sent as a result of a merge. * Whenever a group splits into subgroups, e.g., due to a network partition, * and later the subgroups merge back together, a MergeView instead of a View * will be received by the application. The MergeView class is a subclass of * View and contains as additional instance variable: the list of views that * were merged. For example, if the group denoted by view V1:(p,q,r,s,t) * splits into subgroups V2:(p,q,r) and V2:(s,t), the merged view might be * V3:(p,q,r,s,t). In this case the MergeView would contain a list of 2 views: * V2:(p,q,r) and V2:(s,t). */ public class MergeView extends View { protected Vector subgroups=null; // subgroups that merged into this single view (a list of Views) /** * Used by externalization */ public MergeView() { } /** * Creates a new view * * @param vid The view id of this view (can not be null) * @param members Contains a list of all the members in the view, can be empty but not null. * @param subgroups A list of Views representing the former subgroups */ public MergeView(ViewId vid, Vector

        members, Vector subgroups) { super(vid, members); this.subgroups=subgroups; } /** * Creates a new view * * @param creator The creator of this view (can not be null) * @param id The lamport timestamp of this view * @param members Contains a list of all the members in the view, can be empty but not null. * @param subgroups A list of Views representing the former subgroups */ public MergeView(Address creator, long id, Vector
        members, Vector subgroups) { super(creator, id, members); this.subgroups=subgroups; } public Vector getSubgroups() { return subgroups; } /** * creates a copy of this view * * @return a copy of this view */ public Object clone() { ViewId vid2=vid != null ? (ViewId)vid.clone() : null; Vector
        members2=members != null ? (Vector
        )members.clone() : null; Vector subgroups2=subgroups != null ? (Vector)subgroups.clone() : null; return new MergeView(vid2, members2, subgroups2); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append("MergeView::").append(super.toString()).append(", subgroups=").append(subgroups); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(subgroups); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); subgroups=(Vector)in.readObject(); } public void writeTo(DataOutputStream out) throws IOException { super.writeTo(out); // write subgroups int len=subgroups != null? subgroups.size() : 0; out.writeShort(len); if(len == 0) return; for(View v: subgroups) { if(v instanceof MergeView) out.writeBoolean(true); else out.writeBoolean(false); v.writeTo(out); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { super.readFrom(in); short len=in.readShort(); if(len > 0) { View v; subgroups=new Vector(); for(int i=0; i < len; i++) { boolean is_merge_view=in.readBoolean(); v=is_merge_view? new MergeView() : new View(); v.readFrom(in); subgroups.add(v); } } } public int serializedSize() { int retval=super.serializedSize(); retval+=Global.SHORT_SIZE; // for size of subgroups vector if(subgroups == null) return retval; for(View v: subgroups) { retval+=Global.BYTE_SIZE; // boolean for View or MergeView retval+=v.serializedSize(); } return retval; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/Membership.java0000644000175000017500000001552711366547366024355 0ustar twernertwerner// $Id: Membership.java,v 1.10.4.1 2008/01/10 06:56:48 vlada Exp $ package org.jgroups; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; /** * Class to keep track of Addresses. * The membership object holds a vector of Address objects that are in the same membership. * Each unique address can only exist once; i.e., doing Membership.add(existing_address) * will be ignored. */ public class Membership implements Cloneable { /* private vector to hold all the addresses */ private final List
        members=new LinkedList
        (); protected static final Log log=LogFactory.getLog(Membership.class); /** * Public constructor * Creates a member ship object with zero members */ public Membership() { } /** * Creates a member ship object with the initial members. * The Address references are copied out of the vector, so that the * vector passed in as parameters is not the same reference as the vector * that the membership class is using * * @param initial_members - a list of members that belong to this membership */ public Membership(Collection
        initial_members) { if(initial_members != null) add(initial_members); } /** * returns a copy (clone) of the members in this membership. * the vector returned is immutable in reference to this object. * ie, modifying the vector that is being returned in this method * will not modify this membership object. * * @return a list of members, */ public Vector
        getMembers() { /*clone so that this objects members can not be manipulated from the outside*/ synchronized(members) { return new Vector
        (members); } } /** * Adds a new member to this membership. * If the member already exist (Address.equals(Object) returns true then the member will * not be added to the membership */ public void add(Address new_member) { synchronized(members) { if(new_member != null && !members.contains(new_member)) { members.add(new_member); } } } /** * Adds a list of members to this membership * * @param v - a vector containing Address objects * @throws ClassCastException if v contains objects that don't implement the Address interface * @see #add */ public final void add(Collection
        v) { if(v != null) { for(Iterator
        it=v.iterator(); it.hasNext();) { Address addr=it.next(); add(addr); } } } /** * removes an member from the membership. * If this member doesn't exist, no action will be performed on the existing membership * * @param old_member - the member to be removed */ public void remove(Address old_member) { if(old_member != null) { synchronized(members) { members.remove(old_member); } } } /** * removes all the members contained in v from this membership * * @param v - a vector containing all the members to be removed */ public void remove(Collection
        v) { if(v != null) { synchronized(members) { members.removeAll(v); } } } /** * removes all the members from this membership */ public void clear() { synchronized(members) { members.clear(); } } /** * Clear the membership and adds all members of v * This method will clear out all the old members of this membership by * invoking the Clear method. * Then it will add all the all members provided in the vector v * * @param v - a vector containing all the members this membership will contain */ public void set(Collection
        v) { clear(); if(v != null) { add(v); } } /** * Clear the membership and adds all members of v * This method will clear out all the old members of this membership by * invoking the Clear method. * Then it will add all the all members provided in the vector v * * @param m - a membership containing all the members this membership will contain */ public void set(Membership m) { clear(); if(m != null) { add(m.getMembers()); } } /** * merges membership with the new members and removes suspects * The Merge method will remove all the suspects and add in the new members. * It will do it in the order * 1. Remove suspects * 2. Add new members * the order is very important to notice. * * @param new_mems - a vector containing a list of members (Address) to be added to this membership * @param suspects - a vector containing a list of members (Address) to be removed from this membership */ public void merge(Collection
        new_mems, Collection
        suspects) { remove(suspects); add(new_mems); } /** * Returns true if the provided member belongs to this membership * * @param member * @return true if the member belongs to this membership */ public boolean contains(Address member) { if(member == null) return false; synchronized(members) { return members.contains(member); } } /* Simple inefficient bubble sort, but not used very often (only when merging) */ public void sort() { synchronized(members) { Collections.sort(members); } } /** * returns a copy of this membership * * @return an exact copy of this membership */ public Membership copy() { return ((Membership)clone()); } /** * @return a clone of this object. The list of members is copied to a new * container */ public Object clone() { return new Membership(this.members); } /** * Returns the number of addresses in this membership * * @return the number of addresses in this membership */ public int size() { synchronized(members) { return members.size(); } } /** * Returns the component at the specified index * * @param index - 0..size()-1 * @throws ArrayIndexOutOfBoundsException - if the index is negative or not less than the current size of this Membership object. * @see java.util.Vector#elementAt */ public Address elementAt(int index) { synchronized(members) { return members.get(index); } } public String toString() { synchronized(members) { return members.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/JChannelFactory.java0000644000175000017500000004716111366547366025273 0ustar twernertwerner// $Id: JChannelFactory.java,v 1.49.2.2 2007/11/28 11:39:56 belaban Exp $ package org.jgroups; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.conf.ConfiguratorFactory; import org.jgroups.conf.ProtocolStackConfigurator; import org.jgroups.conf.XmlConfigurator; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.mux.Multiplexer; import org.jgroups.mux.MuxChannel; import org.jgroups.util.Util; import org.w3c.dom.*; import javax.management.MBeanServer; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.*; /** * JChannelFactory creates pure Java implementations of the Channel * interface. * See {@link JChannel} for a discussion of channel properties. */ public class JChannelFactory implements ChannelFactory { private ProtocolStackConfigurator configurator; private Log log=LogFactory.getLog(getClass()); /** * Map. Hashmap which maps stack names to JGroups * configurations. Keys are stack names, values are plain JGroups stack * configs. This is (re-)populated whenever a setMultiplexerConfig() method * is called */ private final Map stacks = Collections.synchronizedMap(new HashMap()); /** * Map, maintains mapping between stack names (e.g. "udp") and Multiplexer(es) * */ private final Map channels = Collections.synchronizedMap(new HashMap()); /** * The MBeanServer to expose JMX management data with (no management data * will be available if null) */ private MBeanServer server = null; /** To expose the channels and protocols */ private String domain = "jgroups"; /** Whether or not to expose channels via JMX */ private boolean expose_channels=true; /** Whether to expose the factory only, or all protocols as well */ private boolean expose_protocols=true; private final static String PROTOCOL_STACKS="protocol_stacks"; private final static String STACK="stack"; private static final String NAME="name"; private static final String CONFIG="config"; /** * Constructs a JChannelFactory instance that contains no * protocol stack configuration. */ public JChannelFactory() { } /** * Constructs a JChannelFactory instance that utilizes the * specified file for protocl stack configuration. * * @param properties a file containing a JGroups XML protocol stack * configuration. * * @throws ChannelException if problems occur during the interpretation of * the protocol stack configuration. */ public JChannelFactory(File properties) throws ChannelException { configurator=ConfiguratorFactory.getStackConfigurator(properties); } /** * Constructs a JChannelFactory instance that utilizes the * specified file for protocl stack configuration. * * @param properties a XML element containing a JGroups XML protocol stack * configuration. * * @throws ChannelException if problems occur during the interpretation of * the protocol stack configuration. */ public JChannelFactory(Element properties) throws ChannelException { configurator=ConfiguratorFactory.getStackConfigurator(properties); } /** * Constructs a JChannelFactory instance that utilizes the * specified file for protocl stack configuration. * * @param properties a URL pointing to a JGroups XML protocol stack * configuration. * * @throws ChannelException if problems occur during the interpretation of * the protocol stack configuration. */ public JChannelFactory(URL properties) throws ChannelException { configurator=ConfiguratorFactory.getStackConfigurator(properties); } /** * Constructs a JChannel instance with the protocol stack * configuration based upon the specified properties parameter. * * @param properties an old style property string, a string representing a * system resource containing a JGroups XML configuration, * a string representing a URL pointing to a JGroups XML * XML configuration, or a string representing a file name * that contains a JGroups XML configuration. * * @throws ChannelException if problems occur during the interpretation of * the protocol stack configuration. */ public JChannelFactory(String properties) throws ChannelException { configurator=ConfiguratorFactory.getStackConfigurator(properties); } public void setMultiplexerConfig(Object properties) throws Exception { setMultiplexerConfig(properties, true); } public void setMultiplexerConfig(Object properties, boolean replace) throws Exception { InputStream input=ConfiguratorFactory.getConfigStream(properties); if(input == null) throw new FileNotFoundException(properties.toString()); try { parse(input, replace); } catch(Exception ex) { throw new Exception("failed parsing " + properties, ex); } finally { Util.close(input); } } public void setMultiplexerConfig(File file) throws Exception { setMultiplexerConfig(file, true); } public void setMultiplexerConfig(File file, boolean replace) throws Exception { InputStream input=ConfiguratorFactory.getConfigStream(file); if(input == null) throw new FileNotFoundException(file.toString()); try { parse(input, replace); } catch(Exception ex) { throw new Exception("failed parsing " + file.toString(), ex); } finally { Util.close(input); } } public void setMultiplexerConfig(Element properties) throws Exception { parse(properties, true); } public void setMultiplexerConfig(Element properties, boolean replace) throws Exception { parse(properties, replace); } public void setMultiplexerConfig(URL url) throws Exception { setMultiplexerConfig(url, true); } public void setMultiplexerConfig(URL url, boolean replace) throws Exception { InputStream input=ConfiguratorFactory.getConfigStream(url); if(input == null) throw new FileNotFoundException(url.toString()); try { parse(input, replace); } catch(Exception ex) { throw new Exception("failed parsing " + url.toString(), ex); } finally { Util.close(input); } } public void setMultiplexerConfig(String properties) throws Exception { setMultiplexerConfig(properties, true); } public void setMultiplexerConfig(String properties, boolean replace) throws Exception { InputStream input=ConfiguratorFactory.getConfigStream(properties); if(input == null) throw new FileNotFoundException(properties); try { parse(input, replace); } catch(Exception ex) { throw new Exception("failed parsing " + properties, ex); } finally { Util.close(input); } } /** * Returns the stack configuration as a string (to be fed into new JChannel()). Throws an exception * if the stack_name is not found. One of the setMultiplexerConfig() methods had to be called beforehand * @return The protocol stack config as a plain string */ public String getConfig(String stack_name) throws Exception { String cfg=stacks.get(stack_name); if(cfg == null) throw new Exception("stack \"" + stack_name + "\" not found in " + stacks.keySet()); return cfg; } /** * @return Returns all configurations */ public String getMultiplexerConfig() { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: stacks.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } /** Removes all configurations */ public void clearConfigurations() { stacks.clear(); } public boolean removeConfig(String stack_name) { return stack_name != null && stacks.remove(stack_name) != null; } public MBeanServer getServer() { return server; } public void setServer(MBeanServer server) { this.server=server; } public String getDomain() { return domain; } public void setDomain(String domain) { this.domain=domain; } public boolean isExposeChannels() { return expose_channels; } public void setExposeChannels(boolean expose_channels) { this.expose_channels=expose_channels; } public boolean isExposeProtocols() { return expose_protocols; } public void setExposeProtocols(boolean expose_protocols) { this.expose_protocols=expose_protocols; if (expose_protocols) this.expose_channels=true; } /** * Creates a JChannel implementation of the * Channel interface. * * @param properties the protocol stack configuration information; a * null value means use the default protocol * stack configuration. * * @throws ChannelException if the creation of the channel failed. * * @deprecated JChannel's conversion to type-specific * construction, and the subsequent deprecation of its * JChannel(Object) constructor, necessitate the * deprecation of this factory method as well. Type-specific * protocol stack configuration should be specfied during * construction of an instance of this factory. */ public Channel createChannel(Object properties) throws ChannelException { return new JChannel(properties); } /** * Creates a JChannel implementation of the * Channel interface using the protocol stack configuration * information specfied during construction of an instance of this factory. * * @throws ChannelException if the creation of the channel failed. */ public Channel createChannel() throws ChannelException { return new JChannel(configurator); } public Channel createChannel(String stack_name) throws Exception { String props=stack_name != null? getConfig(stack_name) : null; return new JChannel(props); } public Channel createMultiplexerChannel(String stack_name, String id) throws Exception { return createMultiplexerChannel(stack_name, id, false, null); } public Channel createMultiplexerChannel(final String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception { if(stack_name == null || id == null) throw new IllegalArgumentException("stack name and service ID have to be non null"); if(stack_name.length()==0 || id.length() == 0) throw new IllegalArgumentException("stack name and service ID have to non empty strings"); Multiplexer mux = null; synchronized (channels) { if (!channels.containsKey(stack_name)) { JChannel ch = new JChannel(getConfig(stack_name)); registerChannel(ch, stack_name); mux = new Multiplexer(ch); channels.put(stack_name, mux); } else { mux = channels.get(stack_name); } } if(register_for_state_transfer) mux.registerForStateTransfer(id, substate_id); Channel c = mux.createMuxChannel(id, stack_name); c.addChannelListener(new MuxFactoryChannelListener()); return c; } /** * Returns true if this factory has already registered MuxChannel with given * stack_name and an id, false otherwise. * * @param stack_name * name of the stack used * @param id * service id * @return true if such MuxChannel exists, false otherwise */ public boolean hasMuxChannel(String stack_name, String id) { Multiplexer entry = channels.get(stack_name); if (entry != null) { Set services = entry.getServiceIds(); return (services != null && services.contains(id)); } return false; } public void create() throws Exception{ if(expose_channels) { if(server == null) server=Util.getMBeanServer(); if(server == null) throw new Exception("No MBeanServer found; JChannelFactory needs to be run with an MBeanServer present, " + "e.g. inside JBoss or JDK 5, or with ExposeChannel set to false"); if(domain == null) domain="jgroups"; } } public void start() throws Exception { } public void stop() { } public void destroy() { synchronized (channels) { for(Map.Entry entry: channels.entrySet()){ Multiplexer m = entry.getValue(); if(m != null){ m.closeAll(); m.close(); } } } unregister(domain + ":*"); channels.clear(); } public String dumpConfiguration() { return stacks.keySet().toString(); } public String dumpChannels() { StringBuilder sb = new StringBuilder(); synchronized (channels) { for (Map.Entry entry : channels.entrySet()) { Multiplexer m = entry.getValue(); sb.append(entry.getKey()).append(": ").append(m.getServiceIds()).append("\n"); } } return sb.toString(); } private void registerChannel(JChannel ch, String stack_name) throws Exception { if(expose_channels && server != null) JmxConfigurator.registerChannel(ch, server, domain, stack_name, expose_protocols); } private void unregister(String name) { if(expose_channels && server != null){ try{ JmxConfigurator.unregister(server, name); }catch(Exception e){ log.error("failed unregistering " + name, e); } } } private void parse(InputStream input, boolean replace) throws Exception { /** * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. * If somebody wants to improve this, please be my guest. */ DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); factory.setValidating(false); //for now DocumentBuilder builder=factory.newDocumentBuilder(); Document document=builder.parse(input); // The root element of the document should be the "config" element, // but the parser(Element) method checks this so a check is not // needed here. Element configElement = document.getDocumentElement(); parse(configElement, replace); } private void parse(Element root, boolean replace) throws Exception { /** * CAUTION: crappy code ahead ! I (bela) am not an XML expert, so the code below is pretty amateurish... * But it seems to work, and it is executed only on startup, so no perf loss on the critical path. * If somebody wants to improve this, please be my guest. */ String root_name=root.getNodeName(); if(!PROTOCOL_STACKS.equals(root_name.trim().toLowerCase())) { String error="XML protocol stack configuration does not start with a '' element; " + "maybe the XML configuration needs to be converted to the new format ?\n" + "use 'java org.jgroups.conf.XmlConfigurator -new_format' to do so"; throw new IOException("invalid XML configuration: " + error); } NodeList tmp_stacks=root.getChildNodes(); for(int i=0; i < tmp_stacks.getLength(); i++) { Node node = tmp_stacks.item(i); if(node.getNodeType() != Node.ELEMENT_NODE ) continue; Element stack=(Element) node; String tmp=stack.getNodeName(); if(!STACK.equals(tmp.trim().toLowerCase())) { throw new IOException("invalid configuration: didn't find a \"" + STACK + "\" element under \"" + PROTOCOL_STACKS + "\""); } NamedNodeMap attrs = stack.getAttributes(); Node name=attrs.getNamedItem(NAME); // Node descr=attrs.getNamedItem(DESCR); String st_name=name.getNodeValue(); // String stack_descr=descr.getNodeValue(); // System.out.print("Parsing \"" + st_name + "\" (" + stack_descr + ")"); NodeList configs=stack.getChildNodes(); for(int j=0; j < configs.getLength(); j++) { Node tmp_config=configs.item(j); if(tmp_config.getNodeType() != Node.ELEMENT_NODE ) continue; Element cfg = (Element) tmp_config; tmp=cfg.getNodeName(); if(!CONFIG.equals(tmp)) throw new IOException("invalid configuration: didn't find a \"" + CONFIG + "\" element under \"" + STACK + "\""); XmlConfigurator conf=XmlConfigurator.getInstance(cfg); // fixes http://jira.jboss.com/jira/browse/JGRP-290 ConfiguratorFactory.substituteVariables(conf); // replace vars with system props String val=conf.getProtocolStackString(); if(replace) { stacks.put(st_name, val); if(log.isTraceEnabled()) log.trace("added config '" + st_name + "'"); } else { if(!stacks.containsKey(st_name)) { stacks.put(st_name, val); if(log.isTraceEnabled()) log.trace("added config '" + st_name + "'"); } else { if(log.isTraceEnabled()) log.trace("didn't add config '" + st_name + " because one of the same name already existed"); } } } } } private class MuxFactoryChannelListener extends ChannelListenerAdapter{ public void channelClosed(Channel channel) { MuxChannel mch = (MuxChannel)channel; Multiplexer multiplexer = mch.getMultiplexer(); boolean all_closed = multiplexer.close(); if(all_closed) { channels.remove(mch.getStackName()); unregister(domain + ":*,cluster=" + mch.getStackName()); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/MembershipListener.java0000644000175000017500000000375411366547366026062 0ustar twernertwerner// $Id: MembershipListener.java,v 1.8 2007/07/21 06:21:55 belaban Exp $ package org.jgroups; /** * Allows a listener to be notified when group membership changes. * These callbacks are used in {@link org.jgroups.blocks.PullPushAdapter}. *

        * The MembershipListener interface is similar to the {@link MessageListener} * interface: every time a new view, a suspicion message, or a * block event is received, the corresponding method of the class implementing * MembershipListener will be called. * Oftentimes the only method containing any functionality will be viewAccepted() * which notifies the receiver that a new member has joined the group or that an * existing member has left or crashed. */ public interface MembershipListener { /** * Called when a change in membership has occurred. * No long running actions or sending of messages should be done in this callback. * If some long running action needs to be performed, it should be done in a separate thread.

        * Note that on reception of the first view (a new member just joined), the channel will not yet be * in the connected state. This only happens when {@link Channel#connect(String)} returns. */ void viewAccepted(View new_view); /** * Called whenever a member is suspected of having crashed, * but has not yet been excluded. */ void suspect(Address suspected_mbr); /** * Called (usually by the FLUSH protocol), as an indication that the member should stop sending messages. * Any messages sent after returning from this callback might get blocked by the FLUSH protocol. When the FLUSH * protocol is done, and messages can be sent again, the FLUSH protocol will simply unblock all pending messages. * If a callback for unblocking is desired, implement {@link org.jgroups.ExtendedMembershipListener#unblock()}. * Note that block() is the equivalent of reception of a BlockEvent in the pull mode. */ void block(); } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ChannelFactory.java0000644000175000017500000001506611366547366025160 0ustar twernertwerner// $Id: ChannelFactory.java,v 1.11.2.1 2008/02/27 16:59:36 belaban Exp $ package org.jgroups; import org.w3c.dom.Element; import java.io.File; import java.net.URL; /** * A channel factory that removes hardwiring of calls to create JGroups * channels. ChannelFactory enables client applications to use custom Channel * creation methodologies. * * @see JChannelFactory * */ public interface ChannelFactory { /** * Initializes the factory. * * * @param properties * @throws ChannelException */ void setMultiplexerConfig(Object properties) throws Exception; /** * Initializes the factory from a file. Example: conf/stacks.xml * * @param properties * @throws ChannelException */ void setMultiplexerConfig(File properties) throws Exception; void setMultiplexerConfig(Element properties) throws Exception; void setMultiplexerConfig(URL properties) throws Exception; void setMultiplexerConfig(String properties) throws Exception; /** * Creates an implementation of the Channel using a given stack name and * registering it under a given identity. *

        * * Channel has to be created with a unique application id per stack name. * *

        * Provided stack name has to be one of the stacks defined in a property * file that was passed to setMultiplexerConfig (e.g conf/stacks.xml). If * clients attempt to create a Channel for an undefined stack name or * they attempt to register a duplicate Channel per stack an Exception will be * thrown. * * *

        * Rather than having each multiplexed channel do a separate state transfer * clients can bundle state transfers for all channels created with the same * ChannelFactory. First of all, clients have to create Channels with * register_for_state_transfer set to true. After the last Channel that was * created with register_for_state_transfer set to true connects and * initiates state transfer the actual state transfer for all such channels * from this ChannelFactory is executed. * *

        * Using bundled state transfers is especially useful with the FLUSH * protocol in a stack. Recall that each state transfer triggers a flush and * thus instead of doing a separate flush for each Channel created with this * ChannelFactory we execute only one flush for all state transfers. * *

        * However, be aware of the implication of asynchronous nature of bundled * state transfer with the respect of channel connect. Recall that each * Channel after it returns from successful getState method can assume that * state is available. In case of bundled state transfer, state will be set * only after the last Channel registered for the bundled * state transfer connects and executes getState. * * * * * @param stack_name * The name of the stack to be used. All stacks are defined in * the configuration with which the factory is configured (see * {@link #setMultiplexerConfig(Object)} for example. * @param id * The identifier used for multiplexing and demultiplexing * (dispatching requests to one of possibly multiple receivers). * Note that id needs to be a string since it will be shipped * with each message. Try to pick a short string, because this is * shipped with every message (overhead). * @param register_for_state_transfer * If set to true, after all registered listeners called * either {@link Channel#connect(String, Address, String, long)} or * {@link Channel#connect(String) and Channel#getState(Address, long)} * successively on the returned Channel, the state for all * registered listeners will be fetched and set in all listeners. * @param substate_id * The ID of the sub state to be retrieved. Set this to null if * the entire state should be retrieved. If * register_for_state_transfer is false, substate_id will be * ignored * * * @return An implementation of Channel which keeps track of the id, so that * it can be attached to each message and be properly dispatched at * the receiver. * * @see Multiplexer * @see MuxChannel * * @throws ChannelException */ Channel createMultiplexerChannel(String stack_name, String id, boolean register_for_state_transfer, String substate_id) throws Exception; /** * Creates an implementation of the Channel using a given stack name and * registering it under a given identity. *

        * * Channel has to be created with a unique application id per stack name. * *

        * Provided stack name has to be one of the stacks defined in a property * file that was passed to setMultiplexerConfig (e.g conf/stacks.xml). If * clients attempt to create a Channel for an undefined stack name or * they attempt to register a duplicate Channel per stack an Exception will be * thrown. * * * * @param stack_name * The name of the stack to be used. All stacks are defined in * the configuration with which the factory is configured (see * {@link #setMultiplexerConfig(Object)} for example. * @param id * The identifier used for multiplexing and demultiplexing * (dispatching requests to one of possibly multiple receivers). * Note that id needs to be a string since it will be shipped * with each message. Try to pick a short string, because this is * shipped with every message (overhead). * * @return An implementation of Channel which keeps track of the id, so that * it can be attached to each message and be properly dispatched at * the receiver. * * @see Multiplexer * @see MuxChannel * * @throws ChannelException */ Channel createMultiplexerChannel(String stack_name, String id) throws Exception; Channel createChannel(Object props) throws ChannelException; /** Create a new channel with the properties defined in the factory */ Channel createChannel() throws ChannelException; Channel createChannel(String stack_name) throws Exception; } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/ChannelClosedException.java0000644000175000017500000000101111366547366026622 0ustar twernertwerner// $Id: ChannelClosedException.java,v 1.4 2006/11/13 17:42:11 bstansberry Exp $ package org.jgroups; /** * Thrown if an operation is attemped on a closed channel. */ public class ChannelClosedException extends ChannelException { private static final long serialVersionUID = -5172168752255182905L; public ChannelClosedException() { super(); } public ChannelClosedException(String msg) { super(msg); } public String toString() { return "ChannelClosedException"; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/mux/0000755000175000017500000000000011621261110022164 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/mux/MuxHeader.java0000644000175000017500000000417011366547366024745 0ustar twernertwernerpackage org.jgroups.mux; import org.jgroups.Global; import org.jgroups.Header; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; /** * Header used for multiplexing and de-multiplexing between service components on top of a Multiplexer (Channel) * @author Bela Ban * @version $Id: MuxHeader.java,v 1.7.4.1 2008/01/22 11:47:07 belaban Exp $ */ public class MuxHeader extends Header implements Streamable { String id=null; /** Used for service state communication between Multiplexers */ ServiceInfo info; private static final long serialVersionUID=9197570523315316128L; public MuxHeader() { } public MuxHeader(String id) { this.id=id; } public MuxHeader(ServiceInfo info) { this.info=info; } public String getId() { return id; } public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(id); out.writeObject(info); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { id=in.readUTF(); info=(ServiceInfo)in.readObject(); } public int size() { int retval=Global.BYTE_SIZE; // presence byte in Util.writeString if(id != null) retval+=id.length() +2; // for UTF retval+=Global.BYTE_SIZE; // presence for info if(info != null) retval+=info.size(); return retval; } public void writeTo(DataOutputStream out) throws IOException { Util.writeString(id, out); if(info != null) { out.writeBoolean(true); info.writeTo(out); } else { out.writeBoolean(false); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { id=Util.readString(in); if(in.readBoolean()) { info=new ServiceInfo(); info.readFrom(in); } } public String toString() { if(id != null) return id; if(info != null) return info.toString(); return ""; } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/mux/MuxChannel.java0000644000175000017500000003234311366547366025130 0ustar twernertwernerpackage org.jgroups.mux; import org.jgroups.*; import org.jgroups.stack.ProtocolStack; import java.io.Serializable; import java.util.Map; /** * Multiplexer channel is a lightweight version of a regular channel where * multiple MuxChannel(s) share the same underlying regular channel. * *

        * MuxChannel has to be created with a unique application id. The multiplexer * keeps track of all registered applications and tags messages belonging to a * specific application with that id for sent messages. When receiving a message * from a remote peer, the multiplexer will dispatch a message to the * appropriate MuxChannel depending on the id attached to the message. * *

        * MuxChannel is created using * {@link ChannelFactory#createMultiplexerChannel(String, String)}. * * @author Bela Ban, Vladimir Blagojevic * @see ChannelFactory#createMultiplexerChannel(String, String) * @see JChannelFactory#createMultiplexerChannel(String, String) * @see Multiplexer * @since 2.4 * @version $Id: MuxChannel.java,v 1.38.2.14 2008/02/15 04:23:42 vlada Exp $ */ public class MuxChannel extends JChannel { /* * Header identifier */ private static final String name="MUX"; /* * MuxChannel service ID */ private final String id; /* * The name of the JGroups stack, e.g. as defined in stacks.xml */ private final String stack_name; /* * Header added to each message sent from this MuxChannel */ private final MuxHeader hdr; /* * Underlying Multiplexer */ private final Multiplexer mux; MuxChannel(String id,String stack_name,Multiplexer mux) { super(false); // don't create protocol stack, queues and threads if(id == null || id.length() == 0) throw new IllegalArgumentException("Cannot create MuxChannel with id " + id); if(stack_name == null || stack_name.length() == 0) throw new IllegalArgumentException("Cannot create MuxChannel with stack_name " + stack_name); if(mux == null) throw new IllegalArgumentException("Cannot create MuxChannel with Multiplexer " + mux); this.stack_name=stack_name; this.id=id; this.hdr=new MuxHeader(id); this.mux=mux; closed=!mux.isOpen(); } public String getStackName() { return stack_name; } public String getId() { return id; } public Multiplexer getMultiplexer() { return mux; } public String getChannelName() { return mux.getChannel().getClusterName(); } public String getClusterName() { return mux.getChannel().getClusterName(); } public Address getLocalAddress() { return mux.getLocalAddress(); } public String getProperties() { return mux.getChannel().getProperties(); } /** This should never be used (just for testing) ! */ public JChannel getChannel() { return mux.getChannel(); } /** * Returns the service view, ie. the cluster view (see * {@link #getView()}) minus the nodes on which this service is * not running, e.g. if S1 runs on A and C, and the cluster view is {A,B,C}, * then the service view is {A,C} * * @return The service view (list of nodes on which this service is running) */ public View getView() { return closed || !connected? null : mux.getServiceView(id); } /** * Returns the JGroups view of a cluster, e.g. if we have nodes A, B and C, * then the view will be {A,B,C} * * @return The JGroups view */ public View getClusterView() { return mux.getChannel().getView(); } public ProtocolStack getProtocolStack() { return mux.getChannel().getProtocolStack(); } public Map dumpStats() { Map retval=mux.getChannel().getProtocolStack().dumpStats(); if(retval != null) { Map tmp=dumpChannelStats(); if(tmp != null) retval.put("channel", tmp); } return retval; } protected void setClosed(boolean f) { closed=f; } protected void setConnected(boolean f) { connected=f; } public synchronized void connect(String channel_name) throws ChannelException, ChannelClosedException { /*make sure the channel is not closed*/ checkClosed(); /*if we already are connected, then ignore this*/ if(isConnected()) { if(log.isTraceEnabled()) log.trace("already connected to " + channel_name); return; } //add service --> MuxChannel mapping to multiplexer in case we called disconnect on this channel mux.addServiceIfNotPresent(getId(), this); if(!mux.isConnected()) { mux.connect(getStackName()); } try { if(mux.flushSupported()) { boolean successfulFlush=mux.startFlush(false); if(!successfulFlush && log.isWarnEnabled()) { log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); } } try { mux.sendServiceUpMessage(getId()); setClosed(false); setConnected(true); notifyChannelConnected(this); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending SERVICE_UP message", e); throw new ChannelException("MuxChannel.connect() failed", e); } } finally { if(mux.flushSupported()) mux.stopFlush(); } } public synchronized void connect(String cluster_name, Address target, String state_id, long timeout) throws ChannelException { /*make sure the channel is not closed*/ checkClosed(); /*if we already are connected, then ignore this*/ if(isConnected()) { if(log.isTraceEnabled()) log.trace("already connected to " + cluster_name); return; } //add service --> MuxChannel mapping to multiplexer in case we called disconnect on this channel mux.addServiceIfNotPresent(getId(), this); if(!mux.isConnected()) { mux.connect(getStackName()); } try { if(mux.flushSupported()) { boolean successfulFlush=mux.startFlush(false); if(!successfulFlush && log.isWarnEnabled()) { log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); } } try { mux.sendServiceUpMessage(getId()); setClosed(false); setConnected(true); notifyChannelConnected(this); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending SERVICE_UP message", e); throw new ChannelException("MuxChannel.connect() failed", e); } View serviceView=mux.getServiceView(getId()); boolean stateTransferOk=false; boolean fetchState=serviceView != null && serviceView.size() > 1; if(fetchState) { stateTransferOk=getState(target, state_id, timeout, false); if(!stateTransferOk) { throw new StateTransferException("Could not retrieve state " + state_id + " from " + target); } } } finally { if(mux.flushSupported()) mux.stopFlush(); } } public synchronized void disconnect() { if(!isConnected()) return; setClosed(false); setConnected(false); notifyServiceDown(); // disconnects JChannel if all MuxChannels are // in disconnected state mux.disconnect(); notifyChannelDisconnected(this); } public synchronized void close() { if(closed) return; if(isConnected()) { setConnected(false); notifyServiceDown(); } setClosed(true); closeMessageQueue(true); notifyChannelClosed(this); } protected void notifyServiceDown() { try { if(mux.flushSupported()) { boolean successfulFlush=mux.startFlush(false); if(!successfulFlush && log.isWarnEnabled()) { log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); } } try { mux.sendServiceDownMessage(getId()); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending SERVICE_DOWN message", e); } } catch(Throwable t) { log.error("closing channel failed", t); } finally { if(mux.flushSupported()) mux.stopFlush(); } } public synchronized void open() throws ChannelException { setClosed(false); setConnected(false); // needs to be connected next if(!mux.isOpen()) { mux.open(); } } public synchronized void shutdown() { if(closed) return; setClosed(true); setConnected(false); try { if(mux.flushSupported()) { boolean successfulFlush=mux.startFlush(false); if(!successfulFlush && log.isWarnEnabled()) { log.warn("Flush failed at " + mux.getLocalAddress() + ":" + getId()); } } try { mux.sendServiceDownMessage(getId()); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed sending SERVICE_DOWN message", e); } } catch(Throwable t) { log.error("shutdown channel failed", t); } finally { if(mux.flushSupported()) mux.stopFlush(); } closeMessageQueue(true); notifyChannelClosed(this); } public void send(Message msg) throws ChannelNotConnectedException,ChannelClosedException { msg.putHeader(name, hdr); mux.getChannel().send(msg); if(stats) { sent_msgs++; sent_bytes+=msg.getLength(); } } public void send(Address dst, Address src, Serializable obj) throws ChannelNotConnectedException, ChannelClosedException { send(new Message(dst, src, obj)); } public void down(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); msg.putHeader(name, hdr); } mux.getChannel().down(evt); } public Object downcall(Event evt) { if(evt.getType() == Event.MSG) { Message msg=(Message)evt.getArg(); msg.putHeader(name, hdr); } return mux.getChannel().downcall(evt); } public boolean getState(Address target, String state_id, long timeout, boolean useFlushIfPresent) throws ChannelNotConnectedException, ChannelClosedException { String my_id=id; if(state_id != null) my_id+="::" + state_id; // we're usig service views, so we need to find the first host in the cluster on which our service runs // http://jira.jboss.com/jira/browse/JGRP-247 // // unless service runs on a specified target node // http://jira.jboss.com/jira/browse/JGRP-401 Address service_view_coordinator=mux.getStateProvider(target, id); Address tmp=getLocalAddress(); if(service_view_coordinator != null) target=service_view_coordinator; if(tmp != null && tmp.equals(target)) // this will avoid the "cannot get state from myself" error target=null; if(!mux.stateTransferListenersPresent()) return mux.getChannel().getState(target, my_id, timeout, useFlushIfPresent); else { View serviceView=mux.getServiceView(getId()); boolean fetchState=serviceView != null && serviceView.size() > 1; if(fetchState) { return mux.getState(target, my_id, timeout); } else { return false; } } } void fireChannelShunned(){ notifyChannelShunned(); } void fireChannelReconnected(Address address){ notifyChannelReconnected(address); } public void returnState(byte[] state) { mux.getChannel().returnState(state, id); } public void returnState(byte[] state, String state_id) { String my_id=id; if(state_id != null) my_id+="::" + state_id; mux.getChannel().returnState(state, my_id); } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/mux/Multiplexer.java0000644000175000017500000013203611366547366025400 0ustar twernertwernerpackage org.jgroups.mux; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.TimeoutException; import org.jgroups.protocols.pbcast.FLUSH; import org.jgroups.stack.StateTransferInfo; import org.jgroups.util.*; import org.jgroups.util.ThreadFactory; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; /** * The multiplexer allows multiple channel interfaces to be associated with one * underlying instance of JChannel. * *

        * The multiplexer is essentially a building block residing on top of a JChannel * providing multiplexing functionality to N instances of MuxChannel. Since * MuxChannel extends the JGroups JChannel class, user applications are * completely unaware of this change in the underlying plumbing. * *

        * Each JGroups application sharing a channel through a multiplexer has to * create a MuxChannel with a unique application id. The multiplexer keeps track * of all registered applications and tags messages belonging to a specific * application with that id for sent messages. When receiving a message from a * remote peer, the multiplexer will dispatch a message to the appropriate * MuxChannel depending on the id attached to the message. * * * @author Bela Ban, Vladimir Blagojevic * @see MuxChannel * @see Channel * @version $Id: Multiplexer.java,v 1.85.2.18 2009/08/11 11:28:28 belaban Exp $ */ public class Multiplexer implements UpHandler { private static final Log log=LogFactory.getLog(Multiplexer.class); private static final String SEPARATOR="::"; private static final short SEPARATOR_LEN=(short)SEPARATOR.length(); private static final String NAME="MUX"; /** * Map. Maintains the mapping between service IDs and * their associated MuxChannels */ private final ConcurrentMap services=new ConcurrentHashMap(); private final JChannel channel; /** Thread pool to concurrently process messages sent to different services */ private final ExecutorService thread_pool; /** * To make sure messages sent to different services are processed * concurrently (using the thread pool above), but messages to the same * service are processed FIFO */ private final FIFOMessageQueue fifo_queue=new FIFOMessageQueue(); /** To collect service acks from Multiplexers */ private final AckCollector service_ack_collector=new AckCollector(); protected long service_ack_timeout = 2000; /** Cluster view */ private volatile View view=null; /** * Map. Map of service IDs and booleans that determine * whether getState() has already been called */ private final Map state_transfer_listeners=new HashMap(); /** * Map>. A map of services as keys and lists of hosts * as values */ private final Map> service_state=new HashMap>(); /** * Map>. Keys are senders, values are a set of * services hosted by that sender. Used to collect responses to * LIST_SERVICES_REQ */ private final Map> service_responses=new HashMap>(); private final List

        services_merged_collector=new ArrayList
        (); private AtomicBoolean services_merged=new AtomicBoolean(false); private long service_response_timeout=3000; public Multiplexer(JChannel channel) { if(channel == null || !channel.isOpen()) throw new IllegalArgumentException("Channel " + channel + " cannot be used for Multiplexer"); this.channel=channel; this.channel.addChannelListener(new MultiplexerChannelListener()); this.channel.setUpHandler(this); this.channel.setOpt(Channel.BLOCK, Boolean.TRUE); // we want to handle BLOCK events ourselves //thread pool is enabled by default boolean use_thread_pool=Global.getPropertyAsBoolean(Global.MUX_ENABLED, true); if(use_thread_pool) { thread_pool=createThreadPool(); } else { thread_pool=null; } } JChannel getChannel() { return channel; } /** * @deprecated Use ${link #getServiceIds()} instead * @return The set of service IDs */ public Set getApplicationIds() { return getServiceIds(); } public Set getServiceIds() { return Collections.unmodifiableSet(services.keySet()); } public long getServicesResponseTimeout() { return service_response_timeout; } public void setServicesResponseTimeout(long services_rsp_timeout) { this.service_response_timeout=services_rsp_timeout; } public long getServiceAckTimeout() { return service_ack_timeout; } public void setServiceAckTimeout(long service_ack_timeout) { this.service_ack_timeout=service_ack_timeout; } /** * Returns a copy of the current view minus the nodes on which * service service_id is not running * * @param service_id * @return The service view */ View getServiceView(String service_id) { List
        hosts=service_state.get(service_id); if(hosts == null) return null; return generateServiceView(hosts); } boolean stateTransferListenersPresent() { return !state_transfer_listeners.isEmpty(); } public synchronized void registerForStateTransfer(String appl_id, String substate_id) { String key=appl_id; if(substate_id != null && substate_id.length() > 0) key+=SEPARATOR + substate_id; state_transfer_listeners.put(key, Boolean.FALSE); } synchronized boolean getState(Address target, String id, long timeout) throws ChannelNotConnectedException, ChannelClosedException { if(state_transfer_listeners.isEmpty()) return false; for(Iterator> it=state_transfer_listeners.entrySet().iterator();it.hasNext();) { Map.Entry entry=it.next(); String key=entry.getKey(); int index=key.indexOf(SEPARATOR); boolean match; if(index > -1) { String tmp=key.substring(0, index); match=id.equals(tmp); } else { match=id.equals(key); } if(match) { entry.setValue(Boolean.TRUE); break; } } Collection values=state_transfer_listeners.values(); boolean all_true=Util.all(values, Boolean.TRUE); if(!all_true) return true; // pseudo boolean rc=false; Set keys=new HashSet(state_transfer_listeners.keySet()); rc=fetchServiceStates(target, keys, timeout); state_transfer_listeners.clear(); return rc; } protected ThreadPoolExecutor createThreadPool() { int min_threads=1, max_threads=4; long keep_alive=30000; Map m=channel.getInfo(); min_threads=Global.getPropertyAsInteger(Global.MUX_MIN_THREADS, min_threads); max_threads=Global.getPropertyAsInteger(Global.MUX_MAX_THREADS, max_threads); keep_alive=Global.getPropertyAsLong(Global.MUX_KEEPALIVE, keep_alive); ThreadFactory factory=new DefaultThreadFactory(Util.getGlobalThreadGroup(), "Multiplexer", false, true); return new ThreadPoolExecutor(min_threads, max_threads, keep_alive, TimeUnit.MILLISECONDS, new SynchronousQueue(), factory, new ShutdownRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())); } protected void shutdownThreadPool() { if(thread_pool != null && !thread_pool.isShutdown()) { thread_pool.shutdownNow(); try { thread_pool.awaitTermination(Global.THREADPOOL_SHUTDOWN_WAIT_TIME, TimeUnit.MILLISECONDS); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } } /** * Fetches the app states for all service IDs in keys. The keys are a * duplicate list, so it cannot be modified by the caller of this method * * @param keys */ private boolean fetchServiceStates(Address target, Set keys, long timeout) throws ChannelClosedException, ChannelNotConnectedException { boolean rc, all_tranfers_ok=false; boolean flushStarted=Util.startFlush(channel); if(flushStarted) { try { for(String stateId:keys) { rc=channel.getState(target, stateId, timeout, false); if(!rc) throw new Exception("Failed transfer for state id " + stateId + ", state provider was " + target); } all_tranfers_ok=true; } catch(Exception e) { log.warn("Failed multiple state transfer under one flush phase ", e); } finally { channel.stopFlush(); } } return flushStarted && all_tranfers_ok; } void sendServiceUpMessage(String service) throws Exception { // we have to make this service message non OOB since we have // to FIFO order service messages and BLOCK/UNBLOCK messages sendServiceMessage(true, ServiceInfo.SERVICE_UP, service, null, false); } void sendServiceDownMessage(String service) throws Exception { // we have to make this service message non OOB since we have // to FIFO order service messages and BLOCK/UNBLOCK messages sendServiceMessage(true, ServiceInfo.SERVICE_DOWN, service, null, false); } /** * Remove mux header and dispatch to correct MuxChannel * * @param evt * @return */ public Object up(final Event evt) { switch(evt.getType()) { case Event.MSG: final Message msg=(Message)evt.getArg(); final MuxHeader hdr=(MuxHeader)msg.getHeader(NAME); if(hdr == null) { log.error("MuxHeader not present - discarding message " + msg); return null; } Address sender=msg.getSrc(); boolean isServiceMessage=hdr.info != null; if(isServiceMessage) { try { handleServiceMessage(hdr.info, sender); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failure in handling service message " + hdr.info + " from sender " + sender, e); } return null; } else { //regular message between MuxChannel(s) final MuxChannel mux_ch=services.get(hdr.id); return mux_ch == null? null : passToMuxChannel(mux_ch, evt, fifo_queue, sender, hdr.id, false, msg.isFlagSet(Message.OOB)); } case Event.VIEW_CHANGE: Vector
        old_members=view != null? view.getMembers() : null; view=(View)evt.getArg(); Vector
        new_members=view != null? view.getMembers() : null; Vector
        left_members=Util.determineLeftMembers(old_members, new_members); if(view instanceof MergeView) { final MergeView temp_merge_view=(MergeView)view.clone(); if(log.isTraceEnabled()) log.trace("received a MergeView: " + temp_merge_view + ", adjusting the service view"); try { handleMergeView(temp_merge_view); } catch(Exception e) { if(log.isErrorEnabled()) log.error("failed handling merge view", e); } finally { synchronized(service_responses) { service_responses.clear(); } synchronized(services_merged_collector) { services_merged_collector.clear(); } services_merged.set(false); } } else { // regular view HashMap> payload=(HashMap>)view.getPayload("service_state"); if(payload != null) { synchronized(service_state) { service_state.putAll(payload); } } } service_ack_collector.handleView(view); for(Address member:left_members) { try { adjustServiceView(member); } catch(Throwable t) { if(log.isErrorEnabled()) log.error("failed adjusting service views", t); } } break; case Event.PREPARE_VIEW: View prepare_view=(View)evt.getArg(); old_members=view != null? view.getMembers() : new Vector
        (); Vector
        added_members=new Vector
        (prepare_view.getMembers()); added_members.removeAll(old_members); if(!added_members.isEmpty()) { synchronized(service_state) { prepare_view.addPayload("service_state", service_state); } } break; case Event.SUSPECT: Address suspected_mbr=(Address)evt.getArg(); service_ack_collector.suspect(suspected_mbr); /* * http://jira.jboss.com/jira/browse/JGRP-665 * Intentionally do not update service_response since we might * get false suspect while merging if FD is aggressive enough. * Instead rely on ServiceInfo.SERVICES_MERGED and timeout * mechanism in handleMergeView */ passToAllMuxChannels(evt); break; case Event.GET_APPLSTATE: return handleStateRequest(evt, true); case Event.STATE_TRANSFER_OUTPUTSTREAM: handleStateRequest(evt, true); break; case Event.GET_STATE_OK: case Event.STATE_TRANSFER_INPUTSTREAM: handleStateResponse(evt, true); break; case Event.SET_LOCAL_ADDRESS: passToAllMuxChannels(evt); break; case Event.BLOCK: passToAllMuxChannels(evt, true, true); return null; case Event.UNBLOCK: // process queued-up MergeViews passToAllMuxChannels(evt); break; case Event.EXIT: //we are being shunned, close all services closeAll(); break; default: passToAllMuxChannels(evt); break; } return null; } public Channel createMuxChannel(String id, String stack_name) throws Exception { if(services.containsKey(id)) { throw new Exception("service ID \"" + id + "\" is already registered at channel" + getLocalAddress() + ", cannot register service with duplicate ID at the same channel"); } MuxChannel ch=new MuxChannel(id, stack_name, this); services.put(id, ch); return ch; } private void passToAllMuxChannels(Event evt) { passToAllMuxChannels(evt, false, true); } private void passToAllMuxChannels(Event evt, boolean block, boolean bypass_thread_pool) { String service_name; MuxChannel ch; for(Map.Entry entry:services.entrySet()) { service_name=entry.getKey(); ch=entry.getValue(); // these events are directly delivered, don't get added to any queue passToMuxChannel(ch, evt, fifo_queue, null, service_name, block, bypass_thread_pool); } } void addServiceIfNotPresent(String id, MuxChannel ch) { services.putIfAbsent(id, ch); } protected MuxChannel removeService(String id) { MuxChannel ch=services.remove(id); //http://jira.jboss.com/jira/browse/JGRP-623 if(ch != null) ch.up(new Event(Event.UNBLOCK)); return ch; } /** Closes the underlying JChannel if all MuxChannels have been disconnected */ void disconnect() { boolean all_disconnected=true; for(MuxChannel mux_ch:services.values()) { if(mux_ch.isConnected()) { all_disconnected=false; break; } } if(all_disconnected) { if(log.isTraceEnabled()) { log.trace("disconnecting underlying JChannel as all MuxChannels are disconnected"); } channel.disconnect(); } } public boolean close() { boolean all_closed=true; for(MuxChannel mux_ch:services.values()) { if(mux_ch.isOpen()) { all_closed=false; break; } } if(all_closed) { if(log.isTraceEnabled()) { log.trace("closing underlying JChannel as all MuxChannels are closed"); } channel.close(); services.clear(); shutdownThreadPool(); } return all_closed; } public void closeAll() { for(MuxChannel mux_ch:services.values()) { mux_ch.setConnected(false); mux_ch.setClosed(true); mux_ch.closeMessageQueue(true); } } boolean shutdown() { boolean all_closed=true; for(MuxChannel mux_ch:services.values()) { if(mux_ch.isOpen()) { all_closed=false; break; } } if(all_closed) { if(log.isTraceEnabled()) { log.trace("shutting down underlying JChannel as all MuxChannels are closed"); } channel.shutdown(); services.clear(); shutdownThreadPool(); } return all_closed; } Address getLocalAddress() { return channel.getLocalAddress(); } boolean flushSupported() { return channel.flushSupported(); } boolean startFlush(boolean automatic_resume) { boolean b = Util.startFlush(channel); if(automatic_resume) channel.stopFlush(); return b; } void stopFlush() { channel.stopFlush(); } boolean isConnected() { return channel.isConnected(); } void connect(String cluster_name) throws ChannelException { channel.connect(cluster_name); } /** * Determines whether the channel is open; i.e., the protocol stack has been * created (may not be connected though). */ boolean isOpen() { return channel.isOpen(); } void open() throws ChannelException { channel.open(); } /** * Returns an Address of a state provider for a given service_id. If * preferredTarget is a member of a service view for a given service_id then * preferredTarget is returned. Otherwise, service view coordinator is * returned if such node exists. If service view is empty for a given * service_id null is returned. * * @param preferredTarget * @param service_id * @return */ Address getStateProvider(Address preferredTarget, String service_id) { Address result=null; List
        hosts=service_state.get(service_id); if(hosts != null && !hosts.isEmpty()) { if(hosts.contains(preferredTarget)) { result=preferredTarget; } else { result=hosts.get(0); } } return result; } private void sendServiceMessage(boolean synchronous, byte type, String service, byte[] payload, boolean oob) throws Exception { Address host=getLocalAddress(); if(host == null) { if(log.isWarnEnabled()) { log.warn("local_addr is null, cannot send ServiceInfo." + ServiceInfo.typeToString(type) + " message"); } return; } if(!channel.isOpen() || !channel.isConnected()) { if(log.isWarnEnabled()) { log.warn("Underlying multiplexer channel " + channel.getLocalAddress() + " is not connected, cannot send ServiceInfo." + ServiceInfo.typeToString(type) + " message"); } return; } Message service_msg=new Message(); service_msg.putHeader(NAME, new MuxHeader(new ServiceInfo(type, service, host, payload))); if(oob) service_msg.setFlag(Message.OOB); if(channel.flushSupported()) service_msg.putHeader(FLUSH.NAME, new FLUSH.FlushHeader(FLUSH.FlushHeader.FLUSH_BYPASS)); if(synchronous) { //for synchronous invocation we need to collect acks //the host that is sending this message should also ack CopyOnWriteArrayList
        muxChannels=new CopyOnWriteArrayList
        (); muxChannels.add(host); List
        list=service_state.get(service); if(list != null && !list.isEmpty()) { muxChannels.addAllAbsent(list); } //initialize collector and ... service_ack_collector.reset(muxChannels); int size=service_ack_collector.size(); //then send a message channel.send(service_msg); long start=System.currentTimeMillis(); try { service_ack_collector.waitForAllAcks(service_ack_timeout); if(log.isTraceEnabled()) log.trace("received all service ACKs (" + size + ") in " + (System.currentTimeMillis() - start) + "ms"); } catch(TimeoutException e) { log.warn("failed to collect all service ACKs (" + size + ") for " + service_msg + " after " + service_ack_timeout + "ms, missing ACKs from " + service_ack_collector.printMissing() + ", local_addr=" + getLocalAddress()); } } else { //if asynchronous then fire and forget channel.send(service_msg); } } private Object handleStateRequest(Event evt, boolean hasReturnValue) { StateTransferInfo info=(StateTransferInfo)evt.getArg(); String id=info.state_id; String original_id=id; Address requester=info.target; // the sender of the state request if(id == null) { if(log.isWarnEnabled()) { log.warn("Invalid state request " + info + " arrived at Multiplexer, dropping it"); } return new StateTransferInfo(null, original_id, 0L, null); } try { int index=id.indexOf(SEPARATOR); if(index > -1) { info.state_id=id.substring(index + SEPARATOR_LEN); id=id.substring(0, index); // similar reuse as above... } else { info.state_id=null; } MuxChannel mux_ch=services.get(id); //JGRP-616 if(mux_ch == null) { if(log.isWarnEnabled()) log.warn("State provider " + channel.getLocalAddress() + " does not have service with id " + id + ", returning null state"); return new StateTransferInfo(null, original_id, 0L, null); } // state_id will be null, get regular state from the service named state_id StateTransferInfo ret=(StateTransferInfo)passToMuxChannel(mux_ch, evt, fifo_queue, requester, id, hasReturnValue); if(ret != null) { ret.state_id=original_id; } else { return new StateTransferInfo(null, original_id, 0L, null); } return ret; } catch(Throwable ex) { if(log.isErrorEnabled()) log.error("failed returning the application state, will return null", ex); return new StateTransferInfo(null, original_id, 0L, null); } } private void handleStateResponse(Event evt, boolean block) { StateTransferInfo info=(StateTransferInfo)evt.getArg(); MuxChannel mux_ch; Address state_sender=info.target; String appl_id, substate_id, tmp; tmp=info.state_id; if(tmp == null) { if(log.isTraceEnabled()) log.trace("state is null, not passing up: " + info); return; } int index=tmp.indexOf(SEPARATOR); if(index > -1) { appl_id=tmp.substring(0, index); substate_id=tmp.substring(index + SEPARATOR_LEN); } else { appl_id=tmp; substate_id=null; } mux_ch=services.get(appl_id); if(mux_ch == null) { log.error("State receiver " + channel.getLocalAddress() + " does not have service with id " + appl_id); } else { StateTransferInfo tmp_info=info.copy(); tmp_info.state_id=substate_id; Event tmpEvt=new Event(evt.getType(), tmp_info); passToMuxChannel(mux_ch, tmpEvt, fifo_queue, state_sender, appl_id, block); } } private void handleServiceMessage(ServiceInfo info, Address sender) throws Exception { switch(info.type) { case ServiceInfo.SERVICE_UP: handleServiceUp(info.service, info.host); ackServiceMessage(info, sender); break; case ServiceInfo.SERVICE_DOWN: handleServiceDown(info.service, info.host); ackServiceMessage(info, sender); break; case ServiceInfo.LIST_SERVICES_RSP: handleServicesRsp(sender, info.state); break; case ServiceInfo.ACK: service_ack_collector.ack(sender); break; case ServiceInfo.SERVICES_MERGED: synchronized(services_merged_collector) { if(!services_merged_collector.contains(sender)) { services_merged_collector.add(sender); } boolean mergeOk=view != null && services_merged_collector.containsAll(view.getMembers()); services_merged.set(mergeOk); if(log.isDebugEnabled()) log.debug(getLocalAddress() + " got service merged from " + sender + " merged so far " + services_merged_collector + " view is " + view.size()); } break; default: if(log.isErrorEnabled()) log.error("service request type " + info.type + " not known"); break; } } private void ackServiceMessage(ServiceInfo info, Address ackTarget) throws ChannelNotConnectedException, ChannelClosedException { Message ack=new Message(ackTarget, null, null); ack.setFlag(Message.OOB); ServiceInfo si=new ServiceInfo(ServiceInfo.ACK, info.service, info.host, null); MuxHeader hdr=new MuxHeader(si); ack.putHeader(NAME, hdr); if(channel.isConnected()) channel.send(ack); } private void handleServicesRsp(Address sender, byte[] state) throws Exception { Set s=(Set)Util.objectFromByteBuffer(state); boolean all_merged=false; synchronized(service_responses) { Set tmp=service_responses.get(sender); if(tmp == null) tmp=new HashSet(); tmp.addAll(s); service_responses.put(sender, tmp); if(log.isDebugEnabled()) log.debug(getLocalAddress() + " received service response: " + sender + "(" + s.toString() + ")"); all_merged=(view != null && service_responses.keySet().containsAll(view.getMembers())); } if(all_merged) { if(log.isDebugEnabled()) log.debug(getLocalAddress() + " sent service merged " + service_responses.keySet() + " view is " + view.getMembers()); sendServiceMessage(false, ServiceInfo.SERVICES_MERGED, null, null, true); } } private void handleServiceDown(String service, Address host) { List
        hosts, hosts_copy; boolean removed=false; synchronized(service_state) { hosts=service_state.get(service); if(hosts == null) return; removed=hosts.remove(host); hosts_copy=new ArrayList
        (hosts); // make a copy so we don't modify hosts in generateServiceView() } if(removed) { View service_view=generateServiceView(hosts_copy); MuxChannel ch=services.get(service); if(ch != null && ch.isConnected()) { Event view_evt=new Event(Event.VIEW_CHANGE, service_view); //we cannot pass service message to thread pool since we have //to FIFO order service messages with BLOCK/UNBLOCK messages //therefore all of them have to bypass thread pool passToMuxChannel(ch, view_evt, fifo_queue, null, service, false, true); } else { if(log.isTraceEnabled()) log.trace("service " + service + " not found, cannot dispatch service view " + service_view); } } Address local_address=getLocalAddress(); boolean isMyService=local_address != null && local_address.equals(host); if(isMyService) removeService(service); } private void handleServiceUp(String service, Address host) { List
        hosts, hosts_copy; boolean added=false; synchronized(service_state) { hosts=service_state.get(service); if(hosts == null) { hosts=new CopyOnWriteArrayList
        (); service_state.put(service, hosts); } if(!hosts.contains(host)) { hosts.add(host); added=true; } hosts_copy=new ArrayList
        (hosts); // make a copy so we don't modify hosts in generateServiceView() } if(added) { View service_view=generateServiceView(hosts_copy); MuxChannel ch=services.get(service); if(ch != null) { Event view_evt=new Event(Event.VIEW_CHANGE, service_view); //we cannot pass service message to thread pool since we have //to FIFO order service messages with BLOCK/UNBLOCK messages //therefore all of them have to bypass thread pool passToMuxChannel(ch, view_evt, fifo_queue, null, service, false, true); } else { if(log.isTraceEnabled()) log.trace("service " + service + " not found, cannot dispatch service view " + service_view); } } } /** * Fetches the service states from everyone else in the cluster. Once all * states have been received and inserted into service_state, compute a * service view (a copy of MergeView) for each service and pass it up * * @param view */ private void handleMergeView(MergeView view) throws Exception { long time_to_wait=service_response_timeout; long start_time=System.currentTimeMillis(); Map> copy=null; byte[] data=Util.objectToByteBuffer(new HashSet(services.keySet())); //loop and keep sending our service list until either //we hit timeout or we get notification of merge completed //http://jira.jboss.com/jira/browse/JGRP-665 while(time_to_wait > 0 && !services_merged.get()) { // we have to make this message OOB since we are running on a thread // propelling a regular synchronous message call to install a new view sendServiceMessage(false, ServiceInfo.LIST_SERVICES_RSP, null, data, true); Util.sleep(500); time_to_wait=service_response_timeout - (System.currentTimeMillis() - start_time); } if(time_to_wait <= 0 && !services_merged.get()) { log.warn("Services not merged at " + getLocalAddress() + " received merge from " + services_merged_collector); } synchronized(service_responses) { copy=new HashMap>(service_responses); } if(log.isDebugEnabled()) log.debug("At " + getLocalAddress() + " emitting views to MuxChannels " + copy); // merges service_responses with service_state and emits MergeViews for // the services affected (MuxChannel) mergeServiceState(view, copy); } private void mergeServiceState(MergeView view, Map> copy) { Set modified_services=new HashSet(); synchronized(service_state) { for(Iterator>> it=copy.entrySet().iterator();it.hasNext();) { Map.Entry> entry=it.next(); Address host=entry.getKey(); Set service_list=entry.getValue(); if(service_list == null) continue; for(String service:service_list) { List
        my_services=service_state.get(service); if(my_services == null) { my_services=new CopyOnWriteArrayList
        (); service_state.put(service, my_services); } boolean was_modified=my_services.add(host); if(was_modified) { modified_services.add(service); } } } } // now emit MergeViews for all services which were modified for(String service:modified_services) { MuxChannel ch=services.get(service); if(ch != null) { List
        hosts=service_state.get(service); Vector
        membersCopy=new Vector
        (view.getMembers()); membersCopy.retainAll(hosts); MergeView v=new MergeView(view.getVid(), membersCopy, view.getSubgroups()); passToMuxChannel(ch, new Event(Event.VIEW_CHANGE, v), fifo_queue, null, service, false); } } } private void adjustServiceView(Address host) { Address local_address=getLocalAddress(); synchronized(service_state) { for(Iterator>> it=service_state.entrySet().iterator();it.hasNext();) { Map.Entry> entry=it.next(); String service=entry.getKey(); List
        hosts=entry.getValue(); if(hosts == null) continue; if(hosts.remove(host)) { // make a copy so we don't modify hosts in // generateServiceView() View service_view=generateServiceView(new ArrayList
        (hosts)); MuxChannel ch=services.get(service); if(ch != null && ch.isConnected()) { Event view_evt=new Event(Event.VIEW_CHANGE, service_view); passToMuxChannel(ch, view_evt, fifo_queue, null, service, false, true); } else { if(log.isTraceEnabled()) log.trace("service " + service + " not found, cannot dispatch service view " + service_view); } } boolean isMyService=local_address != null && local_address.equals(host); if(isMyService) removeService(service); } } } /** * Create a copy of view which contains only members which are present in * hosts. Call viewAccepted() on the MuxChannel which corresponds with * service. If no members are removed or added from/to view, this is a * no-op. * * @param hosts * List
        * @return the servicd view (a modified copy of the real view), or null if * the view was not modified */ private View generateServiceView(List
        hosts) { if(view == null) { Vector
        tmp=new Vector
        (); tmp.add(getLocalAddress()); view=new View(new ViewId(getLocalAddress()), tmp); } Vector
        members=new Vector
        (view.getMembers()); members.retainAll(hosts); return new View(view.getVid(), members); } private Object passToMuxChannel(MuxChannel ch, Event evt, final FIFOMessageQueue queue, final Address sender, final String dest, boolean block) { return passToMuxChannel(ch, evt, queue, sender, dest, block, false); } private Object passToMuxChannel(MuxChannel ch, Event evt, final FIFOMessageQueue queue, final Address sender, final String dest, boolean block, boolean bypass_thread_pool) { if(thread_pool == null || bypass_thread_pool) { return ch.up(evt); } Task task=new Task(ch, evt, queue, sender, dest, block); ExecuteTask execute_task=new ExecuteTask(fifo_queue); // takes Task from queue and executes it try { fifo_queue.put(sender, dest, task); thread_pool.execute(execute_task); if(block) { try { return task.exchanger.exchange(null); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } } catch(InterruptedException e) { Thread.currentThread().interrupt(); } return null; } private class MultiplexerChannelListener extends ChannelListenerAdapter { //handle reconnecting of services after being shunned and //then reconnected back @Override public void channelReconnected(Address addr) { if(log.isDebugEnabled()) log.debug("Reconnecting services " + services.keySet()); for(MuxChannel mux_ch:services.values()) { try { if(log.isDebugEnabled()) log.debug("Reconnecting service " + mux_ch.getId()); mux_ch.open(); boolean reconnect=((Boolean)mux_ch.getOpt(Channel.AUTO_RECONNECT)).booleanValue(); boolean getState=((Boolean)mux_ch.getOpt(Channel.AUTO_GETSTATE)).booleanValue(); boolean fetchAndGetState=reconnect && getState; if(fetchAndGetState) { mux_ch.connect(mux_ch.getClusterName(), null, null, 10000); mux_ch.fireChannelReconnected(mux_ch.getLocalAddress()); } else { if(reconnect) { mux_ch.connect(mux_ch.getClusterName()); mux_ch.fireChannelReconnected(mux_ch.getLocalAddress()); } if(getState) { mux_ch.getState(null, 5000); } } } catch(ChannelException e) { if(log.isErrorEnabled()) log.error("MuxChannel reconnect failed " + e); } } } @Override public void channelShunned() { for(MuxChannel mux_ch:services.values()) { mux_ch.fireChannelShunned(); } } } private static class Task implements Runnable { Exchanger exchanger; MuxChannel channel; Event evt; FIFOMessageQueue queue; Address sender; String dest; Task(MuxChannel channel, Event evt, FIFOMessageQueue queue, Address sender, String dest, boolean result_expected) { this.channel=channel; this.evt=evt; this.queue=queue; this.sender=sender; this.dest=dest; if(result_expected) exchanger=new Exchanger(); } public void run() { Object retval; try { retval=channel.up(evt); if(exchanger != null) exchanger.exchange(retval); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // let the thread pool handle the interrupt - we're done anyway } finally { queue.done(sender, dest); } } } private static class ExecuteTask implements Runnable { FIFOMessageQueue queue; public ExecuteTask(FIFOMessageQueue queue) { this.queue=queue; } public void run() { try { Runnable task=queue.take(); task.run(); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } } } } libjgroups2.6-java-2.6.15.GA.orig/src/org/jgroups/mux/ServiceInfo.java0000644000175000017500000000774011366547366025305 0ustar twernertwernerpackage org.jgroups.mux; import org.jgroups.Address; import org.jgroups.Global; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; /** * Class used for service state communication between Multiplexers * @author Bela Ban * @version $Id: ServiceInfo.java,v 1.7.2.1 2008/01/16 09:15:14 vlada Exp $ */ public class ServiceInfo implements Externalizable, Streamable { public static final byte SERVICE_UP = 3; public static final byte SERVICE_DOWN = 4; public static final byte LIST_SERVICES_RSP = 5; // list of services available on a given node (available in 'state') public static final byte ACK = 6; public static final byte SERVICES_MERGED = 7; byte type=0; String service=null; Address host=null; byte[] state=null; public ServiceInfo() { } public ServiceInfo(byte type, String service, Address host, byte[] state) { this.type=type; this.service=service; this.host=host; this.state=state; } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); out.writeUTF(service); out.writeObject(host); if(state != null) { out.writeInt(state.length); out.write(state); } else { out.writeInt(0); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); service=in.readUTF(); host=(Address)in.readObject(); int len=in.readInt(); if(len > 0) { state=new byte[len]; in.readFully(state, 0, len); } } public long size() { long retval=Global.BYTE_SIZE; // type retval+=Global.BYTE_SIZE; // presence byte for service if(service != null) retval+=service.length() +2; retval+=Util.size(host); retval+=Global.INT_SIZE; // length of state if(state != null) retval+=state.length; return retval; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); Util.writeString(service, out); Util.writeAddress(host, out); if(state != null) { out.writeInt(state.length); out.write(state, 0, state.length); } else { out.writeInt(0); } } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); service=Util.readString(in); host=Util.readAddress(in); int len=in.readInt(); if(len > 0) { state=new byte[len]; in.readFully(state, 0, len); } } public String toString() { switch(type) { case SERVICE_UP: return "SERVICE_UP(" + service + "," + host + ")"; case SERVICE_DOWN: return "SERVICE_DOWN(" + service + "," + host + ")"; case ACK: return "ACK"; case SERVICES_MERGED: return "SERVICES_MERGED("+ host + ")"; case LIST_SERVICES_RSP: String services=null; try { services=Util.objectFromByteBuffer(state).toString(); } catch(Exception e) { } return "LIST_SERVICES_RSP(" + services + ")"; default: return "n/a"; } } public static String typeToString(int t) { switch(t) { case SERVICE_UP: return "SERVICE_UP"; case SERVICE_DOWN: return "SERVICE_DOWN"; case ACK: return "ACK"; case SERVICES_MERGED: return "SERVICES_MERGED"; case LIST_SERVICES_RSP: return "LIST_SERVICES_RSP"; default: return "n/a"; } } } libjgroups2.6-java-2.6.15.GA.orig/build.properties.template0000644000175000017500000000107611366547366023371 0ustar twernertwerner# add your own properties in here # the network interface (NIC) which will be used by the unit tests, change this to # the one you want to use. Note that 'localhost' usually resolved to 127.0.0.1, # which may not work on Linux unless you have a multicast route set up for loopback jgroups.bind_addr=localhost jgroups.tcpping.initial_hosts=localhost[7800] jgroups.udp.mcast_addr=232.15.15.15 jgroups.udp.mcast_port=45588 jgroups.udp.ip_ttl=5 jgroups.tunnel.router_host=localhost jgroups.tunnel.router_port=12001 # use true for IPv6 and false for IPv4 jgroups.useIPv6=false libjgroups2.6-java-2.6.15.GA.orig/build.xml0000644000175000017500000007752311366547366020175 0ustar twernertwerner build.xml file for JGroups. Needs Ant (jakarta.apache.org) to run @{text} Using set: @{set} @{text} Using set: @{set} Tests running with parameters: channel.conf=${channel.conf} channel.conf.flush=${channel.conf.flush} testname.ext=${testname.ext} usingIPv6=${jgroups.useIPv6} Tests running with parameters: channel.conf=${channel.conf} channel.conf.flush=${channel.conf.flush} threadcount=${threadcount} test.verbose=${test.verbose} testname.ext=${testname.ext} formatter.class=${formatter.class} junit.excludes=${junit.excludes} usingIPv6=${jgroups.useIPv6} libjgroups2.6-java-2.6.15.GA.orig/README0000644000175000017500000000650411366547366017223 0ustar twernertwerner// $Id: README,v 1.7 2006/08/09 13:08:02 belaban Exp $ JGroups - A Framework for Group Communication in Java ======================================================== March 3, 1998 Bela Ban 4114 Upson Hall Cornell University Ithaca, NY 14853 bba@cs.cornell.edu belaban@yahoo.com JGroups is a Java package for reliable group communication. It consists of 3 parts: (1) a socket-like API for application development, (2) a protocol stack, which implements reliable communication, and (3) a set of building blocks, which give the application/protocol programmer high-level abstractions (e.g. DistributedHashtable, derived from java.util.Hashtable, which is similar to Linda/JavaSpaces). The API (a channel) looks like a socket: there a methods for joining and leaving groups, sending and receiving messages to/from members, getting the shared group state, and registering for notifications when a member joins, or an existing member leaves or crashes. The protocol stack is a linked list of protocols, through which each message has to be passed. Each protocol implements an up() and down() method, and may modify, reorder, encrypt, fragment/unfragment, drop a message, or pass it up/down unchanged. The protocol stack is created according to a specification given when a channel is created. New protocols can be plugged into the stack easily. Building blocks hide the channel and provide a higher abstraction. Example: DistributedHashtable is a subclass of java.util.Hashtable and overrides all methods that change the hashtable (clear, put, remove). Those methods are invoked on all hashtables in the same group simultaneously, so that all hashtables have the same state. A new hashtable uses a state transfer protocol to initially obtain the shared group state from an existing member. This enables replication of data structures across process boundaries. Group communication is important in the following situations: - A service has to be replicated for availability. As long as at least one of the servers remains operational, the service itself remains operational - Service requests have to be balanced between a set of servers - A large number of objects have to be managed as one entity (e.g. a management domain) - Notification service / push technology: receivers subscribe to a channel, senders send data to the channels, channels distribute data to all receivers subscribed to the channel (see iBus, CastaNet etc.). Used for example for video distribution, videoconferencing JGroups deliberately models a rather low-level message-oriented middleware (MOM) model. The reason is that we don't want to impose a one-size-fits-all model on the programmer, who usually will want to extend the model in various (previously unconceived) ways anyway. Providing low level Java classes allows the programmer to extend/replace classes at will, as the granularity of the system is finer. JGroups can also be used for the construction of higher level toolkits/frameworks. Such frameworks should provide a certain transparency, without, however, preventing extensions to be made. The principle of creating partly 'opened-up' black boxes is called Open Implementation (OI, http://www.parc.xerox.com/spl/projects/oi/) and will be applied both to JGroups and to a further higher level toolkit. libjgroups2.6-java-2.6.15.GA.orig/LICENSE0000644000175000017500000006347611366547366017363 0ustar twernertwerner GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! libjgroups2.6-java-2.6.15.GA.orig/tests/0000755000175000017500000000000011621261110017446 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/stress/0000755000175000017500000000000011621261110020771 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/0000755000175000017500000000000011621261110021560 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/0000755000175000017500000000000011621261110023251 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/tests/0000755000175000017500000000000011621261110024413 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/tests/NetworkUtilization.java0000644000175000017500000000746611366547366031212 0ustar twernertwerner// $Id: NetworkUtilization.java,v 1.4 2004/07/05 14:15:22 belaban Exp $ package org.jgroups.tests; import org.jgroups.util.Util; import java.net.*; /** * @author Bela Ban */ public class NetworkUtilization { boolean sender=false; InetAddress mcast_addr; int mcast_port=7500; long start, stop; long num_received=0; int received_packet_size=0; MulticastSocket mcast_sock=null; class MyTimer extends Thread { MyTimer() { } public void run() { if(mcast_sock != null) mcast_sock.close(); long stop=System.currentTimeMillis(); long diff=stop-start; double secs=diff/1000.0; double num_kb_received=num_received * received_packet_size / 1000.0; Util.sleep(200); System.out.println("** took " + secs + " secs to receive " + num_received + " msgs (" + (num_received / secs) + " msgs/sec)\n" + "** throughput: " + num_kb_received / secs + " KB/sec"); } } byte[] createBuffer(int size) { byte[] buf=new byte[size]; for(int i=0; i < buf.length; i++) { buf[i]='.'; } return buf; } public void start(boolean sender, int packet_size) throws Exception { this.sender=sender; mcast_addr=InetAddress.getByName("228.8.8.8"); if(sender) { DatagramSocket sock=new DatagramSocket(); byte[] buf=createBuffer(packet_size); System.out.println("-- starting to send packets"); while(true) { DatagramPacket p=new DatagramPacket(buf, buf.length, mcast_addr, mcast_port); sock.send(p); } } else { Runtime.getRuntime().addShutdownHook(new MyTimer()); byte[] buf=new byte[1000000]; boolean first=true; DatagramPacket p=new DatagramPacket(buf, buf.length); mcast_sock=new MulticastSocket(mcast_port); // sock.setLoopbackMode(true); // disable reception of own mcasts mcast_sock.joinGroup(mcast_addr); System.out.println("-- joined group " + mcast_addr + ':' + mcast_port + ", waiting for packets\n" + "(press ctrl-c to kill)"); while(true) { p.setData(buf); try { mcast_sock.receive(p); } catch(SocketException ex) { break; } if(first) { first=false; start=System.currentTimeMillis(); received_packet_size=p.getLength(); } num_received++; //System.out.println("-- received " + p.getLength() + " bytes from " + p.getAddress() + ":" + p.getPort()); if(num_received % 1000 == 0) System.out.println(num_received); } } } public static void main(String[] args) { boolean sender=false; int packet_size=10; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-sender".equals(args[i])) { sender=true; continue; } if("-size".equals(args[i])) { packet_size=Integer.parseInt(args[++i]); continue; } help(); return; } try { new NetworkUtilization().start(sender, packet_size); } catch(Exception e) { e.printStackTrace(); } } static void help() { System.out.println("NetworkUtilization [-help] [-sender] [-size ]"); } } libjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/tests/NAKACKTest2.java0000644000175000017500000000612011366547366027161 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; /** * Tests the "NAKACK retransmit message lost" problem. Start 2 members, then a third one, and you should never see * the problem with messages than cannot be retransmitted because they were already garbage-collected. * @author Bela Ban Apr 4, 2004 * @version $Id: NAKACKTest2.java,v 1.4 2004/07/05 14:15:22 belaban Exp $ */ public class NAKACKTest2 { Channel ch; Address local_addr; Receiver receiver; RpcDispatcher disp; class Receiver extends Thread { public void run() { Object obj; Message msg; boolean running=true; while(running) { try { obj=ch.receive(0); if(obj instanceof Message) { msg=(Message)obj; System.out.println(msg.getSrc() + "::" + msg.getObject()); } else System.out.println("received " + obj); } catch(ChannelNotConnectedException e) { running=false; } catch(ChannelClosedException e) { running=false; } catch(TimeoutException e) { ; } } } } public void receive(Address sender, Long i) { System.out.println(sender + "::" + i); } void start(String props, boolean use_rpc) throws Exception { long i=0; Message msg; ch=new JChannel(props); if(use_rpc) disp=new RpcDispatcher(ch, null, null, this); ch.connect("NAKACKTest"); local_addr=ch.getLocalAddress(); if(use_rpc == false) { receiver=new Receiver(); receiver.start(); } while(true) { // for(int j=0; j < 10000; j++) { if(use_rpc) { disp.callRemoteMethods(null, "receive", new Object[]{local_addr, new Long(i++)}, new Class[]{Address.class, Long.class}, GroupRequest.GET_ALL, 10000); } else { msg=new Message(null, null, new Long(i++)); ch.send(msg); } //Util.sleep(1); } // Util.sleep(3600000); } public static void main(String[] args) { String props=null; boolean use_rpc=false; for(int i=0; i < args.length; i++) { if("-props".equals(args[i])) { props=args[++i]; continue; } if("-use_rpc".equals(args[i])) { use_rpc=true; continue; } help(); return; } try { new NAKACKTest2().start(props, use_rpc); } catch(Exception e) { e.printStackTrace(); } } private static void help() { System.out.println("NAKACKTest [-help] [-props properties] [-use_rpc]"); } } libjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/tests/MultiplexerStressTest.java0000644000175000017500000001315411366547366031672 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicLong; /** * Simple and experimental performance program for the Multiplexer. Runs 6 MuxChannels (2 clusters) inside the * same VM. Not optimal, but the main reason was to see whether the Multiplexer falls apart when we stress test it * (seems not to be the case). * @author Bela Ban * @version $Id: MultiplexerStressTest.java,v 1.1 2007/03/05 16:21:22 belaban Exp $ */ public class MultiplexerStressTest { Channel c11, c12, c21, c22, c31, c32; ChannelFactory f1, f2, f3; private MyReceiver r11, r12, r21, r22, r31, r32; static final int NUM_MSGS=100000; static final int SIZE=1000; public MultiplexerStressTest() { } private void start() throws Exception { CyclicBarrier barrier=new CyclicBarrier(7); f1=new JChannelFactory(); f2=new JChannelFactory(); f3=new JChannelFactory(); f1.setMultiplexerConfig("stacks.xml"); f2.setMultiplexerConfig("stacks.xml"); f3.setMultiplexerConfig("stacks.xml"); c11=f1.createMultiplexerChannel("udp", "A"); c11.connect("X"); r11=new MyReceiver(barrier); c11.setReceiver(r11); c12=f1.createMultiplexerChannel("udp", "B"); c12.connect("X"); r12=new MyReceiver(barrier); c12.setReceiver(r12); c21=f2.createMultiplexerChannel("udp", "A"); c21.connect("X"); r21=new MyReceiver(barrier); c21.setReceiver(r21); c22=f2.createMultiplexerChannel("udp", "B"); c22.connect("X"); r22=new MyReceiver(barrier); c22.setReceiver(r22); c31=f3.createMultiplexerChannel("udp", "A"); c31.connect("X"); r31=new MyReceiver(barrier); c31.setReceiver(r31); c32=f3.createMultiplexerChannel("udp", "B"); c32.connect("X"); r32=new MyReceiver(barrier); c32.setReceiver(r32); long start, stop; new MySender(barrier, c11).start(); new MySender(barrier, c12).start(); new MySender(barrier, c21).start(); new MySender(barrier, c22).start(); new MySender(barrier, c31).start(); new MySender(barrier, c32).start(); barrier.await(); // start the 6 sender threads start=System.currentTimeMillis(); barrier.await(); // results from the 6 receivers stop=System.currentTimeMillis(); System.out.println("Cluster A:\n" + printStats(stop-start,new MyReceiver[]{r11,r21,r31})); System.out.println("Cluster B:\n" + printStats(stop-start,new MyReceiver[]{r12,r22,r32})); c32.close(); c31.close(); c21.close(); c22.close(); c12.close(); c11.close(); } private String printStats(long total_time, MyReceiver[] cluster) { int num_msgs=0; long num_bytes=0; int cluster_size=cluster.length; for(int i=0; i < cluster_size; i++) { num_msgs+=cluster[i].getNumMessages(); num_bytes+=cluster[i].getNumBytes(); } double msgs_per_sec=num_msgs / (total_time / 1000.00); double bytes_per_sec=num_bytes * SIZE / (total_time / 1000.00); StringBuilder sb=new StringBuilder(); sb.append("total msgs=").append(num_msgs).append(", msg rate=").append(msgs_per_sec); sb.append(", total time=").append(total_time / 1000.00); sb.append(", throughput=").append(Util.printBytes(bytes_per_sec)); return sb.toString(); } private static class MyReceiver extends ReceiverAdapter { AtomicLong received_msgs=new AtomicLong(0); AtomicLong received_bytes=new AtomicLong(0); CyclicBarrier barrier; int print=NUM_MSGS / 10; public MyReceiver(CyclicBarrier barrier) { this.barrier=barrier; } public long getNumMessages() { return received_msgs.get(); } public long getNumBytes() { return received_bytes.get(); } public void receive(Message msg) { int length=msg.getLength(); if(length > 0) { received_msgs.incrementAndGet(); received_bytes.addAndGet(length); if(received_msgs.get() % print == 0) System.out.println("received " + received_msgs.get() + " msgs"); if(received_msgs.get() >= NUM_MSGS) { try { barrier.await(); } catch(Exception e) { e.printStackTrace(); } } } } } private static class MySender extends Thread { CyclicBarrier barrier; Channel ch; public MySender(CyclicBarrier barrier, Channel ch) { this.barrier=barrier; this.ch=ch; } public void run() { byte[] buf=new byte[SIZE]; Message msg; int print=NUM_MSGS / 10; try { barrier.await(); for(int i=1; i <= NUM_MSGS; i++) { msg=new Message(null, null, buf, 0, buf.length); ch.send(msg); if(i % print == 0) System.out.println(getName() + ": sent " + i + " msgs"); } } catch(Exception e) { e.printStackTrace(); } } } public static void main(String[] args) throws Exception { new MultiplexerStressTest().start(); } } libjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/tests/UnicastContentionTest.java0000644000175000017500000001542511366547366031626 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.util.Util; import java.util.concurrent.CountDownLatch; import java.util.Vector; import java.util.Map; import java.text.NumberFormat; /** * Tests contention of locks in UNICAST, by concurrently sending and receiving unicast messages. The contention is * in the 'connections' hashmap, and results in a lot of retransmissions. Run 2 instances with * java org.jgroups.tests.UnicastContentionTest -props udp.xml -num_msgs 100 -num_threads 200 * and the UNICAST.num_xmits value will be high * @author Bela Ban * @version $Id: UnicastContentionTest.java,v 1.1.2.5 2009/09/14 15:14:02 belaban Exp $ */ public class UnicastContentionTest { static final String GROUP="UnicastContentionTest-Cluster"; int num_msgs=10000; int size=1000; // bytes int num_mbrs=2; int num_threads=1; int MOD=1000; private static NumberFormat f; static { f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } private void start(String props, int num_msgs, int size, int num_mbrs, int num_threads, boolean dump_stats) throws Exception { this.num_msgs=num_msgs; this.size=size; this.num_mbrs=num_mbrs; this.num_threads=num_threads; this.MOD=num_threads * num_msgs / 10; MySender[] senders=new MySender[num_threads]; JChannel ch=new JChannel(props); JmxConfigurator.registerChannel(ch, Util.getMBeanServer(), "jgroups", GROUP, true); final CountDownLatch latch=new CountDownLatch(1); MyReceiver receiver=new MyReceiver(latch); ch.setReceiver(receiver); ch.connect(GROUP); System.out.println("Waiting for " + num_mbrs + " members"); latch.await(); View view=ch.getView(); Address local_addr=ch.getLocalAddress(); Address dest=pickNextMember(view, local_addr); System.out.println("View is " + view + "\n" + num_threads + " threads are sending " + num_msgs + " messages (of " + size + " bytes) to " + dest); for(int i=0; i < senders.length; i++) senders[i]=new MySender(dest, ch); for(MySender sender: senders) sender.start(); for(MySender sender: senders) sender.join(); if(dump_stats) { Util.keyPress("enter to dump stats and close channel"); System.out.println("stats:\n" + printStats(ch.dumpStats("NAKACK")) + "\n" + printStats(ch.dumpStats("FC")) + "\n" + printStats(ch.dumpStats("UNICAST"))); } else Util.sleep(2000); Util.close(ch); } @SuppressWarnings("unchecked") private static String printStats(Map map) { StringBuilder sb=new StringBuilder(); for(Map.Entry entry: map.entrySet()) { sb.append(entry.getKey()).append("\n"); Map val=(Map)entry.getValue(); for(Map.Entry tmp: val.entrySet()) { sb.append(tmp.getKey()).append("=").append(tmp.getValue()).append("\n"); } sb.append("\n"); } return sb.toString(); } private static Address pickNextMember(View view, Address local_addr) { Vector
        mbrs=view.getMembers(); for(Address mbr: mbrs) { if(!mbr.equals(local_addr)) return mbr; } return null; } private class MySender extends Thread { final byte[] buf=new byte[size]; final Address dest; final JChannel ch; public MySender(Address dest, JChannel ch) { this.dest=dest; this.ch=ch; } public void run() { for(int i=0; i < num_msgs; i++) { Message msg=new Message(dest, null, buf); try { ch.send(msg); } catch(Exception e) { e.printStackTrace(); } } } } private class MyReceiver extends ReceiverAdapter { private final CountDownLatch latch; private int msgs=0, bytes=0; private long start=0; private int expected_msgs=num_msgs * num_threads; public MyReceiver(CountDownLatch latch) { this.latch=latch; } /** We receive a message. Doesn't need to be reentrant because only 1 sender sends us unicast messages */ public void receive(Message msg) { if(start == 0) start=System.currentTimeMillis(); msgs++; bytes+=msg.getLength(); if(msgs % MOD == 0) System.out.println("-- " + msgs + " received"); if(msgs >= expected_msgs) { long time=System.currentTimeMillis() - start; double msgs_sec=msgs / (time / 1000.0); double throughput=msgs_sec * size; System.out.println(new StringBuilder("-- received ").append(msgs).append(" messages") .append(" (" + time + " ms, " + f.format(msgs_sec) + " msgs/sec, " + Util.printBytes(throughput) + "/sec)")); } } public void viewAccepted(View new_view) { if(new_view.size() >= num_mbrs) latch.countDown(); } } public static void main(String[] args) throws Exception { int num_msgs=10000; int size=1000; //bytes int num_mbrs=2; int num_threads=1; String props=null; boolean dump_stats=false; for(int i=0; i < args.length; i++) { if(args[i].equals("-num_msgs")) { num_msgs=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-size")) { size=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-num_mbrs")) { num_mbrs=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-num_threads")) { num_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-dump_stats")) { dump_stats=true; continue; } help(); return; } new UnicastContentionTest().start(props, num_msgs, size, num_mbrs, num_threads, dump_stats); } private static void help() { System.out.println("UnicastStressTest2 [-props properties] [-num_msgs ]" + " [-size bytes] [-num_mbrs members]"); } } libjgroups2.6-java-2.6.15.GA.orig/tests/stress/org/jgroups/tests/UnicastStressTest.java0000644000175000017500000002204411366547366030764 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.Address; import org.jgroups.JChannel; import org.jgroups.View; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.util.Util; import java.util.Vector; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * Tests UNICAST by sending anycast messages via an RpcDispatcher * @author Bela Ban * @version $Id: UnicastStressTest.java,v 1.3 2007/04/24 13:57:19 belaban Exp $ */ public class UnicastStressTest { int num_channels=6; int num_threads=1; // number of threads per channel int num_msgs=1000; // number of messages sent by 1 thread int msg_size=4096; // number of bytes / message String props=null; int buddies=1; private JChannel[] channels; private RpcDispatcher[] dispatchers; private Receiver[] receivers; final AtomicInteger msgs_received=new AtomicInteger(0); final AtomicLong bytes_received=new AtomicLong(0); final CyclicBarrier start_barrier; final CyclicBarrier terminate_barrier; public UnicastStressTest(String props, int num_channels, int num_threads, int num_msgs, int msg_size, int buddies) { this.props=props; this.num_channels=num_channels; this.num_threads=num_threads; this.num_msgs=num_msgs; this.msg_size=msg_size; this.buddies=buddies; start_barrier=new CyclicBarrier(num_channels * num_threads +1); terminate_barrier=new CyclicBarrier(num_channels +1); if(buddies > num_channels) throw new IllegalArgumentException("buddies needs to be smaller than number of channels"); } private void start() throws Exception { channels=new JChannel[num_channels]; receivers=new Receiver[num_channels]; dispatchers=new RpcDispatcher[num_channels]; long start, stop; int num_expected_msgs=num_threads * num_msgs * buddies; int num_total_msgs=num_channels * num_threads * num_msgs; // over all channels for(int i=0; i < channels.length; i++) { channels[i]=new JChannel(props); receivers[i]=new Receiver(terminate_barrier, bytes_received, msgs_received, num_expected_msgs, num_total_msgs); dispatchers[i]=new RpcDispatcher(channels[i], null, null, receivers[i]); channels[i].connect("x"); } // start the senders for(int i=0; i < channels.length; i++) { JChannel channel=channels[i]; View view=channel.getView(); Vector
        members=view.getMembers(); if(members.size() != num_channels) { throw new Exception("cluster has not formed correctly, expected " + num_channels + " channels, found" + " only " + members.size() + " (view: " + view + ")"); } Vector
        tmp=pickBuddies(members, channel.getLocalAddress()); for(int j=0; j < num_threads; j++) { Sender sender=new Sender(start_barrier, msg_size, num_msgs, dispatchers[i], channel.getLocalAddress(), tmp); sender.start(); // will wait on barrier } } System.out.println("sending " + num_total_msgs + " msgs with " + num_threads + " threads over " + num_channels + " channels"); start_barrier.await(); // signals all senders to start start=System.currentTimeMillis(); terminate_barrier.await(); // when all receivers have received all messages stop=System.currentTimeMillis(); for(int i=0; i < dispatchers.length; i++) { dispatchers[i].stop(); } for(int i=channels.length -1; i >= 0; i--) { channels[i].close(); } printStats(stop - start); } private void printStats(long time) { for(int i=0; i < receivers.length; i++) { System.out.println("receiver #" + (i+1) + ": " + receivers[i].getNumReceivedMessages()); } System.out.println("total received messages for " + num_channels + " channels: " + msgs_received.get()); System.out.println("total bytes received by " + num_channels + " channels: " + Util.printBytes(bytes_received.get())); System.out.println("time: " + time + " ms"); double msgs_per_sec=msgs_received.get() / (time /1000.0); double throughput=bytes_received.get() / (time / 1000.0); System.out.println("Message rate: " + msgs_per_sec + " msgs/sec"); System.out.println("Throughput: " + Util.printBytes(throughput) + " / sec"); } private Vector
        pickBuddies(Vector
        members, Address local_addr) { Vector
        retval=new Vector
        (); int index=members.indexOf(local_addr); if(index < 0) return null; for(int i=index +1; i <= index + buddies; i++) { int real_index=i % members.size(); Address buddy=members.get(real_index); retval.add(buddy); } return retval; } public static class Receiver { final AtomicInteger msgs; final AtomicLong bytes; final int num_expected_msgs, num_total_msgs, print; final CyclicBarrier barrier; final AtomicInteger num_received_msgs=new AtomicInteger(0); public Receiver(CyclicBarrier barrier, AtomicLong bytes, AtomicInteger msgs, int num_expected_msgs, int num_total_msgs) { this.barrier=barrier; this.bytes=bytes; this.msgs=msgs; this.num_expected_msgs=num_expected_msgs; this.num_total_msgs=num_total_msgs; print=num_total_msgs / 10; } public int getNumReceivedMessages() {return num_received_msgs.get();} public void receive(byte[] data) { msgs.incrementAndGet(); bytes.addAndGet(data.length); int count=num_received_msgs.incrementAndGet(); if(count % print == 0) { System.out.println("received " + count + " msgs"); } if((count=num_received_msgs.get()) >= num_expected_msgs) { try { barrier.await(); } catch(Exception e) { } } } } private static class Sender extends Thread { private final CyclicBarrier barrier; private final int num_msgs; private final int msg_size; private final RpcDispatcher disp; private final Vector buddies; public Sender(CyclicBarrier barrier, int msg_size, int num_msgs, RpcDispatcher disp, Address local_addr, Vector buddies) { this.barrier=barrier; this.msg_size=msg_size; this.num_msgs=num_msgs; this.disp=disp; this.buddies=buddies; setName("Sender (" + local_addr + " --> " + buddies + ")"); } public void run() { final byte[] data=new byte[msg_size]; final Object[] arg=new Object[]{data}; final Class[] types=new Class[]{byte[].class}; try { barrier.await(); } catch(Exception e) { } for(int i=0; i < num_msgs; i++) { disp.callRemoteMethods(buddies, "receive", arg, types, GroupRequest.GET_NONE, 5000, true); } } } public static void main(String[] args) throws Exception { int num_channels=6; int num_threads=10; // number of threads per channel int num_msgs=10000; // number of messages sent by 1 thread int msg_size=4096; // number of bytes / message int buddies=1; String props=null; for(int i=0; i < args.length; i++) { if(args[i].equalsIgnoreCase("-props")) { props=args[++i]; continue; } if(args[i].equalsIgnoreCase("-num_channels")) { num_channels=Integer.parseInt(args[++i]); continue; } if(args[i].equalsIgnoreCase("-num_threads")) { num_threads=Integer.parseInt(args[++i]); continue; } if(args[i].equalsIgnoreCase("-num_msgs")) { num_msgs=Integer.parseInt(args[++i]); continue; } if(args[i].equalsIgnoreCase("-msg_size")) { msg_size=Integer.parseInt(args[++i]); continue; } if(args[i].equalsIgnoreCase("-buddies")) { buddies=Integer.parseInt(args[++i]); continue; } help(); return; } new UnicastStressTest(props, num_channels, num_threads, num_msgs, msg_size, buddies).start(); } private static void help() { System.out.println("UnicastStressTest [-help] [-props ] [-num_channels ] " + "[-num_threads ] [-num_msgs ] [-msg_size ] " + "[-buddies ]"); } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/0000755000175000017500000000000011621261110020402 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/0000755000175000017500000000000011621261110021171 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/0000755000175000017500000000000011621261110022662 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/0000755000175000017500000000000011621261110024024 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/0000755000175000017500000000000011621261110024760 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/Test.java0000644000175000017500000007272611366547366026612 0ustar twernertwernerpackage org.jgroups.tests.perf; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.Version; import org.jgroups.util.Util; import java.io.BufferedReader; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.BrokenBarrierException; /** You start the test by running this class. * @author Bela Ban (belaban@yahoo.com) */ public class Test implements Receiver { String props=null; Properties config; boolean sender=false; Transport transport=null; Object local_addr=null; /** Map members. Keys=member addresses, value=MemberInfo */ Map senders=new ConcurrentHashMap(10); /** Keeps track of members. ArrayList */ final ArrayList members=new ArrayList(); /** Set when first message is received */ long start=0; long last_interval=0; /** Set when last message is received */ long stop=0; int num_members=0; int num_senders=0; long num_msgs_expected=0; long num_msgs_received=0; // from everyone long num_bytes_received=0; // from everyone Log log=LogFactory.getLog(getClass()); boolean all_received=false; boolean final_results_received=false; /** Map. A hashmap of senders, each value is the 'senders' hashmap */ Map results=new HashMap(); private ResultsPublisher publisher=new ResultsPublisher(); final List heard_from=new ArrayList(); final List final_results_ok_list=new ArrayList(); boolean dump_transport_stats=false; /** Log every n msgs received */ long log_interval=1000; long counter=1; long msg_size=1000; boolean jmx=false; /** Number of ms to wait at the receiver to simulate processing of the received message (0 == don't wait) */ long processing_delay=0; final Set start_msgs_received=new HashSet(12); FileWriter output=null; static NumberFormat f; static { f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } public void start(Properties c, boolean verbose, boolean jmx, String output, int num_threads) throws Exception { String config_file="config.txt"; BufferedReader fileReader; String line; String key, val; StringTokenizer st; Properties tmp=new Properties(); long num_msgs=0; if(output != null) this.output=new FileWriter(output, false); config_file=c.getProperty("config"); fileReader=new BufferedReader(new FileReader(config_file)); while((line=fileReader.readLine()) != null) { if(line.startsWith("#")) continue; line=line.trim(); if(line.length() == 0) continue; st=new StringTokenizer(line, "=", false); key=st.nextToken().toLowerCase(); val=st.nextToken(); tmp.put(key, val); } fileReader.close(); // 'tmp' now contains all properties from the file, now we need to override the ones // passed to us by 'c' tmp.putAll(c); this.config=tmp; num_msgs=Integer.parseInt(config.getProperty("num_msgs")); if(num_threads > 0 && num_msgs % num_threads != 0) throw new IllegalArgumentException("num_msgs (" + num_msgs + ") must be devisible by num_threads (" + num_threads + ")"); StringBuilder sb=new StringBuilder(); sb.append("\n\n----------------------- TEST -----------------------\n"); sb.append("Date: ").append(new Date()).append('\n'); sb.append("Run by: ").append(System.getProperty("user.name")).append("\n\n"); if(verbose) sb.append("Properties: ").append(printProperties()).append("\n-------------------------\n\n"); for(Iterator it=this.config.entrySet().iterator(); it.hasNext();) { Map.Entry entry=(Map.Entry)it.next(); sb.append(entry.getKey()).append(":\t").append(entry.getValue()).append('\n'); } sb.append("JGroups version: ").append(Version.description).append('\n'); System.out.println("Configuration is: " + sb); output(sb.toString()); props=this.config.getProperty("props"); num_members=Integer.parseInt(this.config.getProperty("num_members")); num_senders=Integer.parseInt(this.config.getProperty("num_senders")); num_msgs=Long.parseLong(this.config.getProperty("num_msgs")); this.num_msgs_expected=num_senders * num_msgs; sender=Boolean.valueOf(this.config.getProperty("sender")).booleanValue(); msg_size=Long.parseLong(this.config.getProperty("msg_size")); String tmp2=this.config.getProperty("dump_transport_stats", "false"); if(Boolean.valueOf(tmp2).booleanValue()) this.dump_transport_stats=true; tmp2=this.config.getProperty("log_interval"); if(tmp2 != null) log_interval=Long.parseLong(tmp2); if(num_threads > 0 && log_interval % num_threads != 0) throw new IllegalArgumentException("log_interval (" + log_interval + ") must be divisible by num_threads (" + num_threads + ")"); sb=new StringBuilder(); sb.append("\n##### msgs_received"); sb.append(", current time (in ms)"); sb.append(", msgs/sec"); sb.append(", throughput/sec [KB]"); sb.append(", free_mem [KB] "); sb.append(", total_mem [KB] "); output(sb.toString()); if(jmx) { this.config.setProperty("jmx", "true"); } this.jmx=Boolean.valueOf(this.config.getProperty("jmx")).booleanValue(); String tmp3=this.config.getProperty("processing_delay"); if(tmp3 != null) this.processing_delay=Long.parseLong(tmp3); String transport_name=this.config.getProperty("transport"); transport=(Transport)Util.loadClass(transport_name, this.getClass()).newInstance(); transport.create(this.config); transport.setReceiver(this); transport.start(); local_addr=transport.getLocalAddress(); } private void output(String msg) { // if(log.isInfoEnabled()) // log.info(msg); if(this.output != null) { try { this.output.write(msg + "\n"); this.output.flush(); } catch(IOException e) { } } } private String printProperties() { StringBuilder sb=new StringBuilder(); Properties p=System.getProperties(); for(Iterator it=p.entrySet().iterator(); it.hasNext();) { Map.Entry entry=(Map.Entry)it.next(); sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n'); } return sb.toString(); } public void stop() { if(transport != null) { transport.stop(); transport.destroy(); } if(this.output != null) { try { this.output.close(); } catch(IOException e) { } } } public void receive(Object sender, byte[] payload) { if(payload == null || payload.length == 0) { System.err.println("payload is incorrect (sender=" + sender + "): " + payload); return; } try { int type=payload[0]; if(type == 1) { // DATA int len=payload.length -1; handleData(sender, len); return; } byte[] tmp=new byte[payload.length-1]; System.arraycopy(payload, 1, tmp, 0, tmp.length); Data d=(Data)Util.streamableFromByteBuffer(Data.class, tmp); switch(d.getType()) { case Data.DISCOVERY_REQ: // System.out.println("-- received discovery request"); sendDiscoveryResponse(); break; case Data.DISCOVERY_RSP: // System.out.println("-- received discovery response from " + sender); synchronized(this.members) { if(!this.members.contains(sender)) { this.members.add(sender); System.out.println("-- " + sender + " joined"); if(d.sender) { if(!this.senders.containsKey(sender)) { this.senders.put(sender, new MemberInfo(d.num_msgs)); } } this.members.notifyAll(); } } break; case Data.RESULTS: results.put(sender, d.result); heard_from.remove(sender); if(heard_from.isEmpty()) { for(int i=0; i < 3; i++) { sendFinalResults(); Util.sleep(300); } } break; case Data.FINAL_RESULTS: if(!final_results_received) { dumpResults(d.results); final_results_received=true; } boolean done=false; synchronized(final_results_ok_list) { final_results_ok_list.remove(sender); if(final_results_ok_list.isEmpty()) done=true; } if(done) { for(int i=0; i < 3; i++) { sendFinalResultsOk(); Util.sleep(300); } } break; case Data.FINAL_RESULTS_OK: publisher.stop(); synchronized(this) { this.notifyAll(); } break; case Data.START: synchronized(start_msgs_received) { if(start_msgs_received.add(sender)){ start_msgs_received.notifyAll(); } } break; default: log.error("received invalid data type: " + payload[0]); break; } } catch(Exception e) { e.printStackTrace(); } } private void handleData(Object sender, int num_bytes) { MemberInfo info=null; boolean do_sleep=false; synchronized(this) { if(all_received) return; if(start == 0) { start=last_interval=System.currentTimeMillis(); } num_msgs_received++; num_bytes_received+=num_bytes; if(num_msgs_received >= num_msgs_expected) { if(stop == 0) { stop=System.currentTimeMillis(); } all_received=true; } if(num_msgs_received % log_interval == 0) { long curr=System.currentTimeMillis(); long diff=curr - last_interval; last_interval=curr; double msgs_sec=log_interval / (diff / 1000.0); double throughput=msgs_sec * msg_size; System.out.println(new StringBuilder("-- received ").append(num_msgs_received).append(" messages") .append(" (" + diff + " ms, " + f.format(msgs_sec) + " msgs/sec, " + Util.printBytes(throughput) + "/sec)")); } if(counter % log_interval == 0) { output(dumpStats(counter)); } info=(MemberInfo)this.senders.get(sender); if(info != null) { if(info.start == 0) info.start=System.currentTimeMillis(); info.num_msgs_received++; counter++; info.total_bytes_received+=num_bytes; if(info.num_msgs_received >= info.num_msgs_expected) { info.done=true; if(info.stop == 0) info.stop=System.currentTimeMillis(); } else { if(processing_delay > 0) do_sleep=true; } } else { log.error("-- sender " + sender + " not found in senders hashmap"); } } if(do_sleep && processing_delay > 0) { Util.sleep(processing_delay); } synchronized(this) { if(all_received) { if(!this.sender) dumpSenders(); publisher.start(); } } } private void sendResults() throws Exception { Data d=new Data(Data.RESULTS); byte[] buf; MemberInfo info=new MemberInfo(num_msgs_expected); info.done=true; info.num_msgs_received=num_msgs_received; info.start=start; info.stop=stop; info.total_bytes_received=this.num_bytes_received; d.result=info; buf=generatePayload(d, null); transport.send(null, buf, true); } private void sendFinalResults() throws Exception { Data d=new Data(Data.FINAL_RESULTS); d.results=new ConcurrentHashMap(this.results); final byte[] buf=generatePayload(d, null); transport.send(null, buf, true); } private void sendFinalResultsOk() throws Exception { Data d=new Data(Data.FINAL_RESULTS_OK); final byte[] buf=generatePayload(d, null); transport.send(null, buf, true); } boolean allReceived() { return all_received; } boolean receivedFinalResults() { return final_results_received; } void sendMessages(long interval, int nanos, boolean busy_sleep, int num_threads) throws Exception { long total_msgs=0; int msgSize=Integer.parseInt(config.getProperty("msg_size")); int num_msgs=Integer.parseInt(config.getProperty("num_msgs")); byte[] buf=new byte[msgSize]; for(int k=0; k < msgSize; k++) buf[k]='.'; Data d=new Data(Data.DATA); byte[] payload=generatePayload(d, buf); StringBuilder sb=new StringBuilder(); sb.append("-- sending ").append(num_msgs).append(" ").append(Util.printBytes(msgSize)).append(" messages"); if(num_threads > 0) sb.append(" in ").append(num_threads).append(" threads"); System.out.println(sb); if(num_threads > 0) { int number=num_msgs / num_threads; long thread_interval=log_interval / num_threads; CyclicBarrier barrier=new CyclicBarrier(num_threads +1); Thread[] threads=new Thread[num_threads]; for(int i=0; i < threads.length; i++) { threads[i]=new Sender(barrier, number, payload, interval, nanos, busy_sleep, thread_interval); threads[i].setName("thread-" + (i+1)); threads[i].start(); } barrier.await(); } else { for(int i=0; i < num_msgs; i++) { transport.send(null, payload, false); total_msgs++; if(total_msgs % log_interval == 0) { System.out.println("++ sent " + total_msgs); } if(interval > 0 || nanos > 0) { if(busy_sleep) Util.sleep(interval, busy_sleep); else Util.sleep(interval, nanos); } } } } class Sender extends Thread { CyclicBarrier barrier; int num_msgs=0; byte[] payload; long total_msgs=0; long interval=0; boolean busy_sleep=false; int nanos=0; long thread_interval; Sender(CyclicBarrier barrier, int num_msgs, byte[] payload, long interval, int nanos, boolean busy_sleep, long thread_interval) { this.barrier=barrier; this.num_msgs=num_msgs; this.payload=payload; this.interval=interval; this.nanos=nanos; this.busy_sleep=busy_sleep; this.thread_interval=thread_interval; } public void run() { try { barrier.await(); } catch(InterruptedException e) { e.printStackTrace(); return; } catch(BrokenBarrierException e) { e.printStackTrace(); return; } System.out.println("-- [" + getName() + "] sending " + num_msgs + " msgs"); for(int i=0; i < num_msgs; i++) { try { transport.send(null, payload, false); total_msgs++; if(total_msgs % log_interval == 0) { System.out.println("++ sent " + total_msgs + " [" + getName() + "]"); } if(interval > 0 || nanos > 0) { if(busy_sleep) Util.sleep(interval, busy_sleep); else Util.sleep(interval, nanos); } } catch(Exception e) { } } } } byte[] generatePayload(Data d, byte[] buf) throws Exception { byte[] tmp=buf != null? buf : Util.streamableToByteBuffer(d); byte[] payload=new byte[tmp.length +1]; payload[0]=intToByte(d.getType()); System.arraycopy(tmp, 0, payload, 1, tmp.length); return payload; } private byte intToByte(int type) { switch(type) { case Data.DATA: return 1; case Data.DISCOVERY_REQ: return 2; case Data.DISCOVERY_RSP: return 3; case Data.RESULTS: return 4; case Data.FINAL_RESULTS: return 5; default: return 0; } } private void dumpResults(Map final_results) { Object member; Map.Entry entry; MemberInfo val; double combined_msgs_sec, tmp=0; long combined_tp; StringBuilder sb=new StringBuilder(); sb.append("\n-- results:\n"); for(Iterator it=final_results.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); member=entry.getKey(); val=(MemberInfo)entry.getValue(); tmp+=val.getMessageSec(); sb.append("\n").append(member); if(member.equals(local_addr)) sb.append(" (myself)"); sb.append(":\n"); sb.append(val); sb.append('\n'); } combined_msgs_sec=tmp / final_results.size(); combined_tp=(long)combined_msgs_sec * msg_size; sb.append("\ncombined: ").append(f.format(combined_msgs_sec)). append(" msgs/sec averaged over all receivers (throughput=" + Util.printBytes(combined_tp) + "/sec)\n"); System.out.println(sb.toString()); output(sb.toString()); } private void dumpSenders() { StringBuilder sb=new StringBuilder(); dump(this.senders, sb); System.out.println(sb.toString()); } private void dump(Map map, StringBuilder sb) { Map.Entry entry; Object mySender; MemberInfo mi; MemberInfo combined=new MemberInfo(0); combined.start = Long.MAX_VALUE; combined.stop = Long.MIN_VALUE; sb.append("\n-- local results:\n"); for(Iterator it2=map.entrySet().iterator(); it2.hasNext();) { entry=(Map.Entry)it2.next(); mySender=entry.getKey(); mi=(MemberInfo)entry.getValue(); combined.start=Math.min(combined.start, mi.start); combined.stop=Math.max(combined.stop, mi.stop); combined.num_msgs_expected+=mi.num_msgs_expected; combined.num_msgs_received+=mi.num_msgs_received; combined.total_bytes_received+=mi.total_bytes_received; sb.append("sender: ").append(mySender).append(": ").append(mi).append('\n'); } } private String dumpStats(long received_msgs) { double msgs_sec, throughput_sec; long current; StringBuilder sb=new StringBuilder(); sb.append(received_msgs).append(' '); current=System.currentTimeMillis(); sb.append(current).append(' '); msgs_sec=received_msgs / ((current - start) / 1000.0); throughput_sec=msgs_sec * msg_size; sb.append(f.format(msgs_sec)).append(' ').append(f.format(throughput_sec)).append(' '); sb.append(Runtime.getRuntime().freeMemory() / 1000.0).append(' '); sb.append(Runtime.getRuntime().totalMemory() / 1000.0); if(dump_transport_stats) { Map stats=transport.dumpStats(); if(stats != null) { print(stats, sb); } } return sb.toString(); } public String dumpTransportStats() { Map stats=transport.dumpStats(); StringBuilder sb=new StringBuilder(128); if(stats != null) { Map.Entry entry; String key; Map value; for(Iterator it=stats.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=(String)entry.getKey(); value=(Map)entry.getValue(); sb.append("\n").append(key).append(":\n"); for(Iterator it2=value.entrySet().iterator(); it2.hasNext();) { sb.append(it2.next()).append("\n"); } } } return sb.toString(); } private void print(Map stats, StringBuilder sb) { sb.append("\nTransport stats:\n\n"); Map.Entry entry; Object key, val; for(Iterator it=stats.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); val=entry.getValue(); sb.append(key).append(": ").append(val).append("\n"); } } void runDiscoveryPhase() throws Exception { sendDiscoveryRequest(); sendDiscoveryResponse(); System.out.println("-- waiting for " + num_members + " members to join"); boolean received_all_discovery_rsps=false; while(!received_all_discovery_rsps) { synchronized(members) { received_all_discovery_rsps=members.size() >= num_members; if(!received_all_discovery_rsps) members.wait(2000); } if(!received_all_discovery_rsps) { sendDiscoveryRequest(); sendDiscoveryResponse(); } } synchronized(members) { heard_from.addAll(members); // System.out.println("-- members: " + members.size()); } synchronized(final_results_ok_list) { final_results_ok_list.addAll(members); } } void waitForAllOKs() throws Exception { // System.out.println("-- waiting for " + num_members + " START messages"); sendStarted(); boolean received_all_start_msgs=false; while(!received_all_start_msgs) { synchronized(start_msgs_received) { received_all_start_msgs=start_msgs_received.size() >= num_members; if(!received_all_start_msgs) start_msgs_received.wait(2000); } if(!received_all_start_msgs) { sendStarted(); } } System.out.println("-- READY (" + start_msgs_received.size() + " acks)\n"); } void sendStarted() throws Exception { transport.send(null, generatePayload(new Data(Data.START), null), true); } void sendDiscoveryRequest() throws Exception { Data d=new Data(Data.DISCOVERY_REQ); // System.out.println("-- sending discovery request"); transport.send(null, generatePayload(d, null), true); } void sendDiscoveryResponse() throws Exception { final Data d2=new Data(Data.DISCOVERY_RSP); if(sender) { d2.sender=true; d2.num_msgs=Long.parseLong(config.getProperty("num_msgs")); } transport.send(null, generatePayload(d2, null), true); } public static void main(String[] args) { Properties config=new Properties(); boolean sender=false, verbose=false, jmx=false, dump_stats=false; // dumps at end of run Test t=null; String output=null; long interval=0; int interval_nanos=0; boolean busy_sleep=false; int num_threads=0; for(int i=0; i < args.length; i++) { if("-bind_addr".equals(args[i])) { String bind_addr=args[++i]; System.setProperty("jgroups.bind_addr", bind_addr); continue; } if("-sender".equals(args[i])) { config.put("sender", "true"); sender=true; continue; } if("-receiver".equals(args[i])) { config.put("sender", "false"); sender=false; continue; } if("-config".equals(args[i])) { String config_file=args[++i]; config.put("config", config_file); continue; } if("-props".equals(args[i])) { String props=args[++i]; config.put("props", props); continue; } if("-verbose".equals(args[i])) { verbose=true; continue; } if("-jmx".equals(args[i])) { jmx=true; continue; } if("-dump_stats".equals(args[i])) { dump_stats=true; continue; } if("-num_threads".equals(args[i])) { num_threads=Integer.parseInt(args[++i]); continue; } if("-interval".equals(args[i])) { interval=Long.parseLong(args[++i]); continue; } if("-nanos".equals(args[i])) { interval_nanos=Integer.parseInt(args[++i]); continue; } if("-busy_sleep".equals(args[i])) { busy_sleep=true; continue; } if("-f".equals(args[i])) { output=args[++i]; continue; } help(); return; } try { t=new Test(); t.start(config, verbose, jmx, output, num_threads); t.runDiscoveryPhase(); t.waitForAllOKs(); if(sender) { t.sendMessages(interval, interval_nanos, busy_sleep, num_threads); } synchronized(t) { while(t.receivedFinalResults() == false) { t.wait(2000); } } if(dump_stats) { String stats=t.dumpTransportStats(); System.out.println("\nTransport statistics:\n" + stats); } if(t.jmx) { System.out.println("jmx=true: not terminating"); if(t != null) { t.stop(); t=null; } while(true) { Util.sleep(60000); } } } catch(Exception e) { e.printStackTrace(); } finally { if(t != null) { t.stop(); } } } static void help() { System.out.println("Test [-help] ([-sender] | [-receiver]) " + "[-config ] [-num_threads ]" + "[-props ] [-verbose] [-jmx] [-bind_addr " + "[-dump_stats] [-f ] [-interval ] " + "[-nanos ] [-busy_sleep (cancels out -nanos)]"); } private class ResultsPublisher implements Runnable { static final long interval=1000; boolean running=true; volatile Thread t; void start() { if(t == null) { t=new Thread(this, "ResultsPublisher"); t.setDaemon(true); t.start(); } } void stop() { if(t != null && t.isAlive()) { Thread tmp=t; t=null; if(tmp != null) tmp.interrupt(); } } public void run() { try { while(t != null) { sendResults(); Util.sleep(interval); } } catch(Exception e) { e.printStackTrace(); } } } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/Data.java0000644000175000017500000001226511366547366026534 0ustar twernertwernerpackage org.jgroups.tests.perf; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.*; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Data sent around between members * @author Bela Ban Jan 22 * @author 2004 * @version $Id: Data.java,v 1.11.4.1 2009/12/17 16:30:06 belaban Exp $ */ public class Data implements Streamable { final static byte DISCOVERY_REQ = 1; final static byte DISCOVERY_RSP = 2; final static byte DATA = 3; final static byte RESULTS = 4; // sent when a receiver has received all messages final static byte FINAL_RESULTS = 5; // sent when a sender is done final static byte FINAL_RESULTS_OK = 6; // sent when we know the everyone has received FINAL_MSGS final static byte START = 7; // start sending messages public Data() { ; } public Data(byte type) { this.type=type; } byte type=0; byte[] payload=null; // used with DATA boolean sender=false; // used with DISCOVERY_RSP long num_msgs=0; // used with DISCOVERY_RSP MemberInfo result=null; // used with RESULTS Map results=null; // used with final results public int getType() { return type; } public void writeTo(DataOutputStream out) throws IOException { out.writeByte(type); if(payload != null) { out.writeBoolean(true); out.writeInt(payload.length); out.write(payload, 0, payload.length); } else out.writeBoolean(false); out.writeBoolean(sender); out.writeLong(num_msgs); Util.writeStreamable(result, out); if(results != null) { out.writeBoolean(true); out.writeInt(results.size()); Map.Entry entry; Object key; MemberInfo val; for(Iterator it=results.entrySet().iterator(); it.hasNext();) { entry=(Map.Entry)it.next(); key=entry.getKey(); val=(MemberInfo)entry.getValue(); try { Util.writeObject(key, out); } catch(Exception e) { IOException ex=new IOException("failed to write object " + key); ex.initCause(e); throw ex; } Util.writeStreamable(val, out); } } else out.writeBoolean(false); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { type=in.readByte(); if(in.readBoolean()) { int length=in.readInt(); payload=new byte[length]; in.readFully(payload, 0, length); } sender=in.readBoolean(); num_msgs=in.readLong(); result=(MemberInfo)Util.readStreamable(MemberInfo.class, in); if(in.readBoolean()) { int length=in.readInt(); results=new HashMap(length); Object key; MemberInfo val; for(int i=0; i < length; i++) { try { key=Util.readObject(in); } catch(Exception e) { IOException ex=new IOException("failed to read key"); ex.initCause(e); throw ex; } val=(MemberInfo)Util.readStreamable(MemberInfo.class, in); results.put(key, val); } } } public void writeExternal(ObjectOutput out) throws IOException { out.writeByte(type); if(payload != null) { out.writeInt(payload.length); out.write(payload, 0, payload.length); } else { out.writeInt(0); } out.writeBoolean(sender); out.writeLong(num_msgs); if(results != null) { out.writeBoolean(true); out.writeObject(results); } else out.writeBoolean(false); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type=in.readByte(); int len=in.readInt(); if(len > 0) { payload=new byte[len]; in.readFully(payload, 0, payload.length); } sender=in.readBoolean(); num_msgs=in.readLong(); boolean results_available=in.readBoolean(); if(results_available) results=(Map)in.readObject(); } public String toString() { StringBuilder sb=new StringBuilder(); sb.append('['); switch(type) { case DISCOVERY_REQ: sb.append("DISCOVERY_REQ"); break; case DISCOVERY_RSP: sb.append("DISCOVERY_RSP"); break; case DATA: sb.append("DATA"); break; case RESULTS: sb.append("RESULTS"); break; case FINAL_RESULTS: sb.append("FINAL_RESULTS"); break; case FINAL_RESULTS_OK: sb.append("FINAL_RESULTS_OK"); break; case START: sb.append("START"); break; default: sb.append(""); break; } sb.append("] "); return sb.toString(); } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/Receiver.java0000644000175000017500000000035211366547366027421 0ustar twernertwernerpackage org.jgroups.tests.perf; /** * @author Bela Ban Jan 22 * @author 2004 * @version $Id: Receiver.java,v 1.1 2004/01/23 00:08:31 belaban Exp $ */ public interface Receiver { void receive(Object sender, byte[] payload); } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/MemberInfo.java0000644000175000017500000001022411366547366027677 0ustar twernertwernerpackage org.jgroups.tests.perf; import org.jgroups.util.Streamable; import org.jgroups.util.Util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.text.NumberFormat; /** * @author Bela Ban Jan 22 * @author 2004 * @version $Id: MemberInfo.java,v 1.8.6.1 2008/01/22 10:01:14 belaban Exp $ */ public class MemberInfo implements Streamable { public long start=0; public long stop=0; public long num_msgs_expected=0; public long num_msgs_received=0; boolean done=false; long total_bytes_received=0; static NumberFormat f; static { f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } public MemberInfo() { } public MemberInfo(long num_msgs_expected) { this.num_msgs_expected=num_msgs_expected; } public double getMessageSec() { long total_time=stop-start; return num_msgs_received / (total_time/1000.0); } // public String toString() { // StringBuilder sb=new StringBuilder(); // double msgs_sec, throughput_kb=0, throughput_mb=0, kb_received=0, mb_received=0; // long total_time=stop-start; // double loss_rate=0; // long missing_msgs=num_msgs_expected - num_msgs_received; // kb_received=total_bytes_received/1000.0; // if(kb_received >= 1000) // mb_received=kb_received / 1000.0; // msgs_sec=num_msgs_received / (total_time/1000.0); // throughput_kb=kb_received / (total_time / 1000.0); // if(throughput_kb >= 1000) // throughput_mb=throughput_kb / 1000.0; // loss_rate=missing_msgs >= num_msgs_expected? 100.0 : (100.0 / num_msgs_expected) * missing_msgs; // sb.append("num_msgs_expected=").append(num_msgs_expected).append(", num_msgs_received="); // sb.append(num_msgs_received); // sb.append(" (loss rate=").append(loss_rate).append("%)"); // if(mb_received > 0) // sb.append(", received=").append(f.format(mb_received)).append("MB"); // else // sb.append(", received=").append(f.format(kb_received)).append("KB"); // sb.append(", time=").append(f.format(total_time)).append("ms"); // sb.append(", msgs/sec=").append(f.format(msgs_sec)); // if(throughput_mb > 0) // sb.append(", throughput=").append(f.format(throughput_mb)).append("MB/sec"); // else // sb.append(", throughput=").append(f.format(throughput_kb)).append("KB/sec"); // return sb.toString(); // } public String toString() { StringBuilder sb=new StringBuilder(); double msgs_sec, throughput=0; long total_time=stop-start; double loss_rate=0; long missing_msgs=num_msgs_expected - num_msgs_received; msgs_sec=num_msgs_received / (total_time/1000.0); throughput=total_bytes_received / (total_time / 1000.0); loss_rate=missing_msgs >= num_msgs_expected? 100.0 : (100.0 / num_msgs_expected) * missing_msgs; sb.append("num_msgs_expected=").append(num_msgs_expected).append(", num_msgs_received="); sb.append(num_msgs_received); sb.append(" (loss rate=").append(loss_rate).append("%)"); sb.append(", received=").append(Util.printBytes(total_bytes_received)); sb.append(", time=").append(f.format(total_time)).append("ms"); sb.append(", msgs/sec=").append(f.format(msgs_sec)); sb.append(", throughput=").append(Util.printBytes(throughput)); return sb.toString(); } public void writeTo(DataOutputStream out) throws IOException { out.writeLong(start); out.writeLong(stop); out.writeLong(num_msgs_expected); out.writeLong(num_msgs_received); out.writeBoolean(done); out.writeLong(total_bytes_received); } public void readFrom(DataInputStream in) throws IOException, IllegalAccessException, InstantiationException { start=in.readLong(); stop=in.readLong(); num_msgs_expected=in.readLong(); num_msgs_received=in.readLong(); done=in.readBoolean(); total_bytes_received=in.readLong(); } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/PerformanceTestGenerator.java0000644000175000017500000000777711366547366032647 0ustar twernertwernerpackage org.jgroups.tests.perf; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Properties; /** * Utility tool that creates configuration files for automated performance tests. * PerformanceTestGenerator, given an input file with -config flag, generates * test files that are used in conjunction with bin/clusterperfromancetest.sh. * The number of test files generated is numOfSenders*messageSizes. * * Default configuration file used for this utility is conf/performancetestconfig.txt * See bin/clusterperformancetest.sh * * @author Vladimir Blagojevic * @version $Id$ */ public class PerformanceTestGenerator { Properties configProperties = null; private long totalDataBytes; private int[] numOfSenders; private int[] messageSizes; private int numNodes; private int interval; public PerformanceTestGenerator(Properties config) { configProperties = config; } /** * @param args */ public static void main(String[] args) { Properties config = null; for(int i=0; i < args.length; i++) { if("-config".equals(args[i])) { String config_file=args[++i]; config = new Properties(); try { config.load(new FileInputStream(config_file)); } catch (FileNotFoundException e) { System.err.println("File " + config_file + " not found"); return; } catch (IOException e) { System.err.println("Error reading configuration file " + config_file); return; } continue; } System.out.println("PerformanceTestGenerator -config "); return; } try { PerformanceTestGenerator t=new PerformanceTestGenerator(config); t.parse(); t.generateTestConfigFiles(); } catch(Exception e) { e.printStackTrace(); } } private void parse() throws Exception{ numNodes = Integer.parseInt(configProperties.getProperty("nodes")); totalDataBytes = Long.parseLong(configProperties.getProperty("total_data")); numOfSenders = tokenizeAndConvert(configProperties.getProperty("number_of_senders"),","); messageSizes = tokenizeAndConvert(configProperties.getProperty("message_sizes"),","); interval = Integer.parseInt(configProperties.getProperty("interval")); } private void generateFile(int numOfSenders, int messageSize,int nodeCount) { FileWriter fw = null; long numOfMessages = (totalDataBytes/messageSize); try { fw = new FileWriter("config_"+numOfSenders + "_" +nodeCount +"_"+ messageSize + ".txt"); fw.write("transport=org.jgroups.tests.perf.transports.JGroupsTransport\n"); fw.write("msg_size="+messageSize+"\n"); fw.write("num_msgs="+(numOfMessages/numOfSenders) +"\n"); fw.write("num_senders="+numOfSenders+"\n"); fw.write("num_members="+nodeCount+"\n"); fw.write("log_interval="+interval+"\n"); fw.close(); } catch (IOException e) { e.printStackTrace(); } } public static String[] tokenize(String s, String delim) { if (s == null || s.length() == 0) { return new String[0]; } if (delim == null || delim.length() == 0) { return new String[] { s }; } ArrayList tokens = new ArrayList(); int pos = 0; int delPos = 0; while ((delPos = s.indexOf(delim, pos)) != -1) { tokens.add(s.substring(pos, delPos)); pos = delim.length() + delPos; } tokens.add(s.substring(pos)); return (String[]) tokens.toArray(new String[tokens.size()]); } public static int[] tokenizeAndConvert(String s, String delim) { String stokens [] = tokenize(s,delim); int result [] = new int[stokens.length]; for (int i = 0; i < stokens.length; i++) { result[i]=Integer.parseInt(stokens[i]); } return result; } private void generateTestConfigFiles() { for (int i = 0; i < numOfSenders.length; i++) { for (int j = 0; j < messageSizes.length; j++) { generateFile(numOfSenders[i],messageSizes[j],numNodes); } } } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/0000755000175000017500000000000011621261110027177 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/JmsTransport.java0000644000175000017500000000672411366547366032553 0ustar twernertwernerpackage org.jgroups.tests.perf.transports; import org.jgroups.tests.perf.Receiver; import org.jgroups.tests.perf.Transport; import javax.jms.*; import javax.naming.InitialContext; import java.util.Properties; import java.util.Map; /** * @author Bela Ban Jan 22 * @author 2004 * @version $Id: JmsTransport.java,v 1.8 2006/12/19 08:51:46 belaban Exp $ */ public class JmsTransport implements Transport, MessageListener { Receiver receiver=null; Properties config=null; Object local_addr=null; ConnectionFactory factory; InitialContext ctx; TopicConnection conn; TopicSession session; TopicPublisher pub; TopicSubscriber sub; Topic topic; String topic_name="topic/testTopic"; public JmsTransport() { } public Object getLocalAddress() { return local_addr; } public void create(Properties properties) throws Exception { this.config=properties; String tmp=config.getProperty("topic"); if(tmp != null) topic_name=tmp; ctx=new InitialContext(); factory=(ConnectionFactory)ctx.lookup("ConnectionFactory"); // local_addr=new IpAddress(ucast_sock.getLocalAddress(), ucast_sock.getLocalPort()); } public void start() throws Exception { conn=((TopicConnectionFactory)factory).createTopicConnection(); session=conn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); topic=(Topic)ctx.lookup(topic_name); pub=session.createPublisher(topic); sub=session.createSubscriber(topic); sub.setMessageListener(this); conn.start(); this.local_addr=conn.getClientID(); System.out.println("-- local_addr is " + local_addr); } public void stop() { try { conn.stop(); } catch(JMSException e) { e.printStackTrace(); } } public void destroy() { } public void setReceiver(Receiver r) { this.receiver=r; } public Map dumpStats() { return null; } public void send(Object destination, byte[] payload, boolean oob) throws Exception { if(destination != null) throw new Exception("JmsTransport.send(): unicast destination is not supported"); ObjectMessage msg=session.createObjectMessage(payload); msg.setObjectProperty("sender", local_addr); // msg.setObjectProperty("size", new Integer(payload.length)); //todo: write the sender (maybe use ObjectMessage instead of BytesMessage) // msg.writeInt(payload.length); // msg.writeBytes(payload, 0, payload.length); pub.publish(topic, msg); } public void onMessage(Message message) { Object sender=null; if(message == null || !(message instanceof ObjectMessage)) { System.err.println("JmsTransport.onMessage(): received a non ObjectMessage (" + message + "), discarding"); return; } ObjectMessage tmp=(ObjectMessage)message; try { // todo: read the sender sender=tmp.getObjectProperty("sender"); // int len=tmp.readInt(); // int len=((Integer)tmp.getObjectProperty("size")).intValue(); byte[] payload=(byte[])tmp.getObject(); if(receiver != null) receiver.receive(sender, payload); } catch(JMSException e) { e.printStackTrace(); } } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/TcpTransport.java0000644000175000017500000002141311366547366032540 0ustar twernertwernerpackage org.jgroups.tests.perf.transports; import org.jgroups.stack.IpAddress; import org.jgroups.tests.perf.Receiver; import org.jgroups.tests.perf.Transport; import org.jgroups.util.Util; import org.jgroups.Address; import java.io.*; import java.net.*; import java.util.*; /** * @author Bela Ban Jan 22 * @author 2004 * @version $Id: TcpTransport.java,v 1.16.4.1 2008/01/22 10:01:34 belaban Exp $ */ public class TcpTransport implements Transport { Receiver receiver=null; Properties config=null; int max_receiver_buffer_size=500000; int max_send_buffer_size=500000; List nodes; ConnectionTable ct; int start_port=7800; ServerSocket srv_sock=null; InetAddress bind_addr=null; IpAddress local_addr=null; List receivers=new ArrayList(); public TcpTransport() { } public Object getLocalAddress() { return local_addr; } public void create(Properties properties) throws Exception { this.config=properties; String tmp; if((tmp=config.getProperty("srv_port")) != null) start_port=Integer.parseInt(tmp); else if((tmp=config.getProperty("start_port")) != null) start_port=Integer.parseInt(tmp); String bind_addr_str=System.getProperty("udp.bind_addr", config.getProperty("bind_addr")); if(bind_addr_str != null) { bind_addr=InetAddress.getByName(bind_addr_str); } else bind_addr=InetAddress.getLocalHost(); String cluster_def=config.getProperty("cluster"); if(cluster_def == null) throw new Exception("TcpTransport.create(): property 'cluster' is not defined"); nodes=parseCommaDelimitedList(cluster_def); ct=new ConnectionTable(nodes); } public void start() throws Exception { srv_sock=Util.createServerSocket(bind_addr, start_port); local_addr=new IpAddress(srv_sock.getInetAddress(), srv_sock.getLocalPort()); ct.init(); // accept connections and start 1 Receiver per connection Thread acceptor=new Thread() { public void run() { while(true) { try { Socket s=srv_sock.accept(); ReceiverThread r=new ReceiverThread(s); r.setDaemon(true); receivers.add(r); r.start(); } catch(Exception ex) { ex.printStackTrace(); break; } } } }; acceptor.setDaemon(true); acceptor.start(); } public void stop() { ct.close(); for(Iterator it=receivers.iterator(); it.hasNext();) { ReceiverThread thread=(ReceiverThread)it.next(); thread.stopThread(); } } public void destroy() { ; } public void setReceiver(Receiver r) { this.receiver=r; } public Map dumpStats() { return null; } public void send(Object destination, byte[] payload, boolean oob) throws Exception { if(destination != null) throw new Exception("TcpTransport.send(): unicasts not supported"); ct.writeMessage(payload); } class ConnectionTable { /** List */ List myNodes; final Connection[] connections; ConnectionTable(List nodes) throws Exception { this.myNodes=nodes; connections=new Connection[nodes.size()]; } void init() throws Exception { int i=0; for(Iterator it=myNodes.iterator(); it.hasNext();) { InetSocketAddress addr=(InetSocketAddress)it.next(); if(connections[i] == null) { try { connections[i]=new Connection(addr); connections[i].createSocket(); System.out.println("-- connected to " +addr); } catch(ConnectException connect_ex) { System.out.println("-- failed to connect to " +addr); } catch(Exception all_others) { throw all_others; } } i++; } } // todo: parallelize void writeMessage(byte[] msg) throws Exception { for(int i=0; i < connections.length; i++) { Connection c=connections[i]; if(c != null) { try { c.writeMessage(msg); } catch(Exception e) { // System.err.println("failed sending msg on " + c); } } } } void close() { for(int i=0; i < connections.length; i++) { Connection c=connections[i]; if(c != null) c.close(); } } public String toString() { StringBuilder sb=new StringBuilder(); for(Iterator it=myNodes.iterator(); it.hasNext();) { sb.append(it.next()).append(' '); } return sb.toString(); } } class Connection { Socket sock=null; DataOutputStream out; InetSocketAddress to; final Object mutex=new Object(); Connection(InetSocketAddress addr) { this.to=addr; } void createSocket() throws IOException { sock=new Socket(to.getAddress(), to.getPort()); sock.setSendBufferSize(max_send_buffer_size); sock.setReceiveBufferSize(max_receiver_buffer_size); out=new DataOutputStream(new BufferedOutputStream(sock.getOutputStream())); Util.writeAddress(local_addr, out); } void writeMessage(byte[] msg) throws Exception { synchronized(mutex) { if(sock == null) { createSocket(); } out.writeInt(msg.length); out.write(msg, 0, msg.length); } out.flush(); } void close() { try { out.flush(); sock.close(); } catch(Exception ex) { } } public String toString() { return "Connection from " + local_addr + " to " + to; } } class ReceiverThread extends Thread { Socket sock; DataInputStream in; Address peer_addr; ReceiverThread(Socket sock) throws Exception { this.sock=sock; // sock.setSoTimeout(5000); in=new DataInputStream(new BufferedInputStream(sock.getInputStream())); // in=new DataInputStream(sock.getInputStream()); peer_addr=Util.readAddress(in); // System.out.println("-- ACCEPTED connection from " + peer_addr); } public void run() { while(sock != null) { try { int len=in.readInt(); byte[] buf=new byte[len]; in.readFully(buf, 0, len); // System.out.println("-- received data from " + peer_addr); if(receiver != null) receiver.receive(peer_addr, buf); } catch(EOFException eof) { break; } catch(Exception ex) { break; } } System.out.println("-- receiver thread for " + peer_addr + " terminated"); } void stopThread() { try { sock.close(); sock=null; } catch(Exception ex) { } } } public List parseCommaDelimitedList(String s) throws Exception { List retval=new ArrayList(); StringTokenizer tok; String hostname, tmp; int port; InetSocketAddress addr; int index; if(s == null) return null; tok=new StringTokenizer(s, ","); while(tok.hasMoreTokens()) { tmp=tok.nextToken(); index=tmp.indexOf(':'); if(index == -1) throw new Exception("host must be in format , was " + tmp); hostname=tmp.substring(0, index); port=Integer.parseInt(tmp.substring(index+1)); addr=new InetSocketAddress(hostname, port); retval.add(addr); } return retval; } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/JGroupsTransport.javalibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/JGroupsTransport.java0000644000175000017500000000534711366547366033413 0ustar twernertwernerpackage org.jgroups.tests.perf.transports; import org.jgroups.Address; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.jmx.JmxConfigurator; import org.jgroups.tests.perf.Receiver; import org.jgroups.tests.perf.Transport; import org.jgroups.util.Util; import javax.management.MBeanServer; import java.util.Map; import java.util.Properties; /** * @author Bela Ban Jan 22 * @author 2004 * @version $Id: JGroupsTransport.java,v 1.15 2006/12/19 08:51:46 belaban Exp $ */ public class JGroupsTransport extends org.jgroups.ReceiverAdapter implements Transport { Properties config=null; JChannel channel=null; Thread t=null; String props=null; String group_name="perf"; Receiver receiver=null; boolean jmx=false; public JGroupsTransport() { } public Object getLocalAddress() { return channel != null? channel.getLocalAddress() : null; } public void create(Properties properties) throws Exception { this.config=properties; props=config.getProperty("props"); jmx=new Boolean(this.config.getProperty("jmx")).booleanValue(); channel=new JChannel(props); channel.setReceiver(this); } public void start() throws Exception { channel.connect(group_name); if(jmx) { MBeanServer server=Util.getMBeanServer(); if(server == null) { throw new Exception("No MBeanServers found;" + "\nneeds to be run with an MBeanServer present, or inside JDK 5"); } JmxConfigurator.registerChannel(channel, server, "jgroups.perf", channel.getClusterName() , true); } } public void stop() { if(channel != null) { channel.shutdown(); // will cause thread to terminate anyways } t=null; } public void destroy() { if(channel != null) { channel.close(); channel=null; } } public void setReceiver(Receiver r) { this.receiver=r; } public Map dumpStats() { return channel != null? channel.dumpStats() : null; } public void send(Object destination, byte[] payload, boolean oob) throws Exception { Message msg=new Message((Address)destination, null, payload); if(oob) msg.setFlag(Message.OOB); if(channel != null) channel.send(msg); } public void receive(Message msg) { Address sender=msg.getSrc(); byte[] payload=msg.getBuffer(); if(receiver != null) { try { receiver.receive(sender, payload); } catch(Throwable tt) { tt.printStackTrace(); } } } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/UdpTransport.java0000644000175000017500000001107511366547366032545 0ustar twernertwernerpackage org.jgroups.tests.perf.transports; import org.jgroups.stack.IpAddress; import org.jgroups.tests.perf.Receiver; import org.jgroups.tests.perf.Transport; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.MulticastSocket; import java.util.Properties; import java.util.Map; /** * @author Bela Ban Jan 22 * @author 2004 * @version $Id: UdpTransport.java,v 1.7 2006/12/19 08:51:46 belaban Exp $ */ public class UdpTransport implements Transport { Receiver receiver=null; Properties config=null; InetAddress mcast_addr=null; int mcast_port=7500; InetAddress bind_addr=null; MulticastSocket mcast_sock=null; DatagramSocket ucast_sock=null; IpAddress local_addr=null; ReceiverThread mcast_receiver=null; ReceiverThread ucast_receiver=null; int max_receiver_buffer_size=500000; int max_send_buffer_size=500000; public UdpTransport() { } public Object getLocalAddress() { return local_addr; } public void create(Properties properties) throws Exception { this.config=properties; String mcast_addr_str=System.getProperty("udp.mcast_addr", config.getProperty("mcast_addr")); if(mcast_addr_str == null) mcast_addr_str="228.8.8.8"; mcast_addr=InetAddress.getByName(mcast_addr_str); String bind_addr_str=System.getProperty("udp.bind_addr", config.getProperty("bind_addr")); if(bind_addr_str != null) { bind_addr=InetAddress.getByName(bind_addr_str); } else bind_addr=InetAddress.getLocalHost(); ucast_sock=new DatagramSocket(0, bind_addr); ucast_sock.setReceiveBufferSize(max_receiver_buffer_size); ucast_sock.setSendBufferSize(max_send_buffer_size); mcast_sock=new MulticastSocket(mcast_port); mcast_sock.setReceiveBufferSize(max_receiver_buffer_size); mcast_sock.setSendBufferSize(max_send_buffer_size); if(bind_addr != null) mcast_sock.setInterface(bind_addr); mcast_sock.joinGroup(mcast_addr); local_addr=new IpAddress(ucast_sock.getLocalAddress(), ucast_sock.getLocalPort()); System.out.println("-- local_addr is " + local_addr); } public void start() throws Exception { mcast_receiver=new ReceiverThread(mcast_sock); ucast_receiver=new ReceiverThread(ucast_sock); mcast_receiver.start(); ucast_receiver.start(); } public void stop() { if(mcast_receiver != null) mcast_receiver.stop(); if(ucast_receiver != null) ucast_receiver.stop(); } public void destroy() { if(mcast_sock != null) mcast_sock.close(); if(ucast_sock != null) ucast_sock.close(); } public void setReceiver(Receiver r) { this.receiver=r; } public Map dumpStats() { return null; } public void send(Object destination, byte[] payload, boolean oob) throws Exception { DatagramPacket p; if(destination == null) { p=new DatagramPacket(payload, payload.length, mcast_addr, mcast_port); } else { IpAddress addr=(IpAddress)destination; p=new DatagramPacket(payload, payload.length, addr.getIpAddress(), addr.getPort()); } ucast_sock.send(p); } class ReceiverThread implements Runnable { DatagramSocket sock; Thread t=null; ReceiverThread(DatagramSocket sock) { this.sock=sock; } void start() throws Exception { t=new Thread(this, "ReceiverThread for " + sock.getLocalAddress() + ':' + sock.getLocalPort()); t.start(); } void stop() { t=null; if(sock != null) sock.close(); } public void run() { byte[] buf=new byte[128000]; DatagramPacket p; while(t != null) { p=new DatagramPacket(buf, buf.length); try { sock.receive(p); if(receiver != null) { IpAddress addr=new IpAddress(p.getAddress(), p.getPort()); receiver.receive(addr, p.getData()); } } catch(IOException e) { if(sock == null) t=null; } } t=null; } } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/JGroupsClusterTransport.javalibjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/transports/JGroupsClusterTranspo0000644000175000017500000000505411366547366033462 0ustar twernertwernerpackage org.jgroups.tests.perf.transports; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.stack.IpAddress; import org.jgroups.tests.perf.Transport; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; /** * Transport with a minimal stack (usually only UDP or TCP transport layer !) and explicit notion of * cluster membership (listed in properties, config.txt) * @author Bela Ban Jan 22 * @author 2004 * @version $Id: JGroupsClusterTransport.java,v 1.3 2006/12/19 08:51:46 belaban Exp $ */ public class JGroupsClusterTransport extends JGroupsTransport implements Transport { List members; public JGroupsClusterTransport() { super(); } public void create(Properties properties) throws Exception { super.create(properties); String cluster_def=config.getProperty("cluster"); if(cluster_def == null) throw new Exception("property 'cluster' is not defined"); members=parseCommaDelimitedList(cluster_def); } public void send(Object destination, byte[] payload, boolean oob) throws Exception { if(destination != null) { Message msg=new Message((Address)destination, null, payload); if(oob) msg.setFlag(Message.OOB); channel.send(msg); } else { // we don't know the membership from discovery, so we need to send individually Address mbr; for(int i=0; i < members.size(); i++) { mbr=(Address)members.get(i); Message msg=new Message((Address)destination, null, payload); if(oob) msg.setFlag(Message.OOB); msg.setDest(mbr); channel.send(msg); } } } private List parseCommaDelimitedList(String s) throws Exception { List retval=new ArrayList(); StringTokenizer tok; String hostname, tmp; int port; Address addr; int index; if(s == null) return null; tok=new StringTokenizer(s, ","); while(tok.hasMoreTokens()) { tmp=tok.nextToken(); index=tmp.indexOf(':'); if(index == -1) throw new Exception("host must be in format , was " + tmp); hostname=tmp.substring(0, index); port=Integer.parseInt(tmp.substring(index+1)); addr=new IpAddress(hostname, port); retval.add(addr); } return retval; } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/JPerf.java0000644000175000017500000001315111366547366026664 0ustar twernertwernerpackage org.jgroups.tests.perf; import java.io.*; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.text.NumberFormat; /** * Tool to measure TCP throughput, similar to iperf * @author Bela Ban * @version $Id: JPerf.java,v 1.2 2006/01/24 16:26:40 belaban Exp $ */ public class JPerf { boolean sender; int num; InetAddress local_addr; int local_port; int remote_port; InetAddress remote_addr; int size=1000; int incr=1000; static NumberFormat f; static { f=NumberFormat.getNumberInstance(); f.setGroupingUsed(false); f.setMaximumFractionDigits(2); } private void start(boolean sender, int num, int size, String local_addr, int local_port, String remote_addr, int remote_port, int receivebuf, int sendbuf) throws IOException { this.sender=sender; this.num=num; this.size=size; this.local_addr=InetAddress.getByName(local_addr); this.local_port=local_port; this.remote_addr=InetAddress.getByName(remote_addr); this.remote_port=remote_port; incr=num / 10; if(sender) { System.out.println("-- creating socket to " + this.remote_addr + ":" + this.remote_port); Socket sock=new Socket(this.remote_addr, remote_port, this.local_addr, 0); sock.setReceiveBufferSize(receivebuf); sock.setSendBufferSize(sendbuf); DataOutputStream out=new DataOutputStream(new BufferedOutputStream(sock.getOutputStream())); byte[] buf=new byte[size]; int cnt=1; System.out.println("-- sending " + num + " messages"); for(int i=0; i < num; i++) { out.write(buf, 0, buf.length); out.flush(); if(cnt % incr == 0) System.out.println("-- sent " + cnt + " messages"); cnt++; } } else { ServerSocket srv_sock=new ServerSocket(remote_port, 10, this.local_addr); System.out.println("-- waiting for " + num + " messages on " + srv_sock.getLocalSocketAddress()); Socket client_sock=srv_sock.accept(); client_sock.setReceiveBufferSize(receivebuf); client_sock.setSendBufferSize(sendbuf); System.out.println("-- accepted connection from " + client_sock.getRemoteSocketAddress()); DataInputStream in=new DataInputStream(new BufferedInputStream(client_sock.getInputStream())); byte[] buf=new byte[size]; int counter=0; long start=0, stop; while(counter < num) { in.readFully(buf, 0, buf.length); if(start == 0) start=System.currentTimeMillis(); counter++; if(counter % incr == 0) System.out.println("-- received " + counter); } stop=System.currentTimeMillis(); long diff=stop-start; double msgs_sec=(counter / (diff / 1000.0)); System.out.println("\nreceived " + counter + " messages in " + diff + "ms (" + f.format(msgs_sec) + " msgs/sec)"); double throughput=num * size / (diff / 1000.0) / 1000.0; if(throughput < 1000) System.out.println("throughput: " + f.format(throughput) + "KB/sec"); else { throughput/=1000.0; System.out.println("throughput: " + f.format(throughput) + "MB/sec"); } } } static void help() { System.out.println("JPerf [-help] [-sender] [-num ] [-local_addr ] [-local_port ] [-remote_port ] [-receivebuf ] [-sendbuf ]"); } public static void main(String[] args) { boolean sender=false; int num=100000; String local_addr="127.0.0.1"; int local_port=5000; int size=1000; int remote_port=10000; int receivebuf=200000, sendbuf=200000; String remote_addr="127.0.0.1"; for(int i=0; i < args.length; i++) { if(args[i].equals("-sender")) { sender=true; continue; } if(args[i].equals("-num")) { num=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-size")) { size=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-local_addr")) { local_addr=args[++i]; continue; } if(args[i].equals("-remote_addr")) { remote_addr=args[++i]; continue; } if(args[i].equals("-local_port")) { local_port=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-remote_port")) { remote_port=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-receivebuf")) { receivebuf=Integer.parseInt(args[++i]); continue; } if(args[i].equals("-sendbuf")) { sendbuf=Integer.parseInt(args[++i]); continue; } help(); return; } try { new JPerf().start(sender, num, size, local_addr, local_port, remote_addr, remote_port, receivebuf, sendbuf); } catch(IOException e) { e.printStackTrace(); } } } libjgroups2.6-java-2.6.15.GA.orig/tests/perf/org/jgroups/tests/perf/Transport.java0000644000175000017500000000310411366547366027647 0ustar twernertwernerpackage org.jgroups.tests.perf; import java.util.Properties; import java.util.Map; /** * Generic transport abstraction for all different transports (JGroups, JMS, UDP, TCP). The lifecycle is *
          *
        1. Create an instance of the transport (using the empty constructor) *
        2. Call create() *
        3. Possibly call setReceiver() *
        4. Call start() *
        5. Call send() *
        6. Call stop() *
        7. Call destroy() (alternatively call start() again) *
        * @author Bela Ban Jan 22 * @author 2004 * @version $Id: Transport.java,v 1.4 2006/12/19 08:51:47 belaban Exp $ */ public interface Transport { /** Create the transport */ void create(Properties properties) throws Exception; /** Get the local address (= endpoint) of this transport. Guaranteed to be called after * create(), possibly even later (after start()) */ Object getLocalAddress(); /** Start the transport */ void start() throws Exception; /** Stop the transport */ void stop(); /** Destroy the transport. Transport cannot be reused after this call, but a new instance has to be created */ void destroy(); /** Set the receiver */ void setReceiver(Receiver r); Map dumpStats(); /** * Send a message * @param destination A destination. If null, send a message to all members * @param payload A buffer to be sent * @throws Exception */ void send(Object destination, byte[] payload, boolean oob) throws Exception; } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/0000755000175000017500000000000011621261110022737 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/0000755000175000017500000000000011621261110023526 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/0000755000175000017500000000000011621261110025217 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/blocks/0000755000175000017500000000000011621261110026474 5ustar twernertwerner././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/blocks/ConnectionTableTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/blocks/ConnectionTableTest.java0000644000175000017500000002057211366547366033306 0ustar twernertwernerpackage org.jgroups.blocks; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.blocks.BasicConnectionTable.Connection; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.*; /** * Tests ConnectionTable * @author Bela Ban * @version $Id: ConnectionTableTest.java,v 1.7.2.4 2009/07/15 15:07:42 rachmatowicz Exp $ */ public class ConnectionTableTest extends TestCase { private BasicConnectionTable ct1, ct2; static String bind_addr_str=null; static InetAddress bind_addr=null; static byte[] data=new byte[]{'b', 'e', 'l', 'a'}; Address addr1, addr2; // needed as OSs need not release ports immediately after a socket has closed int port_range = 3 ; final static int PORT1=7521, PORT2=8931; static { try { bind_addr_str = System.getProperty("jgroups.bind_addr", "127.0.0.1") ; bind_addr=InetAddress.getByName(bind_addr_str); } catch(UnknownHostException e) { e.printStackTrace(); } } public ConnectionTableTest(String testName) { super(testName); } protected void setUp() throws Exception { super.setUp(); addr1=new IpAddress(bind_addr, PORT1); addr2=new IpAddress(bind_addr, PORT2); } protected void tearDown() throws Exception { if(ct2 != null) { ct2.stop(); ct2=null; } if(ct1 != null) { ct1.stop(); ct1=null; } super.tearDown(); } /** * A connects to B and B connects to A at the same time. This test makes sure we only have one connection, * not two, e.g. a spurious connection. Tests http://jira.jboss.com/jira/browse/JGRP-549 */ public void testConcurrentConnect() throws Exception { Sender sender1, sender2; CyclicBarrier barrier=new CyclicBarrier(3); ct1=new ConnectionTable(bind_addr, PORT1); ct1.start(); ct2=new ConnectionTable(bind_addr, PORT2); ct2.start(); BasicConnectionTable.Receiver dummy=new BasicConnectionTable.Receiver() { public void receive(Address sender, byte[] data, int offset, int length) {} }; ct1.setReceiver(dummy); ct2.setReceiver(dummy); sender1=new Sender((ConnectionTable)ct1, barrier, addr2, 0); sender2=new Sender((ConnectionTable)ct2, barrier, addr1, 0); sender1.start(); sender2.start(); Util.sleep(100); int num_conns; System.out.println("ct1: " + ct1 + "ct2: " + ct2); num_conns=ct1.getNumConnections(); assertEquals(0, num_conns); num_conns=ct2.getNumConnections(); assertEquals(0, num_conns); barrier.await(10000, TimeUnit.MILLISECONDS); sender1.join(); sender2.join(); System.out.println("ct1: " + ct1 + "\nct2: " + ct2); num_conns=ct1.getNumConnections(); assertEquals(1, num_conns); num_conns=ct2.getNumConnections(); assertEquals(1, num_conns); Util.sleep(500); System.out.println("ct1: " + ct1 + "\nct2: " + ct2); num_conns=ct1.getNumConnections(); assertEquals(1, num_conns); num_conns=ct2.getNumConnections(); assertEquals(1, num_conns); Connection connection = ct1.getConnection(addr2); assertFalse("valid connection to peer",connection.isSocketClosed()); connection = ct2.getConnection(addr1); assertFalse("valid connection to peer",connection.isSocketClosed());; } private static class Sender extends Thread { final ConnectionTable conn_table; final CyclicBarrier barrier; final Address dest; final long sleep_time; public Sender(ConnectionTable conn_table, CyclicBarrier barrier, Address dest, long sleep_time) { this.conn_table=conn_table; this.barrier=barrier; this.dest=dest; this.sleep_time=sleep_time; } public void run() { try { barrier.await(10000, TimeUnit.MILLISECONDS); if(sleep_time > 0) Util.sleep(sleep_time); conn_table.send(dest, data, 0, data.length); } catch(Exception e) { } } } public void testBlockingQueue() { final BlockingQueue queue=new LinkedBlockingQueue(); Thread taker=new Thread() { public void run() { try { System.out.println("taking an element from the queue"); queue.take(); System.out.println("clear"); } catch(InterruptedException e) { } } }; taker.start(); Util.sleep(500); queue.clear(); // does this release the taker thread ? Util.interruptAndWaitToDie(taker); assertFalse("taker: " + taker, taker.isAlive()); } public void testStopConnectionTableNoSendQueues() throws Exception { ct1=new ConnectionTable(new DummyReceiver(), bind_addr, null, PORT1, PORT1+port_range, 60000, 120000); ct1.setUseSendQueues(false); ct1.start(); ct2=new ConnectionTable(new DummyReceiver(), bind_addr, null, PORT2, PORT2+port_range, 60000, 120000); ct2.setUseSendQueues(false); ct2.start(); _testStop(ct1, ct2); } public void testStopConnectionTableWithSendQueues() throws Exception { ct1=new ConnectionTable(new DummyReceiver(), bind_addr, null, PORT1, PORT1+port_range, 60000, 120000); ct1.start(); ct2=new ConnectionTable(new DummyReceiver(), bind_addr, null, PORT2, PORT2+port_range, 60000, 120000); ct2.start(); _testStop(ct1, ct2); } public void testStopConnectionTableNIONoSendQueues() throws Exception { ct1=new ConnectionTableNIO(new DummyReceiver(), bind_addr, null, PORT1, PORT1+port_range, 60000, 120000, false); ct1.setUseSendQueues(false); ct2=new ConnectionTableNIO(new DummyReceiver(), bind_addr, null, PORT2, PORT2+port_range, 60000, 120000, false); ct2.setUseSendQueues(false); ct1.start(); ct2.start(); _testStop(ct1, ct2); } public void testStopConnectionTableNIOWithSendQueues() throws Exception { ct1=new ConnectionTableNIO(new DummyReceiver(), bind_addr, null, PORT1, PORT1+port_range, 60000, 120000, false); ct2=new ConnectionTableNIO(new DummyReceiver(), bind_addr, null, PORT2, PORT2+port_range, 60000, 120000, false); ct1.start(); ct2.start(); _testStop(ct1, ct2); } private void _testStop(BasicConnectionTable table1, BasicConnectionTable table2) throws Exception { table1.send(addr1, data, 0, data.length); // send to self assertEquals(0, table1.getNumConnections()); // sending to self should not create a connection table1.send(addr2, data, 0, data.length); // send to other table2.send(addr2, data, 0, data.length); // send to self table2.send(addr1, data, 0, data.length); // send to other System.out.println("table1:\n" + table1 + "\ntable2:\n" + table2); assertEquals(1, table1.getNumConnections()); assertEquals(1, table2.getNumConnections()); table2.stop(); table1.stop(); assertEquals(0, table1.getNumConnections()); assertEquals(0, table2.getNumConnections()); } public static Test suite() { return new TestSuite(ConnectionTableTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(ConnectionTableTest.suite()); } static class DummyReceiver implements ConnectionTable.Receiver { public void receive(Address sender, byte[] data, int offset, int length) { System.out.println("-- received " + length + " bytes from " + sender); } } static class DummyReceiverNIO implements ConnectionTableNIO.Receiver { public void receive(Address sender, byte[] data, int offset, int length) { System.out.println("-- received " + length + " bytes from " + sender); } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/blocks/GroupRequestTest.java0000644000175000017500000003352011366547366032701 0ustar twernertwerner// $Id: GroupRequestTest.java,v 1.6 2007/07/30 10:53:23 belaban Exp $$ package org.jgroups.blocks; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.*; import org.jgroups.stack.IpAddress; import org.jgroups.util.RspList; import org.jgroups.util.Util; import java.util.Vector; public class GroupRequestTest extends TestCase { // GroupRequest req; Address a1, a2, a3; Vector
        dests=null; public GroupRequestTest(String testName) { super(testName); } protected void setUp() throws Exception { super.setUp(); a1=new IpAddress("127.0.0.1", 1111); a2=new IpAddress("127.0.0.1", 2222); a3=new IpAddress("127.0.0.1", 3333); dests=new Vector
        (2); dests.add(a1); dests.add(a2); } protected void tearDown() throws Exception { dests.clear(); super.tearDown(); } public void testMessageTimeout() throws Exception { _testMessageTimeout(true); } public void testMessageReception() throws Exception { _testMessageReception(true); _testMessageReception(false); } public void testMessageReceptionWithSuspect() throws Exception { _testMessageReceptionWithSuspect(true); _testMessageReceptionWithSuspect(false); } public void testMessageReceptionWithViewChange() throws Exception { _testMessageReceptionWithViewChange(true); _testMessageReceptionWithViewChange(false); } public void testMessageReceptionWithViewChangeMemberLeft() throws Exception { _testMessageReceptionWithViewChangeMemberLeft(true); _testMessageReceptionWithViewChangeMemberLeft(false); } public void testGetFirstWithResponseFilter() throws Exception { Object[] responses=new Message[]{new Message(null, a1, new Long(1)), new Message(null, a2, new Long(2)), new Message(null, a3, new Long(3))}; MyTransport transport=new MyDelayedTransport(true, responses, 500); dests.add(a3); GroupRequest req=new GroupRequest(new Message(), transport, dests, GroupRequest.GET_FIRST, 0, 3); req.setResponseFilter(new RspFilter() { int num_rsps=0; public boolean isAcceptable(Object response, Address sender) { boolean retval=response instanceof Long && ((Long)response).longValue() == 2L; System.out.println("-- received " + response + " from " + sender + ": " + (retval? "OK" : "NOTOK")); if(retval) num_rsps++; return retval; } public boolean needMoreResponses() { return num_rsps < 1; } }); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assertTrue(rc); assertEquals(0, req.getSuspects().size()); assertTrue(req.isDone()); RspList results=req.getResults(); assertEquals(3, results.size()); assertEquals(1, results.numReceived()); } public void testGetAllWithResponseFilter() throws Exception { Object[] responses=new Message[]{new Message(null, a1, new Long(1)), new Message(null, a2, new Long(2)), new Message(null, a3, new Long(3))}; MyTransport transport=new MyDelayedTransport(true, responses, 500); dests.add(a3); GroupRequest req=new GroupRequest(new Message(), transport, dests, GroupRequest.GET_ALL, 0, 3); req.setResponseFilter(new RspFilter() { int num_rsps=0; public boolean isAcceptable(Object response, Address sender) { boolean retval=response instanceof Long && (((Long)response).longValue() == 1L || ((Long)response).longValue() == 2L); System.out.println("-- received " + response + " from " + sender + ": " + (retval? "OK" : "NOTOK")); if(retval) num_rsps++; return retval; } public boolean needMoreResponses() { return num_rsps < 2; } }); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assertTrue(rc); assertEquals(0, req.getSuspects().size()); assertTrue(req.isDone()); RspList results=req.getResults(); assertEquals(3, results.size()); assertEquals(2, results.numReceived()); } /** * test group timeout. demonstrates that the timeout mechanism times out too * quickly as multiple responses are received by the GroupRequest. * Demonstrates by group request receiving multiple messages in a timeframe * less than the total timeout. the request will fail, as after each * received message, the request alters the total timeout. * * @throws Exception */ private void _testMessageTimeout(boolean async) throws Exception { // need multiple destinations to replicate error int destCount = 10; // total timeout to hear from all members final long timeout = destCount * 300; // how long each destination should delay final long delay = 75L; Object[] responses = new Message[destCount]; dests = new Vector
        (); for (int i = 0; i < destCount; i++) { Address addr = new IpAddress("127.0.0.1", Integer.parseInt(String.valueOf(i) + i + i + i)); dests.add(addr); // how long does this simulated destination take to execute? the sum is just less than the total timeout responses[i] = new Message(null, addr, new Long(i)); } MyDelayedTransport tp = new MyDelayedTransport(async, responses, delay); // instantiate request with dummy correlator GroupRequest req=new GroupRequest(new Message(), tp, dests, GroupRequest.GET_ALL, timeout, dests.size()); tp.setGroupRequest(req); boolean rc = req.execute(); System.out.println("group request is " + req); assertTrue(rc); assertEquals(0, req.getSuspects().size()); assertTrue(req.isDone()); RspList results = req.getResults(); assertEquals(dests.size(), results.size()); } private void _testMessageReception(boolean async) throws Exception { Object[] responses=new Message[]{new Message(null, a1, new Long(1)),new Message(null, a2, new Long(2))}; MyTransport transport=new MyTransport(async, responses); GroupRequest req=new GroupRequest(new Message(), transport, dests, GroupRequest.GET_ALL, 0, 2); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assertTrue(rc); assertEquals(0, req.getSuspects().size()); assertTrue(req.isDone()); RspList results=req.getResults(); assertEquals(2, results.size()); } private void _testMessageReceptionWithSuspect(boolean async) throws Exception { Object[] responses=new Object[]{new Message(null, a1, new Long(1)), new SuspectEvent(a2)}; MyTransport transport=new MyTransport(async, responses); GroupRequest req=new GroupRequest(new Message(), transport, dests, GroupRequest.GET_ALL, 0, 2); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assertTrue(rc); assertEquals(1, req.getSuspects().size()); assertTrue(req.isDone()); RspList results=req.getResults(); assertEquals(2, results.size()); } private void _testMessageReceptionWithViewChange(boolean async) throws Exception { Vector
        new_dests=new Vector
        (); new_dests.add(a1); new_dests.add(a2); new_dests.add(new IpAddress("127.0.0.1", 3333)); Object[] responses=new Object[]{new Message(null, a1, new Long(1)), new View(new IpAddress("127.0.0.1", 9999), 322649, new_dests), new Message(null, a2, new Long(2))}; MyTransport transport=new MyTransport(async, responses); GroupRequest req=new GroupRequest(new Message(), transport, dests, GroupRequest.GET_ALL, 0, 2); transport.setGroupRequest(req); boolean rc=req.execute(); System.out.println("group request is " + req); assertTrue(rc); assertEquals("suspects are " + req.getSuspects(), 0, req.getSuspects().size()); assertTrue(req.isDone()); RspList results=req.getResults(); assertEquals(2, results.size()); } private void _testMessageReceptionWithViewChangeMemberLeft(boolean async) throws Exception { Vector
        new_dests=new Vector
        (); new_dests.add(a2); Object[] responses=new Object[]{new Message(null, a2, new Long(1)), new View(new IpAddress("127.0.0.1", 9999), 322649, new_dests)}; MyTransport transport=new MyTransport(async, responses); GroupRequest req=new GroupRequest(new Message(), transport, dests, GroupRequest.GET_ALL, 0, 2); transport.setGroupRequest(req); System.out.println("group request before execution: " + req); boolean rc=req.execute(); System.out.println("group request after execution: " + req); assertTrue(rc); assertEquals("suspects are " + req.getSuspects(), 1, req.getSuspects().size()); assertTrue(req.isDone()); RspList results=req.getResults(); assertEquals(2, results.size()); } public static Test suite() { return new TestSuite(GroupRequestTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } protected static class MyTransport implements Transport { GroupRequest request; boolean async=true; Object[] responses=null; public MyTransport(boolean async, Object[] responses) { this.async=async; this.responses=responses; } public void setGroupRequest(GroupRequest r) { request=r; } public void send(Message msg) throws Exception { if(async) { new Thread() { public void run() { sendResponses(); } }.start(); } else { sendResponses(); } } public Object receive(long timeout) throws Exception { return null; } void sendResponses() { if(responses != null) { Object obj; for(int i=0; i < responses.length; i++) { obj=responses[i]; if(obj == null) { System.err.println("object was null"); continue; } if(obj instanceof Message) { Message msg=(Message)obj; Address sender=msg.getSrc(); Object retval=null; try { retval=Util.objectFromByteBuffer(msg.getBuffer()); } catch(Exception e) { e.printStackTrace(); } request.receiveResponse(retval, sender); } else if(obj instanceof SuspectEvent) request.suspect((Address)((SuspectEvent)obj).getMember()); else if(obj instanceof View) request.viewChange((View)obj); else System.err.println("Object needs to be Message, SuspectEvent or View"); } } } } /** * transport with set delays between messages * * @author bgodfrey * */ private static final class MyDelayedTransport extends MyTransport { long delay; public MyDelayedTransport(boolean async, Object[] responses) { super(async, responses); } public MyDelayedTransport(boolean async, Object[] responses, long delay) { super(async, responses); this.delay = delay; } void sendResponses() { if (responses != null) { Object obj; for (int i = 0; i < responses.length; i++) { try { Thread.sleep(delay); } catch (InterruptedException e1) { e1.printStackTrace(); } obj = responses[i]; if (obj == null) { System.err.println("object was null"); continue; } if (obj instanceof Message) { Message msg = (Message) obj; Address sender = msg.getSrc(); Object retval = null; try { retval = Util.objectFromByteBuffer(msg.getBuffer()); } catch (Exception e) { e.printStackTrace(); } request.receiveResponse(retval, sender); } else if (obj instanceof SuspectEvent) request.suspect((Address) ((SuspectEvent) obj).getMember()); else if (obj instanceof View) request.viewChange((View) obj); else System.err.println("Object needs to be Message, SuspectEvent or View"); } } } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/0000755000175000017500000000000011621261110026361 5ustar twernertwerner././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/EncryptMessageOrderTestCase.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/EncryptMessageOrderTestCa0000644000175000017500000003237511366547366033401 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.blocks.PullPushAdapter; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Iterator; import java.util.Vector; /** * This test case checks ordering of messages using the Encrypt protocol * using UDPprotocols. It can be run * as JUnit test case or in the command line. Parameters are: *
          *
        • -sleep n - means that after each message sending, sender * thread will sleep for n milliseconds; *
        • -msg_num n - n is number of messages to send; *
        • -debug - pop-up protocol debugger; *
        * $Id: EncryptMessageOrderTestCase.java,v 1.1.4.2 2008/09/15 12:52:29 belaban Exp $ */ public class EncryptMessageOrderTestCase extends TestCase { public static int MESSAGE_NUMBER=5 * 100; public static boolean SLEEP_BETWEEN_SENDING=false; public static int SLEEP_TIME=1; String groupName = "ENCRYPT_ORDER_TEST"; boolean orderCounterFailure = false; protected Log log=LogFactory.getLog(this.getClass()); public static final String properties ="EncryptNoKeyStore.xml"; static { try { ClassConfigurator.getInstance(true).add((short)5764, EncryptOrderTestHeader.class); } catch(ChannelException e) { throw new IllegalArgumentException(e); } } /** * Constructor to create test case. */ public EncryptMessageOrderTestCase(String string) { super(string); } protected JChannel channel1; protected PullPushAdapter adapter1; protected JChannel channel2; protected PullPushAdapter adapter2; /** * Print selected options before test starts. */ protected static void printSelectedOptions() { System.out.println("will sleep : " + SLEEP_BETWEEN_SENDING); if(SLEEP_BETWEEN_SENDING) System.out.println("sleep time : " + SLEEP_TIME); System.out.println("msg num : " + MESSAGE_NUMBER); } /** * Set up unit test. It might add protocol * stack debuggers if such option was selected at startup. */ protected void setUp() throws Exception { super.setUp(); printSelectedOptions(); channel1=new JChannel(properties); System.out.print("Connecting to channel..."); channel1.connect(groupName); System.out.println("channel1 connected, view is " + channel1.getView()); adapter1=new PullPushAdapter(channel1); // sleep one second before second member joins try { Thread.sleep(1000); } catch(InterruptedException ex) { } channel2=new JChannel(properties); channel2.connect(groupName); System.out.println("channel2 connected, view is " + channel2.getView()); adapter2=new PullPushAdapter(channel2); // sleep one second before processing continues try { Thread.sleep(1000); } catch(InterruptedException ex) { } } /** * Tears down test case. This method closes all opened channels. */ protected void tearDown() throws Exception { super.tearDown(); adapter2.stop(); channel2.close(); adapter1.stop(); channel1.close(); } protected boolean finishedReceiving; /** * Test method. This method adds a message listener to the PullPushAdapter * on channel 1, and starts sending specified number of messages into * channel 1 or 2 depending if we are in loopback mode or not. Each message * containg timestamp when it was created. By measuring time on message * delivery we can calculate message trip time. Listener is controlled by * two string messages "start" and "stop". After sender has finished to * send messages, it waits until listener receives all messages or "stop" * message. Then test is finished and calculations are showed. *

        * Also we calculate how much memory * was allocated before excuting a test and after executing a test. */ public void testLoad() { try { final String startMessage="start"; final String stopMessage="stop"; final Object mutex=new Object(); final Vector receivedTimes=new Vector(MESSAGE_NUMBER); final Vector normalMessages=new Vector(MESSAGE_NUMBER); final Vector tooQuickMessages=new Vector(); final Vector tooSlowMessages=new Vector(); adapter1.setListener(new MessageListener() { private boolean started=false; private boolean stopped=false; private long counter = 0L; public byte[] getState() { return null; } public void setState(byte[] state) { } public void receive(Message jgMessage) { Object message=jgMessage.getObject(); if(startMessage.equals(message)) { started=true; finishedReceiving=false; } else if(stopMessage.equals(message)) { stopped=true; finishedReceiving=true; synchronized(mutex) { mutex.notifyAll(); } } else if(message instanceof Long) { Long travelTime=new Long(System.currentTimeMillis() - ((Long)message).longValue()); try { assertEquals(counter, ((EncryptOrderTestHeader)((Message)jgMessage).getHeader("EncryptOrderTest")).seqno); counter++; } catch (Exception e){ log.warn(e); orderCounterFailure =true; } if(!started) tooQuickMessages.add(message); else if(started && !stopped) { receivedTimes.add(travelTime); normalMessages.add(message); } else tooSlowMessages.add(message); } } }); System.out.println("Free memory: " + Runtime.getRuntime().freeMemory()); System.out.println("Total memory: " + Runtime.getRuntime().totalMemory()); System.out.println("Starting sending messages."); long time=System.currentTimeMillis(); Message startJgMessage=new Message(); startJgMessage.setObject(startMessage); JChannel sender= channel2; sender.send(startJgMessage); for(int i=0; i < MESSAGE_NUMBER; i++) { Long message=new Long(System.currentTimeMillis()); Message jgMessage=new Message(); jgMessage.putHeader("EncryptOrderTest", new EncryptOrderTestHeader(i)); jgMessage.setObject(message); sender.send(jgMessage); if(i % 1000 == 0) System.out.println("sent " + i + " messages."); if(SLEEP_BETWEEN_SENDING) org.jgroups.util.Util.sleep(1, true); } Message stopJgMessage=new Message(); stopJgMessage.setObject(stopMessage); sender.send(stopJgMessage); time=System.currentTimeMillis() - time; System.out.println("Finished sending messages. Operation took " + time); synchronized(mutex) { int received=0; while(!finishedReceiving) { mutex.wait(1000); if(receivedTimes.size() != received) { received=receivedTimes.size(); System.out.println(); System.out.print("Received " + receivedTimes.size() + " messages."); } else { System.out.print("."); } } } try { Thread.sleep(1000); } catch(Exception ex) { } double avgDeliveryTime=-1.0; long maxDeliveryTime=Long.MIN_VALUE; long minDeliveryTime=Long.MAX_VALUE; Iterator iterator=receivedTimes.iterator(); while(iterator.hasNext()) { Long message=(Long)iterator.next(); if(avgDeliveryTime == -1.0) avgDeliveryTime=message.longValue(); else avgDeliveryTime=(avgDeliveryTime + message.doubleValue()) / 2.0; if(message.longValue() > maxDeliveryTime) maxDeliveryTime=message.longValue(); if(message.longValue() < minDeliveryTime) minDeliveryTime=message.longValue(); } System.out.println("Sent " + MESSAGE_NUMBER + " messages."); System.out.println("Received " + receivedTimes.size() + " messages."); System.out.println("Average delivery time " + avgDeliveryTime + " ms"); System.out.println("Minimum delivery time " + minDeliveryTime + " ms"); System.out.println("Maximum delivery time " + maxDeliveryTime + " ms"); System.out.println("Received " + tooQuickMessages.size() + " too quick messages"); System.out.println("Received " + tooSlowMessages.size() + " too slow messages"); } catch(Exception ex) { ex.printStackTrace(); } System.out.println("Free memory: " + Runtime.getRuntime().freeMemory()); System.out.println("Total memory: " + Runtime.getRuntime().totalMemory()); System.out.println("Performing GC"); Runtime.getRuntime().gc(); try { Thread.sleep(2000); } catch(InterruptedException ex) { } System.out.println("Free memory: " + Runtime.getRuntime().freeMemory()); System.out.println("Total memory: " + Runtime.getRuntime().totalMemory()); assertTrue("Message ordering is incorrect - check log output",(!orderCounterFailure)); } /** * Main method to start a test case from the command line. Parameters are: *

          *
        • -sleep n - means that after each message sending, sender * thread will sleep for n milliseconds; *
        • -msg_num n - n is number of messages to send;; *
        • -debug - pop-up protocol debugger; *
        */ public static void main(String[] args) { for(int i=0; i < args.length; i++) { if("-sleep".equals(args[i])) { SLEEP_BETWEEN_SENDING=true; if(!(i < args.length - 1)) throw new RuntimeException("You have to specify sleep time"); try { SLEEP_TIME=Integer.parseInt(args[++i]); } catch(NumberFormatException nfex) { throw new RuntimeException("Cannot parse sleep time"); } } else if("-msg_num".equals(args[i])) { if(!(i < args.length - 1)) throw new RuntimeException("You have to specify messages number"); try { MESSAGE_NUMBER=Integer.parseInt(args[++i]); } catch(NumberFormatException nfex) { throw new RuntimeException("Cannot parse messages number"); } } else if("-help".equals(args[i])) { help(); return; } } junit.textui.TestRunner.run(EncryptMessageOrderTestCase.class); } static void help() { System.out.println("EncryptOrderTest [-help] [-sleep ] " + " [-msg_num ]"); } public static class EncryptOrderTestHeader extends Header{ long seqno = -1; // either reg. NAK_ACK_MSG or first_seqno in retransmissions public EncryptOrderTestHeader(){} public EncryptOrderTestHeader(long seqno){ this.seqno = seqno; } public int size(){ return 512; } public void writeExternal(ObjectOutput out) throws IOException{ out.writeLong(seqno); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{ seqno = in.readLong(); } public EncryptOrderTestHeader copy(){ EncryptOrderTestHeader ret = new EncryptOrderTestHeader(seqno); return ret; } public String toString(){ StringBuilder ret = new StringBuilder(); ret.append("[ENCRYPT_ORDER_TEST: seqno=" + seqno); ret.append(']'); return ret.toString(); } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ViewIdTest.java0000644000175000017500000000311111366547366031301 0ustar twernertwerner// $Id: ViewIdTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.ViewId; import java.net.InetAddress; public class ViewIdTest extends TestCase { ViewId v1, v2, v3, v4; public ViewIdTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); try { v1=new ViewId(new org.jgroups.stack.IpAddress(InetAddress.getByName("localhost"), 1000), 22); v2=new ViewId(new org.jgroups.stack.IpAddress(InetAddress.getByName("localhost"), 1000), 21); v3=(ViewId)v1.clone(); } catch(Exception e) { System.err.println("ViewIdTest.setUp(): " + e); } } public void tearDown() throws Exception { super.tearDown(); v1=v2=v3=null; } public void test0() { assertTrue(v1.equals(v2) == false); } public void test1() { assertEquals(v1, v3); } public void test2() { v3=(ViewId)v1.clone(); assertEquals(v1, v3); } public void test3() { assertTrue(v1.compareTo(v3) == 0); } public void test4() { assertTrue(v1.compareTo(v2) > 0); } public void test5() { assertTrue(v2.compareTo(v1) < 0); } public static Test suite() { TestSuite s=new TestSuite(ViewIdTest.class); return s; } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/SchedulerTest.java0000644000175000017500000000436511366547366032044 0ustar twernertwerner// $Id: SchedulerTest.java,v 1.1.4.1 2008/02/11 23:32:15 vlada Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.Scheduler; import org.jgroups.util.SchedulerListener; import org.jgroups.util.Util; public class SchedulerTest extends TestCase { static class MyThread implements Runnable { String name; MyThread(String name) { this.name=name; } public void run() { long sleep_time=(long)(Math.random() * 1000); System.out.println("\n--> " + name + ": sleeping for " + sleep_time + " ms"); Util.sleep(sleep_time); System.out.println("--> " + name + ": Done"); } public String toString() { return "MyThread [name=" + name + ']'; } } static class Listener implements SchedulerListener { public void started(Runnable t) { System.out.println("--> Started: " + t); } public void stopped(Runnable t) { System.out.println("--> Stopped: " + t); } public void suspended(Runnable t) { System.out.println("--> Suspended: " + t); } public void resumed(Runnable t) { System.out.println("--> Resumed: " + t); } } public SchedulerTest(String name) { super(name); } public void testScheduler() throws Exception { Scheduler sch=new Scheduler(); sch.setListener(new Listener()); sch.add(new MyThread("Bela")); sch.add(new MyThread("Janet")); sch.add(new MyThread("Ralph")); sch.start(); sch.add(new MyThread("Frank")); sch.add(new MyThread("Marco")); Util.sleep(1000); sch.addPrio(new MyThread("Gabi")); sch.add(new MyThread("Rolf")); sch.addPrio(new MyThread("Gabi2")); sch.addPrio(new MyThread("Gabi3")); sch.addPrio(new MyThread("Gabi4")); Util.sleep(1000); // increase this if you want to see all thread running (and possibly completing) sch.stop(); } public static void main(String[] args) { String[] testCaseName={SchedulerTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/MethodCallTest.java0000644000175000017500000003102111366547366032127 0ustar twernertwerner// $Id: MethodCallTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.blocks.MethodCall; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; /** * @author Bela Ban belaban@yahoo.com * @author Ovidiu Feodorov * @version $Revision: 1.1 $ **/ public class MethodCallTest extends TestCase { Class cl=MethodCallTest.class; public MethodCallTest(String name) { super(name); } public boolean foo(int a, String b) { System.out.println("test(" + a + ", " + b + ')'); return true; } public void bar(String[] a, String b) { if(a != null) { for(int i=0; i < a.length; i++) { String s=a[i]; System.out.print(s + ' '); } } else System.out.println("a=null"); if(b != null) System.out.println("b=" + b); else System.out.println("b=null"); } public void foobar() { System.out.println("foobar()"); } public void testOld() { try { MethodCall mc=new MethodCall("foo", new Object[]{new Integer(22), "Bela"}); assertEquals(mc.invoke(this), Boolean.TRUE); } catch(Throwable t) { fail(t.toString()); } } public void testOld2() { try { MethodCall mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, "Bela"}); mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testWithNull() { try { MethodCall mc=new MethodCall("foobar", null, (Class[])null); System.out.println("mc: " + mc); mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testOldWithNull() { try { MethodCall mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, null}); mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testOldWithNull2() { try { MethodCall mc=new MethodCall("bar", new Object[]{null, "Bela"}); mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testOldWithNull3() { try { MethodCall mc=new MethodCall("foobar", null); mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testOldWithNull4() { try { MethodCall mc=new MethodCall("foobar", new Object[0]); mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testMethod() { Method m; try { m=cl.getMethod("foo", new Class[]{int.class, String.class}); MethodCall mc=new MethodCall(m, new Object[]{new Integer(22), "Bela"}); assertEquals(mc.invoke(this), Boolean.TRUE); } catch(Throwable t) { fail(t.toString()); } } public void testTypes() { MethodCall mc; mc=new MethodCall("foo", new Object[]{new Integer(35), "Bela"}, new Class[]{int.class, String.class}); try { assertEquals(mc.invoke(this), Boolean.TRUE); } catch(Throwable t) { fail(t.toString()); } } public void testTypesWithArray() { MethodCall mc; mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, "Bela"}, new Class[]{String[].class, String.class}); try { mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testTypesWithNullArgument() { MethodCall mc; mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, null}, new Class[]{String[].class, String.class}); try { mc.invoke(this); } catch(Throwable t) { fail(t.toString()); } } public void testTypesWithNullArgument2() { MethodCall mc; mc=new MethodCall("bar", new Object[]{new String[]{"one", "two", "three"}, new Object[]{}}, new Class[]{String[].class, String.class}); try { mc.invoke(this); } catch(IllegalArgumentException ex) { assertTrue("this was expected", true); } catch(Throwable t) { fail(t.toString()); } } public void testTypesWithNullArgument3() { MethodCall mc; mc=new MethodCall("foobar", new Object[]{}, new Class[]{}); try { mc.invoke(this); } catch(IllegalArgumentException ex) { assertTrue("this was expected", true); } catch(Throwable t) { fail(t.toString()); } } public void testTypesWithNullArgument4() { MethodCall mc; mc=new MethodCall("foobar", (Object[])null, (Class[])null); try { mc.invoke(this); } catch(IllegalArgumentException ex) { assertTrue("this was expected", true); } catch(Throwable t) { fail(t.toString()); } } public void testTypesWithNullArgument5() { MethodCall mc; mc=new MethodCall("foobar", new Object[0], new Class[0]); try { mc.invoke(this); } catch(IllegalArgumentException ex) { assertTrue("this was expected", true); } catch(Throwable t) { fail(t.toString()); } } public void testSignature() { MethodCall mc; mc=new MethodCall("foo", new Object[]{new Integer(35), "Bela"}, new String[]{int.class.getName(), String.class.getName()}); try { assertEquals(mc.invoke(this), Boolean.TRUE); } catch(Throwable t) { fail(t.toString()); } } public void testBufferSize() throws Exception { int a=10; String b="Bela"; MethodCall m=new MethodCall("foo", new Object[]{new Integer(a),b}, new Class[]{int.class, String.class}); ByteArrayOutputStream msg_data=new ByteArrayOutputStream(); ObjectOutputStream msg_out=new ObjectOutputStream(msg_data); m.writeExternal(msg_out); msg_out.flush(); msg_out.close(); byte[] data=msg_data.toByteArray(); ByteArrayInputStream msg_in_data=new ByteArrayInputStream(data); ObjectInputStream msg_in=new ObjectInputStream(msg_in_data); MethodCall m2=new MethodCall(); m2.readExternal(msg_in); System.out.println(m2.getName()); System.out.println(m2.getArgs().length); } // // OLD // public void testOLD() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] {"abc"}); Target target = new Target(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } public void testInheritanceOLD() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] {"abc"}); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } // // METHOD // public void testMETHOD() throws Throwable { Method method = Target.class.getMethod("someMethod", new Class[] { String.class }); MethodCall methodCall = new MethodCall(method, new Object[] {"abc"}); Target target = new Target(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } public void testInheritanceMETHOD() throws Throwable { Method method = Target.class.getMethod("someMethod", new Class[] { String.class }); MethodCall methodCall = new MethodCall(method, new Object[] {"abc"}); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } // // TYPES // public void testTYPES() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] { "abc" }, new Class[] { String.class }); Target target = new Target(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } public void testInheritanceTYPES() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] { "abc" }, new Class[] { String.class }); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } /** * This tests whether overriden methods are correctly identified and invoked. */ public void testOverriddenForTYPES() throws Throwable { MethodCall methodCall = new MethodCall("overriddenMethod", new Object[] { "abc" }, new Class[] { String.class }); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); assertEquals("TargetSubclassABC", result); } public void testNoArgumentMethodForTYPES() throws Throwable { MethodCall methodCall = new MethodCall("noArgumentMethod", new Object[0], new Class[0]); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); assertEquals("noArgumentMethodResult", result); } // // SIGNATURE // public void testSIGNATURE() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] { "abc" }, new String[] { "java.lang.String" }); Target target = new Target(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } public void testInheritanceSIGNATURE() throws Throwable { MethodCall methodCall = new MethodCall("someMethod", new Object[] { "abc" }, new String[] { "java.lang.String" }); TargetSubclass target = new TargetSubclass(); Object result = methodCall.invoke(target); assertEquals("ABC", result); } public void testMarshalling() throws Exception { MethodCall methodCall = new MethodCall("someMethod", new Object[] { "abc" }, new String[] { "java.lang.String" }); methodCall.put("name", "Bela"); methodCall.put("id", new Integer(322649)); System.out.println("methodCall: " + methodCall); MethodCall m=marshalAndUnmarshal(methodCall); System.out.println("m: " + m); assertEquals(m.get("name"), "Bela"); assertEquals(m.get("id"), new Integer(322649)); } private MethodCall marshalAndUnmarshal(MethodCall m) throws Exception { byte[] buf=Util.objectToByteBuffer(m); MethodCall retval=(MethodCall)Util.objectFromByteBuffer(buf); return retval; } public static Test suite() { TestSuite s=new TestSuite(MethodCallTest.class); return s; } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public class Target { public String someMethod(String arg) { return arg.toUpperCase(); } public String overriddenMethod(String arg) { return "Target" + arg.toUpperCase(); } public String noArgumentMethod() { return "noArgumentMethodResult"; } } public class TargetSubclass extends Target { public String overriddenMethod(String arg) { return "TargetSubclass" + arg.toUpperCase(); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/UNICAST_ConnectionTests.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/UNICAST_ConnectionTests.j0000644000175000017500000001470011366547366033100 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import org.jgroups.protocols.UNICAST; import org.jgroups.util.Util; import java.util.List; import java.util.ArrayList; import junit.framework.TestCase; /** * Tests unilateral closings of UNICAST connections. The test scenarios are described in doc/design.UNICAST.new.txt. * @author Bela Ban * @version $Id: UNICAST_ConnectionTests.java,v 1.7.4.3 2009/09/17 15:17:39 belaban Exp $ */ public class UNICAST_ConnectionTests extends TestCase { private JChannel a, b; private Address a_addr, b_addr; private MyReceiver r1, r2; private UNICAST u1, u2; private static final String props="SHARED_LOOPBACK:UNICAST"; private static final String CLUSTER="UNICAST_ConnectionTests"; protected void setUp() throws Exception { super.setUp(); r1=new MyReceiver("A"); r2=new MyReceiver("B"); a=new JChannel(props); a.connect(CLUSTER); a_addr=a.getLocalAddress(); a.setReceiver(r1); u1=(UNICAST)a.getProtocolStack().findProtocol(UNICAST.class); b=new JChannel(props); b.connect(CLUSTER); b_addr=b.getLocalAddress(); b.setReceiver(r2); u2=(UNICAST)b.getProtocolStack().findProtocol(UNICAST.class); System.out.println("A=" + a_addr + ", B=" + b_addr); } protected void tearDown() throws Exception { Util.close(b, a); super.tearDown(); } /** * Tests cases #1 and #2 of UNICAST.new.txt * @throws Exception */ public void testRegularMessageReception() throws Exception { sendAndCheck(a, b_addr, 100, r2); sendAndCheck(b, a_addr, 50, r1); } /** * Tests case #3 of UNICAST.new.txt */ public void testBothChannelsClosing() throws Exception { sendToEachOtherAndCheck(10); // now close the connections to each other System.out.println("==== Closing the connections on both sides"); u1.removeConnection(b_addr); u2.removeConnection(a_addr); r1.clear(); r2.clear(); // causes new connection establishment sendToEachOtherAndCheck(10); } /** * Scenario #4 (A closes the connection unilaterally (B keeps it open), then reopens it and sends messages) */ public void testAClosingUnilaterally() throws Exception { sendToEachOtherAndCheck(10); // now close connection on A unilaterally System.out.println("==== Closing the connection on A"); u1.removeConnection(b_addr); // then send messages from A to B sendAndCheck(a, b_addr, 10, r2); } /** * Scenario #5 (B closes the connection unilaterally (A keeps it open), then A sends messages to B) */ public void testBClosingUnilaterally() throws Exception { sendToEachOtherAndCheck(10); // now close connection on A unilaterally System.out.println("==== Closing the connection on B"); u2.removeConnection(a_addr); // then send messages from A to B sendAndCheck(a, b_addr, 10, r2); } /** * Scenario #6 (A closes the connection unilaterally (B keeps it open), then reopens it and sends messages, * but loses the first message */ public void testAClosingUnilaterallyButLosingFirstMessage() throws Exception { sendToEachOtherAndCheck(10); // now close connection on A unilaterally System.out.println("==== Closing the connection on A"); u1.removeConnection(b_addr); // add a Drop protocol to drop the first unicast message Drop drop=new Drop(true); a.getProtocolStack().insertProtocol(drop, ProtocolStack.BELOW, UNICAST.class); // then send messages from A to B sendAndCheck(a, b_addr, 10, r2); } /** * Send num unicasts on both channels and verify the other end received them * @param num * @throws Exception */ private void sendToEachOtherAndCheck(int num) throws Exception { for(int i=1; i <= num; i++) { a.send(b_addr, null, "m" + i); b.send(a_addr, null, "m" + i); } List l1=r1.getMessages(); List l2=r2.getMessages(); for(int i=0; i < 10; i++) { if(l1.size() == num && l2.size() == num) break; Util.sleep(500); } System.out.println("l1 = " + print(l1)); System.out.println("l2 = " + print(l2)); assertEquals(num, l1.size()); assertEquals(num, l2.size()); } private static void sendAndCheck(JChannel channel, Address dest, int num, MyReceiver receiver) throws Exception { receiver.clear(); for(int i=1; i <= num; i++) channel.send(dest, null, "m" + i); List list=receiver.getMessages(); for(int i=0; i < 10; i++) { if(list.size() == num) break; Util.sleep(500); } System.out.println("list = " + print(list)); int size=list.size(); assertEquals("list has " + size + " elements", num, size); } private static String print(List list) { List tmp=new ArrayList(list.size()); for(Message msg: list) tmp.add((String)msg.getObject()); return Util.printListWithDelimiter(tmp, " "); } private static class MyReceiver extends ReceiverAdapter { final String name; final List msgs=new ArrayList(20); public MyReceiver(String name) { this.name=name; } public void receive(Message msg) { msgs.add(msg); } public List getMessages() { return msgs; } public void clear() {msgs.clear();} public int size() {return msgs.size();} public String toString() { return name; } } private static class Drop extends Protocol { private volatile boolean drop_next=false; private Drop(boolean drop_next) { this.drop_next=drop_next; } public String getName() { return "Drop"; } public void dropNext() { drop_next=true; } public Object down(Event evt) { if(drop_next && evt.getType() == Event.MSG) { drop_next=false; return null; } return super.down(evt); } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ReentrantLockTest.java0000644000175000017500000000350711366547366032676 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.concurrent.locks.ReentrantLock; /** * Tests the ReentrantLock * @author Bela Ban * @version $Id: ReentrantLockTest.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ */ public class ReentrantLockTest extends TestCase { ReentrantLock lock; public ReentrantLockTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); lock=new ReentrantLock(); } public void tearDown() throws Exception { releaseAll(lock); lock=null; super.tearDown(); } public void testAcquireLock() { lock.lock(); assertEquals(1, lock.getHoldCount()); lock.lock(); assertEquals(2, lock.getHoldCount()); release(lock); assertEquals(1, lock.getHoldCount()); release(lock); assertEquals(0, lock.getHoldCount()); } public void testAcquireLock2() { lock.lock(); assertEquals(1, lock.getHoldCount()); lock.lock(); assertEquals(2, lock.getHoldCount()); releaseAll(lock); assertEquals(0, lock.getHoldCount()); } private void release(ReentrantLock lock) { if(lock != null && lock.getHoldCount() > 0) lock.unlock(); } private void releaseAll(ReentrantLock lock) { if(lock != null) { long holds=lock.getHoldCount(); if(holds > 0) { for(int i=0; i < holds; i++) lock.unlock(); } } } public static Test suite() { return new TestSuite(ReentrantLockTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(ReentrantLockTest.suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ConfiguratorTest.java0000644000175000017500000001333511366547366032565 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.JChannel; import org.jgroups.stack.Configurator; import org.jgroups.stack.Protocol; import org.jgroups.stack.ProtocolStack; import java.util.List; import java.util.Vector; /** * Tests ProtocolStack.insertProtocol() and removeProtocol() * @author Bela Ban * @version $Id: ConfiguratorTest.java,v 1.2.2.1 2007/11/20 08:53:45 belaban Exp $ */ public class ConfiguratorTest extends TestCase { ProtocolStack stack; static final String props="UDP(mcast_addr=225.1.2.3):PING:FD:pbcast.NAKACK:UNICAST:FC"; final String[] names={"FC", "UNICAST", "NAKACK", "FD", "PING", "UDP"}; final String[] below={"FC", "UNICAST", "TRACE", "NAKACK", "FD", "PING", "UDP"}; final String[] above={"FC", "TRACE", "UNICAST", "NAKACK", "FD", "PING", "UDP"}; public ConfiguratorTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); JChannel mock_channel=new JChannel() { }; stack=new ProtocolStack(mock_channel, props); } public void tearDown() throws Exception { super.tearDown(); } public void testInsertion() throws Exception { stack.setup(); List protocols=stack.getProtocols(); assertNotNull(protocols); assertEquals(6, protocols.size()); for(int i=0; i < names.length; i++) { String name=names[i]; Protocol p=(Protocol)protocols.get(i); assertEquals(name, p.getName()); } // insert below Protocol new_prot=(Protocol)Class.forName("org.jgroups.protocols.TRACE").newInstance(); stack.insertProtocol(new_prot, ProtocolStack.BELOW, "UNICAST"); protocols=stack.getProtocols(); assertEquals(7, protocols.size()); for(int i=0; i < below.length; i++) { String name=below[i]; Protocol p=(Protocol)protocols.get(i); assertEquals(name, p.getName()); } // remove Protocol prot=stack.removeProtocol("TRACE"); assertNotNull(prot); protocols=stack.getProtocols(); assertEquals(6, protocols.size()); for(int i=0; i < names.length; i++) { String name=names[i]; Protocol p=(Protocol)protocols.get(i); assertEquals(name, p.getName()); } // insert above new_prot=(Protocol)Class.forName("org.jgroups.protocols.TRACE").newInstance(); stack.insertProtocol(new_prot, ProtocolStack.ABOVE, "UNICAST"); protocols=stack.getProtocols(); assertEquals(7, protocols.size()); for(int i=0; i < above.length; i++) { String name=above[i]; Protocol p=(Protocol)protocols.get(i); assertEquals(name, p.getName()); } } public void testParsing() throws Exception { String config="UDP(mcast_addr=ff18:eb72:479f::2:3;oob_thread_pool.max_threads=4;" + "oob_thread_pool.keep_alive_time=5000;max_bundle_size=64000;mcast_send_buf_size=640000;" + "oob_thread_pool.queue_max_size=10;mcast_recv_buf_size=25000000;" + "use_concurrent_stack=true;tos=8;mcast_port=45522;loopback=true;thread_pool.min_threads=2;" + "oob_thread_pool.rejection_policy=Run;thread_pool.max_threads=8;enable_diagnostics=true;" + "thread_naming_pattern=cl;ucast_send_buf_size=640000;ucast_recv_buf_size=20000000;" + "thread_pool.enabled=true;use_incoming_packet_handler=true;oob_thread_pool.enabled=true;ip_ttl=2;" + "enable_bundling=true;thread_pool.rejection_policy=Run;discard_incompatible_packets=true;" + "thread_pool.keep_alive_time=5000;thread_pool.queue_enabled=false;mcast_addr=228.10.10.15;" + "max_bundle_timeout=30;oob_thread_pool.queue_enabled=false;oob_thread_pool.min_threads=2;" + "thread_pool.queue_max_size=100):" + "PING(num_initial_members=3;timeout=2000):" + "MERGE2(min_interval=5000;max_interval=10000):" + "FD_SOCK:" + "FD(max_tries=3;timeout=2000):" + "VERIFY_SUSPECT(timeout=1500):" + "BARRIER:" + "pbcast.NAKACK(gc_lag=0;use_mcast_xmit=false;retransmit_timeout=300,600,1200,2400,4800;" + "discard_delivered_msgs=true):" + "UNICAST(loopback=false;timeout=300,600,1200,2400,3600):" + "pbcast.STABLE(desired_avg_gossip=50000;max_bytes=1000000;stability_delay=1000):" + "VIEW_SYNC(avg_send_interval=60000):" + "pbcast.GMS(print_local_addr=true;view_bundling=true;join_timeout=3000;" + "shun=false):" + "FC(max_block_time=10000;max_credits=5000000;min_threshold=0.25):" + "FRAG2(frag_size=60000):" + "pbcast.STREAMING_STATE_TRANSFER(use_reading_thread=true)"; Vector ret=Configurator.parseConfigurations(config); System.out.println("config:\n" + ret); assertEquals(15, ret.size()); config="UDP(mcast_addr=ff18:eb72:479f::2:3;mcast_port=2453):pbcast.FD:FRAG(frag_size=2292):FD_SIMPLE(s=22;d=33):MERGE2(a=22)"; ret=Configurator.parseConfigurations(config); System.out.println("config:\n" + ret); assertEquals(5, ret.size()); config="com.mycomp.Class:B:pbcast.C:H(a=b;c=d;e=f)"; ret=Configurator.parseConfigurations(config); System.out.println("config:\n" + ret); assertEquals(4, ret.size()); } public static void main(String[] args) { String[] testCaseName={ConfiguratorTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/PromiseTest.java0000644000175000017500000000571511366547366031544 0ustar twernertwerner// $Id: PromiseTest.java,v 1.2.2.1 2007/11/20 08:37:23 belaban Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.TimeoutException; import org.jgroups.util.Promise; import org.jgroups.util.Util; /** * Various test cases for Promise * @author Bela Ban */ public class PromiseTest extends TestCase { Promise p; public PromiseTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); p=new Promise(); } public void tearDown() throws Exception { p.reset(); super.tearDown(); } public void testGetResultNoTimeout() { Object result; new ResultSetter(p, 500).start(); result=p.getResult(0); assertEquals(Boolean.TRUE, result); } public void testGetResultNoTimeout_ResultAlreadySet() { Object result; new ResultSetter(p, 1).start(); Util.sleep(100); result=p.getResult(0); assertEquals(Boolean.TRUE, result); } public void testGetResultWithTimeout() { try { p.getResultWithTimeout(500); fail("this should throw a TimeoutException"); } catch(TimeoutException e) { assertNotNull(e); } } public void testGetResultWithTimeoutNoException() { Object ret=p.getResult(500); assertNull(ret); } public void testGetResultWithTimeoutAndInterrupt() { new Interrupter(Thread.currentThread(), 100).start(); Object result=p.getResult(500); assertNull(result); } public void testGetResultWithTimeoutAndResultSetter() { Thread t=new Thread() { public void run() { Util.sleep(500); System.out.println("-- setting promise to \"Bela\""); p.setResult("Bela"); } }; t.start(); long start=System.currentTimeMillis(), stop; Object result=p.getResult(100000); stop=System.currentTimeMillis(); System.out.println("-- waited for " + (stop-start) + "ms, result is " + result); assertNotNull(result); assertEquals("Bela", result); assertFalse("promise was reset after getResult()", p.hasResult()); } static class ResultSetter extends Thread { long wait_time=2000; Promise target=null; ResultSetter(Promise target, long wait_time) { this.target=target; this.wait_time=wait_time; } public void run() { Util.sleep(wait_time); target.setResult(Boolean.TRUE); } } static class Interrupter extends Thread { long wait_time=2000; Thread target=null; Interrupter(Thread target, long wait_time) { this.target=target; this.wait_time=wait_time; } public void run() { Util.sleep(wait_time); target.interrupt(); } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/NakReceiverWindowTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/NakReceiverWindowTest.jav0000644000175000017500000004164611366547366033356 0ustar twernertwerner// $Id: NakReceiverWindowTest.java,v 1.1.4.2 2009/09/11 12:07:55 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Message; import org.jgroups.Address; import org.jgroups.util.TimeScheduler; import org.jgroups.stack.NakReceiverWindow; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Retransmitter; public class NakReceiverWindowTest extends TestCase { private Address sender; private MyRetransmitCommand cmd=new MyRetransmitCommand(); private TimeScheduler timer=new TimeScheduler(); public NakReceiverWindowTest(String name) { super(name); } protected void setUp() throws Exception { super.setUp(); sender=new IpAddress("127.0.0.1", 5555); } public void test1() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 1, timer); check(win, 0, 1, 1); assertNull(win.get(23)); } public void test2() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); check(win, 0, 100, 100); } public void test3() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); assertNotNull(win.get(1)); check(win, 0, 1, 0); win.add(2, new Message()); check(win, 0, 2, 0); assertNotNull(win.get(2)); win.remove(); check(win, 0, 2, 1); win.remove(); check(win, 0, 2, 2); } public void test4() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 1, timer); win.add(2, new Message()); check(win, 0, 2, 1); } public void test5() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); win.add(101, new Message()); win.add(100, new Message()); check(win, 0, 101, 100); } public void test6() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); win.add(101, new Message()); System.out.println("win: " + win); win.add(100, new Message()); System.out.println("win: " + win); check(win, 0, 101, 100); win.remove(); System.out.println("win: " + win); check(win, 0, 101, 101); while((win.remove()) != null); check(win, 0, 101, 101); } public void testLowerBounds() { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, 50, timer); win.add(101, new Message()); System.out.println("win: " + win); win.add(100, new Message()); System.out.println("win: " + win); check(win, 50, 101, 100); win.remove(); System.out.println("win: " + win); check(win, 50, 101, 101); while((win.remove()) != null); check(win, 50, 101, 101); } public void test7() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); check(win, 0, 4, 0); System.out.println("Note that the subsequent warning is expected:"); win.stable(4); // no-op because we haven't even removed 4 messages check(win, 0, 4, 0); while(win.remove() != null); check(win, 0, 4, 4); win.stable(4); check(win, 4, 4, 4); } public void testLowerBounds2() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, 50, timer); win.add(100, new Message()); win.add(101, new Message()); win.add(102, new Message()); win.add(103, new Message()); System.out.println("win: " + win); check(win, 50, 103, 100); System.out.println("Note that the subsequent warning is expected:"); win.stable(103); // no-op because we haven't even removed 4 messages check(win, 50, 103, 100); while(win.remove() != null); check(win, 50, 103, 103); win.stable(103); System.out.println("win: " + win); check(win, 103, 103, 103); } public void test8() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); win.add(6, new Message()); check(win, 0, 6, 0); // haven't delivered a message yet while(win.remove() != null); check(win, 0, 6, 4); win.add(5, new Message()); check(win, 0, 6, 4); win.remove(); check(win, 0, 6, 5); win.remove(); check(win, 0, 6, 6); win.stable(4); check(win, 4, 6, 6); win.stable(6); check(win, 6, 6, 6); } public void testAdd() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); check(win, 0, 0, 0); win.add(0, new Message()); // discarded, next expected is 1 check(win, 0, 0, 0); win.add(1, new Message()); check(win, 0, 1, 0); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); check(win, 0, 4, 0); win.add(6, new Message()); check(win, 0, 6, 0); win.add(5, new Message()); check(win, 0, 6, 0); while(win.remove() != null) ; check(win, 0, 6, 6); win.stable(4); check(win, 4, 6, 6); win.stable(6); check(win, 6, 6, 6); } public void test9() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); win.add(6, new Message()); System.out.println("win: " + win); while((win.remove()) != null) ; win.stable(6); // 6 is ignore as it is >= highest delivered message System.out.println("win: " + win); assertNotNull(win.get(2)); check(win, 0, 6, 4); win.add(5, new Message()); check(win, 0, 6, 4); while((win.remove()) != null) ; check(win, 0, 6, 6); win.stable(6); check(win, 6, 6, 6); } public void testHighestDelivered() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); check(win, 0, 4, 0); win.add(10, new Message()); check(win, 0, 10, 0); System.out.println("win: " + win); win.add(9, new Message()); win.add(7, new Message()); win.add(8, new Message()); win.add(6, new Message()); win.add(5, new Message()); System.out.println("win: " + win); check(win, 0, 10, 0); while((win.remove()) != null) ; check(win, 0, 10, 10); win.stable(5); System.out.println("win: " + win); check(win, 5, 10, 10); win.stable(10); System.out.println("win: " + win); check(win, 10, 10, 10); } public void testMissingMessages() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(5, new Message()); check(win, 0, 5, 0); win.add(6, new Message()); check(win, 0, 6, 0); System.out.println("win: " + win); } public void testMissingMessages2() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(5, new Message()); check(win, 0, 5, 0); win.add(8, new Message()); check(win, 0, 8, 0); win.add(9, new Message()); check(win, 0, 9, 0); System.out.println("win: " + win); } public void testMissingMessages3() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(5, new Message()); check(win, 0, 5, 0); win.add(8, new Message()); check(win, 0, 8, 0); win.add(9, new Message()); check(win, 0, 9, 0); System.out.println("win: " + win); win.add(2, new Message()); check(win, 0, 9, 0); win.add(3, new Message()); win.add(4, new Message()); check(win, 0, 9, 0); win.add(7, new Message()); check(win, 0, 9, 0); win.add(6, new Message()); check(win, 0, 9, 0); win.add(10, new Message()); check(win, 0, 10, 0); win.add(11, new Message()); check(win, 0, 11, 0); System.out.println("win: " + win); } public void testMissingMessages4() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); win.add(101, new Message()); win.add(105, new Message()); check(win, 0, 105, 100); win.add(108, new Message()); check(win, 0, 108, 100); win.add(109, new Message()); check(win, 0, 109, 100); System.out.println("win: " + win); win.add(102, new Message()); check(win, 0, 109, 100); win.add(103, new Message()); win.add(104, new Message()); check(win, 0, 109, 100); win.add(107, new Message()); check(win, 0, 109, 100); win.add(106, new Message()); check(win, 0, 109, 100); win.add(110, new Message()); check(win, 0, 110, 100); win.add(110, new Message()); check(win, 0, 110, 100); System.out.println("win: " + win); } public void testMissingMessages5() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 100, timer); win.add(101, new Message()); check(win, 0, 101, 100); win.add(108, new Message()); check(win, 0, 108, 100); win.remove(); win.add(109, new Message()); check(win, 0, 109, 101); System.out.println("win: " + win); win.add(102, new Message()); check(win, 0, 109, 101); win.add(103, new Message()); win.add(104, new Message()); check(win, 0, 109, 101); win.add(107, new Message()); check(win, 0, 109, 101); win.add(106, new Message()); win.add(105, new Message()); check(win, 0, 109, 101); win.add(110, new Message()); check(win, 0, 110, 101); win.add(110, new Message()); check(win, 0, 110, 101); System.out.println("win: " + win); } public void test10() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); while((win.remove()) != null) ; check(win, 0, 4, 4); } public void test10a() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); while((win.remove()) != null) ; win.stable(4); check(win, 4, 4, 4); } public void test11() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); while((win.remove()) != null) ; win.reset(); check(win, 0, 0, 0); } public void test12() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message(null, null, new Integer(1))); win.add(2, new Message(null, null, new Integer(2))); win.add(3, new Message(null, null, new Integer(3))); assertEquals(1, ((Integer)win.remove().getObject()).intValue()); assertEquals(2, ((Integer)win.remove().getObject()).intValue()); assertEquals(3, ((Integer)win.remove().getObject()).intValue()); } public void test13() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); win.add(1, new Message()); win.add(2, new Message()); win.add(3, new Message()); win.add(4, new Message()); check(win, 0, 4, 0); win.remove(); win.remove(); win.add(5, new Message()); win.add(6, new Message()); check(win, 0, 6, 2); win.stable(2); check(win, 2, 6, 2); } public void testAddOOBAtHead() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); boolean rc; rc=win.add(0, oob()); assertFalse(rc); rc=win.add(1, oob()); assertTrue(rc); rc=win.add(1, oob()); assertFalse(rc); } public void testAddOOBAtTail() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); boolean rc; rc=win.add(1, oob()); assertTrue(rc); rc=win.add(2, oob()); assertTrue(rc); rc=win.add(2, oob()); assertFalse(rc); } public void testAddOOBInTheMiddle() throws Exception { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); boolean rc; rc=win.add(3, oob()); assertTrue(rc); rc=win.add(3, oob()); assertFalse(rc); rc=win.add(1, oob()); assertTrue(rc); rc=win.add(1, oob()); assertFalse(rc); rc=win.add(2, oob()); assertTrue(rc); rc=win.add(2, oob()); assertFalse(rc); } public void testUpdateHighestSeen() { add(1000); add(2000); add(3000); add(4000); add(5000); add(10000); add(15000); add(20000); add(30000); } public void test1000() { add(1000); } public void test10000() { add(10000); } public void testHasMessagesToRemove() { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); assertFalse(win.hasMessagesToRemove()); win.add(2, new Message()); assertFalse(win.hasMessagesToRemove()); win.add(1, oob()); assertTrue(win.hasMessagesToRemove()); win.remove(); assertTrue(win.hasMessagesToRemove()); win.remove(); assertFalse(win.hasMessagesToRemove()); } public void testRemoveOOBMessage() { NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 0, timer); System.out.println("win = " + win); win.add(2, new Message()); System.out.println("win = " + win); assertNull(win.removeOOBMessage()); assertNull(win.remove()); win.add(1, oob()); System.out.println("win = " + win); assertNotNull(win.removeOOBMessage()); System.out.println("win = " + win); assertNull(win.removeOOBMessage()); assertNotNull(win.remove()); assertNull(win.remove()); assertNull(win.removeOOBMessage()); } private void add(int num_msgs) { long start, stop; double time_per_msg; NakReceiverWindow win=new NakReceiverWindow(sender, cmd, 1, timer); start=System.currentTimeMillis(); for(int i=1; i < 1 + num_msgs; i++) { win.add(i, new Message()); } stop=System.currentTimeMillis(); time_per_msg=(stop-start) / (double)num_msgs; System.out.println("-- time for " + num_msgs + " msgs: " + (stop-start) + ", " + time_per_msg + " ms/msg"); } private Message oob() { Message retval=new Message(); retval.setFlag(Message.OOB); return retval; } private void check(NakReceiverWindow win, long lowest, long highest_received, long highest_delivered) { assertEquals("lowest=" + lowest + ", win.lowest=" + win.getLowestSeen(), lowest, win.getLowestSeen()); assertEquals("highest_received=" + highest_received + ", win.highest_received=" + win.getHighestReceived(), highest_received, win.getHighestReceived()); assertEquals("highest_delivered=" + highest_delivered + ", win.highest_delivered=" + win.getHighestDelivered(), highest_delivered, win.getHighestDelivered()); } private static class MyRetransmitCommand implements Retransmitter.RetransmitCommand { public void retransmit(long first_seqno, long last_seqno, Address sender) { } } public static Test suite() { return new TestSuite(NakReceiverWindowTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/RspListTest.java0000644000175000017500000001147011366547366031521 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.util.*; public class RspListTest extends TestCase { RspList rl; Address a1, a2, a3, a4, a5; Rsp rsp1, rsp2, rsp3, rsp4, rsp5; public RspListTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); rl=new RspList(); a1=new IpAddress(1111); a2=new IpAddress(2222); a3=new IpAddress(3333); a4=new IpAddress(4444); a5=new IpAddress(5555); rsp1=new Rsp(a1); rsp2=new Rsp(a2, true); rsp3=new Rsp(a3, "hello world"); rsp4=new Rsp(a4, Boolean.TRUE); rsp5=new Rsp(a5, true); rl.put(a1, rsp1); rl.put(a2, rsp2); rl.put(a3, rsp3); rl.put(a4, rsp4); rl.put(a5, rsp5); } protected void tearDown() throws Exception { rl.clear(); super.tearDown(); } public void testConstructor() { Collection c=new LinkedList(); c.add(rsp1); c.add(rsp2); c.add(rsp3); RspList tmp=new RspList(c); assertEquals(c.size(), tmp.size()); assertTrue(tmp.containsKey(a1)); assertTrue(tmp.containsKey(a2)); assertTrue(tmp.containsKey(a3)); assertTrue(tmp.containsValue(rsp1)); assertTrue(tmp.containsValue(rsp2)); assertTrue(tmp.containsValue(rsp3)); } public void testIsEmpty() { RspList tmp=new RspList(); assertTrue(tmp.isEmpty()); tmp.addRsp(a1, rsp1); assertFalse(tmp.isEmpty()); } public void testContainsKey() { assertTrue(rl.containsKey(a1)); assertTrue(rl.containsKey(a3)); } public void testContainsValue() { assertTrue(rl.containsValue(rsp1)); assertTrue(rl.containsValue(rsp3)); } public void testGet() { Rsp rsp=(Rsp)rl.get(a1); assertEquals(rsp, rsp1); rsp=(Rsp)rl.get(a3); assertEquals(rsp, rsp3); } public void testPut() { Rsp rsp; rsp=(Rsp)rl.put(new IpAddress(6666), new Rsp(new IpAddress(6666), true)); assertNull(rsp); rsp=(Rsp)rl.put(a2, rsp2); assertEquals(rsp, rsp2); assertEquals(6, rl.size()); } public void testRemove() { Rsp rsp; rsp=(Rsp)rl.remove(new IpAddress(6666)); assertNull(rsp); rsp=(Rsp)rl.remove(a2); assertEquals(rsp, rsp2); assertEquals(4, rl.size()); } public void testClear() { rl.clear(); assertEquals(0, rl.size()); } public void testKeySet() { RspList tmp=new RspList(); Set keys=tmp.keySet(); assertNotNull(keys); assertEquals(0, keys.size()); } public void testKeySet2() { Set keys=rl.keySet(); assertNotNull(keys); assertEquals(rl.size(), keys.size()); } public void testAddRsp() { rl.addRsp(new IpAddress(6666), new Integer(322649)); assertEquals(6, rl.size()); Rsp rsp=(Rsp)rl.get(new IpAddress(6666)); assertNotNull(rsp); assertTrue(rsp.wasReceived()); assertFalse(rsp.wasSuspected()); assertEquals(new Integer(322649), rsp.getValue()); } public void testAddRsp2() { rl.addRsp(a1, new Integer(322649)); assertEquals(5, rl.size()); Rsp rsp=(Rsp)rl.get(a1); assertNotNull(rsp); assertTrue(rsp.wasReceived()); assertFalse(rsp.wasSuspected()); assertEquals(new Integer(322649), rsp.getValue()); } public void testNumSuspectedMembers() { assertEquals(2, rl.numSuspectedMembers()); } public void testGetFirst() { Object obj=rl.getFirst(); System.out.println("-- first (non-null) value is " + obj); assertNotNull(obj); } public void testGetResults() { Vector v=rl.getResults(); assertNotNull(v); assertEquals(2, v.size()); } public void testElementAt() { Rsp rsp; Set s=new HashSet(); for(int i=0; i < rl.size(); i++) { rsp=(Rsp)rl.elementAt(i); s.add(rsp.getSender()); } System.out.println("-- set is " + s); assertEquals(rl.size(), s.size()); } public void testElementAtWithOOBEx() { try { rl.elementAt(6); fail("this should have thrown an ArrayIndexOutOfBoundsException"); } catch(ArrayIndexOutOfBoundsException ex) { } } public static Test suite() { return new TestSuite(RspListTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(RspListTest.suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckCollectorTest.java0000644000175000017500000001443211366547366032467 0ustar twernertwerner// $Id: AckCollectorTest.java,v 1.1.4.1 2009/08/11 11:28:13 belaban Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.AckCollector; import org.jgroups.util.Util; import org.jgroups.TimeoutException; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import java.util.ArrayList; import java.util.List; import java.net.UnknownHostException; public class AckCollectorTest extends TestCase { List l=new ArrayList(5); AckCollector ac; private List new_list=new ArrayList(3); public AckCollectorTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); l.add("one"); l.add("two"); l.add("three"); l.add("four"); l.add("five"); ac=new AckCollector(l); new_list.add("six"); new_list.add("seven"); new_list.add("eight"); } public void tearDown() throws Exception { super.tearDown(); l.clear(); new_list.clear(); } public void testConstructor() { System.out.println("AckCollector is " + ac); assertEquals(5, ac.size()); } public void testWaitForAllAcksNoTimeout() { new Thread() { public void run() { ac.ack("one"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("two"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("three"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("four"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("five"); System.out.println("AckCollector: " + ac); } }.start(); ac.waitForAllAcks(); assertEquals(0, ac.size()); } public void testWaitForAllAcksWithTimeoutException() { try { ac.waitForAllAcks(200); fail("we should get a timeout exception here"); } catch(TimeoutException e) { System.out.println("received timeout exception, as expected"); } } public void testWaitForAllAcksWithTimeout() { new Thread() { public void run() { ac.ack("one"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("two"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("three"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("four"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("five"); System.out.println("AckCollector: " + ac); } }.start(); try { ac.waitForAllAcks(1000); assertTrue("we should not get a timeout exception here", true); } catch(TimeoutException e) { fail("we should not get a timeout exception here"); } assertEquals(0, ac.size()); } public void testWaitForAllAcksWithTimeoutException2() { new Thread() { public void run() { ac.ack("one"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("two"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("three"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("four"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("five"); System.out.println("AckCollector: " + ac); } }.start(); try { ac.waitForAllAcks(300); fail("we should get a timeout exception here"); } catch(TimeoutException e) { assertTrue("we should get a timeout exception here", true); } } public void testReset() { new Thread() { public void run() { Util.sleep(500); System.out.println("resetting AckCollector"); ac.reset(new_list); System.out.println("reset AckCollector: " + ac); } }.start(); System.out.println("initial AckCollector: " + ac); try { ac.waitForAllAcks(1000); fail("needs to throw TimeoutException"); } catch(TimeoutException e) { assertTrue("expected TimeoutException", e != null); } System.out.println("new AckCollector: " + ac); } public void testReset2() throws TimeoutException { new Thread() { public void run() { Util.sleep(500); System.out.println("resetting AckCollector"); ac.reset(new_list); System.out.println("reset AckCollector: " + ac); Util.sleep(100); ac.ack("six"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("seven"); System.out.println("AckCollector: " + ac); Util.sleep(100); ac.ack("eight"); System.out.println("AckCollector: " + ac); } }.start(); System.out.println("initial AckCollector: " + ac); ac.waitForAllAcks(2000); System.out.println("new AckCollector: " + ac); } public void testNullList() throws TimeoutException { AckCollector coll=new AckCollector(); coll.waitForAllAcks(1000); } public void testOneList() throws TimeoutException, UnknownHostException { List tmp=new ArrayList(); Address addr=new IpAddress("127.0.0.1", 5555); tmp.add(addr); AckCollector coll=new AckCollector(tmp); coll.ack(addr); coll.waitForAllAcks(1000); } public static void main(String[] args) { String[] testCaseName={AckCollectorTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/NAKACK_OOB_Test.java0000644000175000017500000001112611366547366031705 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.JChannel; import org.jgroups.Message; import org.jgroups.ReceiverAdapter; import org.jgroups.protocols.DISCARD_PAYLOAD; import org.jgroups.stack.ProtocolStack; import org.jgroups.util.Util; import java.util.LinkedList; import java.util.List; /** * Tests the NAKACK protocol for OOB msgs, tests http://jira.jboss.com/jira/browse/JGRP-379 * @author Bela Ban * @version $Id: NAKACK_OOB_Test.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ */ public class NAKACK_OOB_Test extends TestCase { JChannel ch1, ch2, ch3; final String props="udp.xml"; public NAKACK_OOB_Test(String name) { super(name); } public void setUp() throws Exception { super.setUp(); ch1=new JChannel(props); ch2=new JChannel(props); ch3=new JChannel(props); } public void tearDown() throws Exception { if(ch1 != null) ch1.close(); if(ch2 != null) ch2.close(); if(ch3 != null) ch3.close(); super.tearDown(); } /** * Tests http://jira.jboss.com/jira/browse/JGRP-379: we send 1, 2, 3, 4(OOB) and 5 to the cluster. * Message with seqno 3 is discarded two times, so retransmission will make the receivers receive it *after* 4. * Because 4 is marked as OOB, we will deliver 4 *immediately* (before 3 and 5), so the sequence of the messages * at the receivers is 1 - 2 - 4 -3 - 5. * Note that OOB messages *destroys* FIFO ordering (or whatever ordering properties are set) ! * @throws Exception */ public void testOutOfBandMessages() throws Exception { NAKACK_OOB_Test.MyReceiver receiver1=new NAKACK_OOB_Test.MyReceiver(); NAKACK_OOB_Test.MyReceiver receiver2=new NAKACK_OOB_Test.MyReceiver(); NAKACK_OOB_Test.MyReceiver receiver3=new NAKACK_OOB_Test.MyReceiver(); ch1.setReceiver(receiver1); ch2.setReceiver(receiver2); ch3.setReceiver(receiver3); // all channels will discard messages with seqno #3 two times, the let them pass ch1.getProtocolStack().insertProtocol(new DISCARD_PAYLOAD(), ProtocolStack.BELOW, "NAKACK"); ch2.getProtocolStack().insertProtocol(new DISCARD_PAYLOAD(), ProtocolStack.BELOW, "NAKACK"); ch3.getProtocolStack().insertProtocol(new DISCARD_PAYLOAD(), ProtocolStack.BELOW, "NAKACK"); ch1.connect("x"); ch2.connect("x"); ch3.connect("x"); assertEquals(3, ch3.getView().getMembers().size()); for(int i=1; i <=5; i++) { Message msg=new Message(null, null, new Long(i)); if(i == 4) msg.setFlag(Message.OOB); System.out.println("-- sending message #" + i); ch1.send(msg); Util.sleep(100); } Util.sleep(5000); // wait until retransmission of seqno #3 happens, so that 4 and 5 are received as well List seqnos1=receiver1.getSeqnos(); List seqnos2=receiver2.getSeqnos(); List seqnos3=receiver3.getSeqnos(); System.out.println("sequence numbers:"); System.out.println("ch1: " + seqnos1); System.out.println("ch2: " + seqnos2); System.out.println("ch3: " + seqnos3); // expected sequence is: 1 2 4 3 5 ! Reason: 4 is sent OOB, does *not* wait until 3 has been retransmitted !! Long[] expected_seqnos=new Long[]{new Long(1), new Long(2), new Long(4), new Long(3), new Long(5)}; for(int i=0; i < expected_seqnos.length; i++) { Long expected_seqno=expected_seqnos[i]; Long received_seqno=(Long)seqnos1.get(i); assertEquals(expected_seqno, received_seqno); received_seqno=(Long)seqnos2.get(i); assertEquals(expected_seqno, received_seqno); received_seqno=(Long)seqnos3.get(i); assertEquals(expected_seqno, received_seqno); } } public static class MyReceiver extends ReceiverAdapter { /** List of unicast sequence numbers */ List seqnos=new LinkedList(); public MyReceiver() { } public List getSeqnos() { return seqnos; } public void receive(Message msg) { if(msg != null) { Long num=(Long)msg.getObject(); seqnos.add(num); } } } public static Test suite() { return new TestSuite(NAKACK_OOB_Test.class); } public static void main(String[] args) { junit.textui.TestRunner.run(NAKACK_OOB_Test.suite()); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/UnmodifiableVectorTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/UnmodifiableVectorTest.ja0000644000175000017500000000447111366547366033356 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.UnmodifiableVector; import java.util.Iterator; import java.util.ListIterator; import java.util.Vector; public class UnmodifiableVectorTest extends TestCase { Vector v; UnmodifiableVector uv; public UnmodifiableVectorTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); v=new Vector(); v.add("one"); v.add("two"); uv=new UnmodifiableVector(v); } public void tearDown() throws Exception { super.tearDown(); } public void testCreation() { assertEquals(v.size(), uv.size()); assertTrue(uv.contains("two")); } public void testAddition() { try { uv.add("three"); fail("should throw an exception"); } catch(UnsupportedOperationException ex) { // expected } } public void testRemoval() { try { uv.add("two"); fail("should throw an exception"); } catch(UnsupportedOperationException ex) { // expected } } public void testIteration() { Object el; for(Iterator it=uv.iterator(); it.hasNext();) { el=it.next(); System.out.println(el); } for(Iterator it=uv.iterator(); it.hasNext();) { el=it.next(); try { it.remove(); fail("should throw exception"); } catch(UnsupportedOperationException ex) { // expected } } } public void testListIteration() { Object el; for(ListIterator it=uv.listIterator(); it.hasNext();) { el=it.next(); System.out.println(el); } for(ListIterator it=uv.listIterator(); it.hasNext();) { el=it.next(); try { it.remove(); fail("should throw exception"); } catch(UnsupportedOperationException ex) { // expected } } } public static void main(String[] args) { String[] testCaseName={UnmodifiableVectorTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/TimerTest.java0000644000175000017500000001411311366547366031176 0ustar twernertwerner// $Id: TimerTest.java,v 1.2 2007/08/10 12:32:14 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.stack.Interval; import org.jgroups.stack.StaticInterval; import org.jgroups.util.Util; import java.util.HashMap; import java.util.Timer; import java.util.TimerTask; /** * Test cases for Timer. May fail sometimes, for example if there is GC during the run. Therefore, if run * standalone, the likelyhood of GC is smaller than when run with the entire testsuite * * @author Bela Ban */ public class TimerTest extends TestCase { Timer win=null; static final int NUM_MSGS=1000; long[] xmit_timeouts={1000, 2000, 4000, 8000}; double PERCENTAGE_OFF=0.3; // how much can expected xmit_timeout and real timeout differ to still be okay ? HashMap msgs=new HashMap(); // keys=seqnos (Long), values=Entries class MyTask extends TimerTask { Entry entry; MyTask(Entry entry) { this.entry=entry; } public void run() { entry.retransmit(); } } class Entry { long start_time=0; // time message was added long first_xmit=0; // time between start_time and first_xmit should be ca. 1000ms long second_xmit=0; // time between first_xmit and second_xmit should be ca. 2000ms long third_xmit=0; // time between third_xmit and second_xmit should be ca. 4000ms long fourth_xmit=0; // time between third_xmit and second_xmit should be ca. 8000ms Interval interval=new StaticInterval(xmit_timeouts); long seqno=0; Entry(long seqno) { this.seqno=seqno; start_time=System.currentTimeMillis(); } public void retransmit() { if(first_xmit == 0) first_xmit=System.currentTimeMillis(); else if(second_xmit == 0) second_xmit=System.currentTimeMillis(); else if(third_xmit == 0) third_xmit=System.currentTimeMillis(); else if(fourth_xmit == 0) fourth_xmit=System.currentTimeMillis(); win.schedule(new MyTask(this), interval.next()); } public long next() { return interval.next(); } /** * Entry is correct if xmit timeouts are not more than 30% off the mark */ boolean isCorrect() { long t; long expected; long diff, delta; boolean off=false; t=first_xmit - start_time; expected=xmit_timeouts[0]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=second_xmit - first_xmit; expected=xmit_timeouts[1]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=third_xmit - second_xmit; expected=xmit_timeouts[2]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=fourth_xmit - third_xmit; expected=xmit_timeouts[3]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; if(off) { System.err.println("#" + seqno + ": " + this + ": (" + "entry is more than " + PERCENTAGE_OFF + " percentage off "); return false; } return true; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(first_xmit - start_time).append(", ").append(second_xmit - first_xmit).append(", "); sb.append(third_xmit - second_xmit).append(", ").append(fourth_xmit - third_xmit); return sb.toString(); } } public TimerTest(String name) { super(name); } /** * Tests whether retransmits are called at correct times for 1000 messages. A retransmit should not be * more than 30% earlier or later than the scheduled retransmission time */ public void testRetransmits() { Entry entry; MyTask task; int num_non_correct_entries=0; win=new Timer(); // 1. Add NUM_MSGS messages: System.out.println("-- adding " + NUM_MSGS + " messages:"); for(long i=0; i < NUM_MSGS; i++) { entry=new Entry(i); task=new MyTask(entry); msgs.put(new Long(i), entry); win.schedule(task, entry.next()); } System.out.println("-- done"); // 2. Wait for at least 4 xmits/msg: total of 1000 + 2000 + 4000 + 8000ms = 15000ms; wait for 20000ms System.out.println("-- waiting for 20 secs for all retransmits"); Util.sleep(20000); // 3. Check whether all Entries have correct retransmission times for(long i=0; i < NUM_MSGS; i++) { entry=(Entry)msgs.get(new Long(i)); if(!entry.isCorrect()) { num_non_correct_entries++; } } if(num_non_correct_entries > 0) System.err.println("Number of incorrect retransmission timeouts: " + num_non_correct_entries); else { for(long i=0; i < NUM_MSGS; i++) { entry=(Entry)msgs.get(new Long(i)); if(entry != null) System.out.println(i + ": " + entry); } } assertSame(0, num_non_correct_entries); try { win.cancel(); } catch(Exception ex) { System.err.println(ex); } } public static Test suite() { TestSuite suite; suite=new TestSuite(TimerTest.class); return (suite); } public static void main(String[] args) { String[] name={TimerTest.class.getName()}; junit.textui.TestRunner.main(name); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/UNICAST_Test.java0000644000175000017500000001220311366547366031361 0ustar twernertwerner// $Id: UNICAST_Test.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.debug.Simulator; import org.jgroups.protocols.DISCARD; import org.jgroups.protocols.UNICAST; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import java.nio.ByteBuffer; import java.util.Properties; import java.util.Vector; /** * Tests the UNICAST protocol * @author Bela Ban */ public class UNICAST_Test extends TestCase { IpAddress a1, a2; Vector members; View v; Simulator simulator; final int SIZE=1000; // bytes final int NUM_MSGS=10000; public UNICAST_Test(String name) { super(name); } public void setUp() throws Exception { super.setUp(); } public void tearDown() throws Exception { super.tearDown(); if(simulator != null) simulator.stop(); } public void testReceptionOfAllMessages() throws Throwable { UNICAST unicast=new UNICAST(); Properties props=new Properties(); props.setProperty("timeout", "500,1000,2000,3000"); unicast.setProperties(props); Protocol[] stack=new Protocol[]{unicast}; createStack(stack); _testReceptionOfAllMessages(); } public void testReceptionOfAllMessagesWithDISCARD() throws Throwable { UNICAST unicast=new UNICAST(); Properties props=new Properties(); props.setProperty("timeout", "500,1000,2000,3000"); unicast.setProperties(props); DISCARD discard=new DISCARD(); props.clear(); props.setProperty("down", "0.1"); // discard all down message with 10% probability discard.setProperties(props); Protocol[] stack=new Protocol[]{unicast,discard}; createStack(stack); _testReceptionOfAllMessages(); } private static byte[] createPayload(int size, int seqno) { ByteBuffer buf=ByteBuffer.allocate(size); buf.putInt(seqno); return buf.array(); } /** Checks that messages 1 - NUM_MSGS are received in order */ class Receiver implements Simulator.Receiver { int num_mgs_received=0, next=1; Throwable exception=null; boolean received_all=false; public void receive(Event evt) { if(evt.getType() == Event.MSG) { if(exception != null) return; Message msg=(Message)evt.getArg(); ByteBuffer buf=ByteBuffer.wrap(msg.getRawBuffer()); int seqno=buf.getInt(); if(seqno != next) { exception=new Exception("expected seqno was " + next + ", but received " + seqno); return; } next++; num_mgs_received++; if(num_mgs_received % 1000 == 0) System.out.println("<== " + num_mgs_received); if(num_mgs_received == NUM_MSGS) { synchronized(this) { received_all=true; this.notifyAll(); } } } } public int getNumberOfReceivedMessages() { return num_mgs_received; } public boolean receivedAll() {return received_all;} public Throwable getException() { return exception; } } private void _testReceptionOfAllMessages() throws Throwable { int num_received=0; Receiver r=new Receiver(); simulator.setReceiver(r); for(int i=1; i <= NUM_MSGS; i++) { Message msg=new Message(a1, null, createPayload(SIZE, i)); // unicast message Event evt=new Event(Event.MSG, msg); simulator.send(evt); if(i % 1000 == 0) System.out.println("==> " + i); } int num_tries=10; while((num_received=r.getNumberOfReceivedMessages()) != NUM_MSGS && num_tries > 0) { if(r.getException() != null) throw r.getException(); synchronized(r) { try {r.wait(3000);} catch(InterruptedException e) {} } num_tries--; } printStats(num_received); assertEquals(num_received, NUM_MSGS); } private void createStack(Protocol[] stack) throws Exception { a1=new IpAddress(1111); members=new Vector(); members.add(a1); v=new View(a1, 1, members); simulator=new Simulator(); simulator.setLocalAddress(a1); simulator.setView(v); simulator.addMember(a1); simulator.setProtocolStack(stack); simulator.start(); } private void printStats(int num_received) { System.out.println("-- num received=" + num_received + ", stats:\n" + simulator.dumpStats()); } public static Test suite() { return new TestSuite(UNICAST_Test.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/TupleTest.java0000644000175000017500000000256411366547366031216 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.Tuple; import java.util.HashMap; import java.util.Map; /** * @author Bela Ban * @version $Id: TupleTest.java,v 1.1.2.2 2008/01/21 11:36:17 belaban Exp $ */ public class TupleTest extends TestCase { public void testCreation() { Tuple tuple=new Tuple("Bela", 322649); System.out.println("tuple: " + tuple); assertEquals("Bela", tuple.getVal1()); assertEquals(322649, tuple.getVal2().intValue()); } public void testSet() { Tuple tuple=new Tuple("Bela", 322649); System.out.println("tuple: " + tuple); tuple.setVal1("Michelle"); tuple.setVal2(7); assertEquals("Michelle", tuple.getVal1()); assertEquals(7, tuple.getVal2().intValue()); } public void testHashMap() { Map> map=new HashMap>(); map.put(1, new Tuple("one",1)); map.put(2, new Tuple("two", 2)); System.out.println("map: " + map); assertEquals("one", map.get(1).getVal1()); assertEquals(1, map.get(1).getVal2().intValue()); assertEquals("two", map.get(2).getVal1()); assertEquals(2, map.get(2).getVal2().intValue()); } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/XmlConfigurationTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/XmlConfigurationTest.java0000644000175000017500000000207711366547366033414 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jgroups.conf.XmlConfigurator; import org.jgroups.util.Util; import java.io.InputStream; public class XmlConfigurationTest extends TestCase { protected Log log=LogFactory.getLog(getClass()); public XmlConfigurationTest(String Name_) { super(Name_); } public void testBasic() { try { InputStream default_config=Util.getResourceAsStream("udp.xml", this.getClass()); XmlConfigurator conf=XmlConfigurator.getInstance(default_config); if(log.isDebugEnabled()) log.debug(conf.getProtocolStackString()); assertTrue("Successfully parsed a valid XML configuration file.", true); } catch(Exception x) { fail(x.getMessage()); } } public static void main(String[] args) { String[] testCaseName={XmlConfigurationTest.class.getName()}; junit.swingui.TestRunner.main(testCaseName); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ExponentialIntervalTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ExponentialIntervalTest.j0000644000175000017500000000405311366547366033423 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.stack.ExponentialInterval; /** * @author Bela Ban * @version $Id: ExponentialIntervalTest.java,v 1.2 2007/08/21 07:18:12 belaban Exp $ */ public class ExponentialIntervalTest extends TestCase { ExponentialInterval interval; public ExponentialIntervalTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); } public void tearDown() throws Exception { super.tearDown(); } public void testInitialization() { interval=new ExponentialInterval(10); System.out.println("interval=" + interval); long value=interval.next(); System.out.println("interval=" + interval); assertEquals(10, value); value=interval.next(); System.out.println("interval=" + interval); assertEquals(20, value); } public void testNoargConstructor() { interval=new ExponentialInterval(); assertEquals(30, interval.next()); assertEquals(60, interval.next()); } public void testMax() { interval=new ExponentialInterval(1000); System.out.println("interval=" + interval); assertEquals(1000, interval.next()); System.out.println("interval=" + interval); assertEquals(2000, interval.next()); System.out.println("interval=" + interval); assertEquals(4000, interval.next()); System.out.println("interval=" + interval); assertEquals(8000, interval.next()); System.out.println("interval=" + interval); assertEquals(15000, interval.next()); System.out.println("interval=" + interval); assertEquals(15000, interval.next()); System.out.println("interval=" + interval); } public static Test suite() { return new TestSuite(ExponentialIntervalTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } }././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/FIFOMessageQueueTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/FIFOMessageQueueTest.java0000644000175000017500000004144411366547366033162 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import org.jgroups.util.FIFOMessageQueue; import org.jgroups.util.Util; import java.util.Collections; import java.util.LinkedList; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * @author Bela Ban * @version $Id: FIFOMessageQueueTest.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ */ public class FIFOMessageQueueTest extends TestCase { FIFOMessageQueue queue; String s1="s1", s2="s2", s3="s3"; private static final Address a1, a2; static { a1=new IpAddress(5000); a2=new IpAddress(6000); } public void setUp() throws Exception { super.setUp(); queue=new FIFOMessageQueue(); } public void tearDown() throws Exception { super.tearDown(); } public void testPollFromEmptyQueue() throws InterruptedException { assertEquals(0, queue.size()); Integer ret=queue.poll(5); assertNull(ret); assertEquals("queue.size() should be 0, but is " + queue.size(), 0, queue.size()); } public void testPutTwoTakeTwo() throws InterruptedException { queue.put(a1, s1, 1); // 1 is available immediately queue.put(a1, s1, 2); // 2 is queued Integer ret=queue.poll(5); assertNotNull(ret); queue.done(a1, s1); // 2 is made available (moved into 'queue') queue.done(a1, s1); // done() by the first putter ret=queue.poll(5); assertNotNull(ret); assertEquals(0, queue.size()); queue.put(a1, s1, 3); assertEquals(1, queue.size()); ret=queue.poll(5); // 3 should be available because queue for a1/s1 was empty assertNotNull(ret); } public void testTakeFollowedByPut() throws InterruptedException { assertEquals(0, queue.size()); new Thread() { public void run() { Util.sleep(1000); try { queue.put(a1, s1, 1); } catch(InterruptedException e) { } } }.start(); Integer ret=queue.take(); assertNotNull(ret); assertEquals(1, ret.intValue()); assertEquals("queue.size() should be 0, but is " + queue.size(), 0, queue.size()); } public void testMultipleTakersOnePutter() throws Exception { final CyclicBarrier barrier=new CyclicBarrier(11); for(int i=0; i < 10; i++) { new Thread() { public void run() { try { barrier.await(); queue.take(); } catch(Exception e) { } } }.start(); } barrier.await(); for(int i=0; i < 10; i++) { queue.put(a1, s1, i); queue.done(a1, s1); } Util.sleep(100); assertEquals(0, queue.size()); } public void testConcurrentPutsAndTakes() throws InterruptedException { final int NUM=10000; final int print=NUM / 10; Thread putter=new Thread() { public void run() { setName("Putter"); int cnt=0; for(int i=0; i < NUM; i++) { try { queue.put(a1, s1, i); cnt++; if(cnt % print == 0) { System.out.println("Putter: " + cnt); } queue.done(a1, s1); } catch(InterruptedException e) { e.printStackTrace(); } } } }; Thread taker=new Thread() { public void run() { setName("Taker"); int cnt=0; for(int i=0; i < NUM; i++) { try { queue.take(); cnt++; if(cnt % print == 0) { System.out.println("Taker: " + cnt); } } catch(InterruptedException e) { e.printStackTrace(); } } } }; System.out.println("starting threads"); taker.start(); putter.start(); new Thread() { public void run() { Util.sleep(3000); System.out.println("queue:\n" + queue); } }.start(); putter.join(); taker.join(); assertEquals(0, queue.size()); } public void testNullAddress() throws InterruptedException { queue.put(null, s1, 1); queue.put(a1, s1, 2); queue.put(a1, s1, 3); queue.put(null, s1, 4); System.out.println("queue:\n" + queue); Integer ret=queue.poll(5); assertNotNull(ret); assertEquals(1, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(2, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(4, ret.intValue()); ret=queue.poll(5); assertNull(ret); queue.done(a1, s1); ret=queue.poll(5); assertNotNull(ret); assertEquals(3, ret.intValue()); ret=queue.poll(5); assertNull(ret); assertEquals(0, queue.size()); } public void testSimplePutAndTake() throws InterruptedException { queue.put(a1, s1, 1); assertEquals(1, queue.size()); int ret=queue.take(); assertEquals(1, ret); assertEquals(0, queue.size()); } public void testSimplePutAndTakeMultipleSenders() throws InterruptedException { queue.put(a1, s1, 1); queue.put(a2, s1, 2); System.out.println("queue is:\n" + queue); assertEquals(2, queue.size()); int ret=queue.take(); assertEquals(1, ret); assertEquals(1, queue.size()); ret=queue.take(); assertEquals(2, ret); assertEquals(0, queue.size()); } public void testMultiplePutsAndTakes() throws InterruptedException { for(int i=1; i <= 5; i++) queue.put(a1, s1, i); System.out.println("queue is " + queue); assertEquals(5, queue.size()); for(int i=1; i <= 5; i++) { int ret=queue.take(); assertEquals(i, ret); assertEquals(5-i, queue.size()); queue.done(a1, s1); } assertEquals(0, queue.size()); } /** * Sender A sends M1 to S1 and M2 to S1. M2 should wait until M1 is done */ public void testSameSenderSameDestination() throws InterruptedException { queue.put(a1, s1, 1); queue.put(a1, s1, 2); queue.put(a1, s1, 3); System.out.println("queue:\n" + queue); assertEquals(3, queue.size()); int ret=queue.take(); assertEquals(1, ret); Integer retval=queue.poll(100); assertNull(retval); queue.done(a1, s1); System.out.println("queue:\n" + queue); ret=queue.take(); assertEquals(2, ret); queue.done(a1, s1); System.out.println("queue:\n" + queue); ret=queue.take(); System.out.println("queue:\n" + queue); assertEquals(3, ret); } /** * Sender A sends M1 to S1 and M2 to S2. M2 should get processed immediately and not have * to wait for M1 to complete */ public void testSameSenderMultipleDestinations() throws InterruptedException { queue.put(a1, s1, 10); queue.put(a1, s1, 11); queue.put(a1, s1, 12); queue.put(a1, s2, 20); queue.put(a1, s2, 21); queue.put(a1, s2, 22); queue.put(a1, s3, 30); queue.put(a1, s3, 31); queue.put(a1, s3, 32); System.out.println("queue:\n" + queue); Integer ret=queue.poll(5); assertNotNull(ret); assertEquals(10, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(20, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(30, ret.intValue()); ret=queue.poll(5); assertNull(ret); queue.done(a1, s3); queue.done(a1, s1); queue.done(a1, s2); ret=queue.poll(5); assertNotNull(ret); assertEquals(31, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(11, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(21, ret.intValue()); ret=queue.poll(5); assertNull(ret); assertEquals(3, queue.size()); ret=queue.poll(5); assertNull(ret); queue.done(a1, s1); queue.done(a1, s3); queue.done(a1, s2); ret=queue.poll(5); assertNotNull(ret); assertEquals(12, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(32, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(22, ret.intValue()); ret=queue.poll(5); assertNull(ret); assertEquals(0, queue.size()); } /** * Sender A sends M1 to S1 and sender B sends M2 to S1. M2 should get processed concurrently to M1 and * should not have to wait for M1's completion */ public void testDifferentSendersSameDestination() throws InterruptedException { queue.put(a1, s1, 10); queue.put(a2, s1, 20); queue.put(a1, s1, 11); queue.put(a2, s1, 21); System.out.println("queue:\n" + queue); assertEquals(4, queue.size()); Integer ret=queue.poll(5); assertNotNull(ret); assertEquals(10, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(20, ret.intValue()); queue.done(a1, s1); ret=queue.poll(5); assertNotNull(ret); assertEquals(11, ret.intValue()); queue.done(a2, s1); ret=queue.poll(5); assertNotNull(ret); assertEquals(21, ret.intValue()); ret=queue.poll(5); assertNull(ret); assertEquals(0, queue.size()); } /** * Sender A sends M1 to S1 and sender B sends M2 to S2. M1 and M2 should get processed concurrently */ public void testDifferentSendersDifferentDestinations() throws Exception { queue.put(a1, s1, 1); queue.put(a2, s2, 2); queue.put(a1, s2, 3); queue.put(a2, s1, 4); System.out.println("queue:\n" + queue); assertEquals(4, queue.size()); Integer ret=queue.poll(5); assertNotNull(ret); assertEquals(1, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(2, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(3, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(4, ret.intValue()); ret=queue.poll(5); assertNull(ret); assertEquals(0, queue.size()); } public void testDifferentSendersDifferentDestinationsMultipleMessages() throws Exception { queue.put(a1, s1, 1); queue.put(a2, s2, 2); queue.put(a1, s2, 3); queue.put(a2, s1, 4); queue.put(a1, s1, 5); queue.put(a2, s2, 6); queue.put(a1, s2, 7); queue.put(a2, s1, 8); System.out.println("queue:\n" + queue); assertEquals(8, queue.size()); Integer ret=queue.poll(5); assertNotNull(ret); assertEquals(1, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(2, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(3, ret.intValue()); ret=queue.poll(5); assertNotNull(ret); assertEquals(4, ret.intValue()); queue.done(a1, s1); ret=queue.poll(5); assertNotNull(ret); assertEquals(5, ret.intValue()); queue.done(a2, s2); ret=queue.poll(5); assertNotNull(ret); assertEquals(6, ret.intValue()); queue.done(a1, s2); ret=queue.poll(5); assertNotNull(ret); assertEquals(7, ret.intValue()); queue.done(a2, s1); ret=queue.poll(5); assertNotNull(ret); assertEquals(8, ret.intValue()); } public void testOrdering() throws InterruptedException { for(int i=1; i <= 3; i++) queue.put(a1, s1, i); assertEquals(3, queue.size()); int ret=queue.take(); assertEquals(1, ret); assertEquals(2, queue.size()); queue.done(a1, s1); queue.put(a1, s1, 4); queue.put(a1, s1, 5); System.out.println("queue: " + queue); for(int i=2; i <= 5; i++) { ret=queue.take(); assertEquals(i, ret); assertEquals(5-i, queue.size()); queue.done(a1, s1); } assertEquals(0, queue.size()); } public void testOrderingMultipleThreads() throws BrokenBarrierException, InterruptedException { CyclicBarrier barrier=new CyclicBarrier(4); int NUM=500; Producer p1=new Producer(queue, "s1", 1, NUM, barrier); Producer p2=new Producer(queue, "s2", 1001, NUM, barrier); Producer p3=new Producer(queue, "s3", 2001, NUM, barrier); p1.start(); p2.start(); p3.start(); Util.sleep(100); barrier.await(); // starts all 3 threads p1.join(); p2.join(); p3.join(); System.out.println("queue: " + queue.size() + " elements"); assertEquals(NUM * 3, queue.size()); } public void testOrderingMultipleThreadsWithTakes() throws BrokenBarrierException, InterruptedException { testOrderingMultipleThreads(); int ret; LinkedList list=new LinkedList(); int size=queue.size(); for(int i=0; i < size; i++) { ret=queue.take(); list.add(ret); queue.done(a1, "s1"); queue.done(a1, "s2"); queue.done(a1, "s3"); } System.out.println("analyzing returned values for correct ordering"); LinkedList one=new LinkedList(), two=new LinkedList(), three=new LinkedList(); for(int val: list) { if(val < 1000) { one.add(val); continue; } if(val > 1000 && val <= 2000) { two.add(val); continue; } if(val > 2000) { three.add(val); } } int len=one.size(); assertEquals(len, two.size()); assertEquals(len, three.size()); LinkedList sorted_one=new LinkedList(one); Collections.sort(sorted_one); assertEquals("one: " + one + ", sorted: " + sorted_one, one, sorted_one); LinkedList sorted_two=new LinkedList(two); Collections.sort(sorted_two); assertEquals("two: " + two + ", sorted: " + sorted_two, two, sorted_two); LinkedList sorted_three=new LinkedList(three); Collections.sort(sorted_three); assertEquals("three: " + three + ", sorted: " + sorted_three, three, sorted_three); System.out.println("OK - all 3 collections are ordered"); } private static class Producer extends Thread { private FIFOMessageQueue queue; private String key; private int num_msgs; private CyclicBarrier barrier; private int start_num; private Producer(FIFOMessageQueue queue, String key, int start_num, int num_msgs, CyclicBarrier barrier) { this.queue=queue; this.key=key; this.start_num=start_num; this.num_msgs=num_msgs; this.barrier=barrier; } public void run() { try { barrier.await(); } catch(Exception e) { e.printStackTrace(); } for(int i=start_num; i <= num_msgs+start_num-1; i++) { try { // Util.sleepRandom(50); queue.put(a1, key, i); } catch(InterruptedException e) { e.printStackTrace(); } } } } public static junit.framework.Test suite() { return new TestSuite(FIFOMessageQueueTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/UtilTest.java0000644000175000017500000004435211366547366031043 0ustar twernertwerner// $Id: UtilTest.java,v 1.4 2007/08/20 09:22:54 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.stack.IpAddress; import org.jgroups.util.Buffer; import org.jgroups.util.Util; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Vector; public class UtilTest extends TestCase { static { try { ClassConfigurator.getInstance(true); } catch(ChannelException e) { e.printStackTrace(); } } public UtilTest(String name) { super(name); } public void testGetProperty() { Properties props=new Properties(); props.setProperty("name", "Bela"); props.setProperty("key", "val"); System.setProperty("name", "Michelle"); System.setProperty("name2", "Nicole"); String retval; retval=Util.getProperty(new String[]{"name", "name2"}, props, "name", false, "Jeannette"); assertEquals("Michelle", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name", false, "Jeannette"); assertEquals("Nicole", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name3", "name"}, props, "name", false, "Jeannette"); assertEquals("Michelle", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name3", "name4"}, props, "name", false, "Jeannette"); assertEquals("Bela", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name", true, "Jeannette"); assertEquals("Bela", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name2", true, "Jeannette"); assertEquals("Jeannette", retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); retval=Util.getProperty(new String[]{"name2", "name"}, props, "name2", true, null); assertNull(retval); props.setProperty("name", "Bela"); props.setProperty("key", "val"); } public void testIgnoreBindAddress() { boolean retval; retval=Util.isBindAddressPropertyIgnored(); assertFalse(retval); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "true"); retval=Util.isBindAddressPropertyIgnored(); assertTrue(retval); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "true2"); retval=Util.isBindAddressPropertyIgnored(); assertFalse(retval); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "false"); retval=Util.isBindAddressPropertyIgnored(); assertFalse(retval); System.getProperties().remove(Global.IGNORE_BIND_ADDRESS_PROPERTY); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD, "false"); retval=Util.isBindAddressPropertyIgnored(); assertFalse(retval); System.getProperties().remove(Global.IGNORE_BIND_ADDRESS_PROPERTY); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD, "true"); retval=Util.isBindAddressPropertyIgnored(); assertTrue(retval); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY, "true"); System.setProperty(Global.IGNORE_BIND_ADDRESS_PROPERTY_OLD, "true"); retval=Util.isBindAddressPropertyIgnored(); assertTrue(retval); } public void testPrintBytes() { long num; String s; num=1; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1b", s); num=999; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("999b", s); num=1000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1KB", s); num=1001; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1KB", s); num=1010; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1.01KB", s); num=1543; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1.54KB", s); num=10000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("10KB", s); num=150000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("150KB", s); num=150023; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("150.02KB", s); num=1200000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1.2MB", s); num=150000000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("150MB", s); num=150030000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("150.03MB", s); num=1200000000; s=Util.printBytes(num); System.out.println(num + " is " + s); assertEquals("1.2GB", s); } public void testObjectToFromByteBuffer() throws Exception { byte[] buf; IpAddress addr=new IpAddress("localhost", 5000), addr2; List list=new ArrayList(), list2; list.add("Bela"); list.add("Jeannette"); buf=Util.objectToByteBuffer(addr); addr2=(IpAddress)Util.objectFromByteBuffer(buf); System.out.println("addr=" + addr + ", addr2=" + addr2); assertEquals(addr, addr2); buf=Util.objectToByteBuffer(list); list2=(List)Util.objectFromByteBuffer(buf); System.out.println("list=" + list + ", list2=" + list2); assertEquals(list, list2); Object obj=null; buf=Util.objectToByteBuffer(obj); assertNotNull(buf); assertTrue(buf.length > 0); obj=Util.objectFromByteBuffer(buf); assertNull(obj); Object[] values=new Object[]{ Boolean.TRUE, Boolean.FALSE, new Byte((byte)22), new Byte("2"), new Character('5'), new Double(3.14), new Float(352.3), new Integer(100), new Long(322649), new Short((short)22), "Bela Ban" }; for(int i=0; i < values.length; i++) { Object value=values[i]; marshal(value); } } public void testMessageToByteBuffer() throws Exception { _testMessage(new Message()); _testMessage(new Message(null, null, "hello world")); _testMessage(new Message(null, new IpAddress("localhost", 5000), null)); _testMessage(new Message(null, new IpAddress("localhost", 5000), null)); _testMessage(new Message(null, new IpAddress("localhost", 5000), "bela")); } private static void _testMessage(Message msg) throws Exception { Buffer buf=Util.messageToByteBuffer(msg); Message msg2=Util.byteBufferToMessage(buf.getBuf(), buf.getOffset(), buf.getLength()); assertEquals(msg.getSrc(), msg2.getSrc()); assertEquals(msg.getDest(), msg2.getDest()); assertEquals(msg.getLength(), msg2.getLength()); } public void testObjectToByteArrayWithLargeString() throws Exception { marshalString(Short.MAX_VALUE ); } public void testObjectToByteArrayWithLargeString2() throws Exception { marshalString(Short.MAX_VALUE - 100); } public void testObjectToByteArrayWithLargeString3() throws Exception { marshalString(Short.MAX_VALUE + 1); } public void testObjectToByteArrayWithLargeString4() throws Exception { marshalString(Short.MAX_VALUE + 100); } public void testObjectToByteArrayWithLargeString5() throws Exception { marshalString(Short.MAX_VALUE + 100000); } private static void marshalString(int size) throws Exception { byte[] tmp=new byte[size]; String str=new String(tmp, 0, tmp.length); byte[] retval=Util.objectToByteBuffer(str); System.out.println("length=" + retval.length + " bytes"); String obj=(String)Util.objectFromByteBuffer(retval); System.out.println("read " + obj.length() + " string"); } static void marshal(Object obj) throws Exception { byte[] buf=Util.objectToByteBuffer(obj); assertNotNull(buf); assertTrue(buf.length > 0); Object obj2=Util.objectFromByteBuffer(buf); System.out.println("obj=" + obj + ", obj2=" + obj2 + " (type=" + obj.getClass().getName() + ", length=" + buf.length + " bytes)"); assertEquals(obj, obj2); } public void testWriteStreamable() throws IOException, IllegalAccessException, InstantiationException { Message m=new Message(null, null, "Hello"); ViewId vid=new ViewId(null, 12345); ViewId vid2=new ViewId(new IpAddress("127.0.0.1", 5555), 35623); ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeGenericStreamable(m, dos); Util.writeGenericStreamable(vid, dos); Util.writeGenericStreamable(vid2, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); Message m2=(Message)Util.readGenericStreamable(dis); ViewId v3=(ViewId)Util.readGenericStreamable(dis); ViewId v4=(ViewId)Util.readGenericStreamable(dis); assertNotNull(m2.getBuffer()); assertEquals(m.getLength(), m2.getLength()); assertNotNull(v3); assertEquals(vid, v3); assertNotNull(v4); assertEquals(vid2, v4); } public void testWriteViewIdWithNullCoordinator() throws IOException, IllegalAccessException, InstantiationException { ViewId vid=new ViewId(null, 12345); ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeGenericStreamable(vid, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); ViewId v4=(ViewId)Util.readGenericStreamable(dis); assertEquals(vid, v4); } public void testWriteView() throws IOException, IllegalAccessException, InstantiationException { ViewId vid=new ViewId(null, 12345); Vector members=new Vector(); View v; IpAddress a1=new IpAddress("localhost", 1234); IpAddress a2=new IpAddress("127.0.0.1", 4444); IpAddress a4=new IpAddress("127.0.0.1", 7777); members.add(a1); members.add(a2); members.add(a4); v=new View(vid, members); ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeGenericStreamable(v, dos); Util.writeStreamable(v, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); View v2=(View)Util.readGenericStreamable(dis); assertEquals(v, v2); v2=(View)Util.readStreamable(View.class, dis); assertEquals(v, v2); } public void testWriteString() throws IOException { String s1="Bela Ban", s2="Michelle Ban"; ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeString(s1, dos); Util.writeString(s2, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); String s3=Util.readString(dis); String s4=Util.readString(dis); assertEquals(s1, s3); assertEquals(s2, s4); } public static void writeAddress() throws IOException, IllegalAccessException, InstantiationException { IpAddress a1=new IpAddress("localhost", 1234); IpAddress a2=new IpAddress("127.0.0.1", 4444); IpAddress a3=new IpAddress("thishostdoesnexist", 6666); IpAddress a4=new IpAddress("127.0.0.1", 7777); ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeAddress(a1, dos); Util.writeAddress(a2, dos); Util.writeAddress(a3, dos); Util.writeAddress(a4, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); assertEquals(a1, Util.readAddress(dis)); assertEquals(a2, Util.readAddress(dis)); assertEquals(a3, Util.readAddress(dis)); assertEquals(a4, Util.readAddress(dis)); } public static void writeNullAddress() throws IOException, IllegalAccessException, InstantiationException { IpAddress a1=null; ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeAddress(a1, dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); assertNull(Util.readAddress(dis)); } public void testWriteByteBuffer() throws IOException { byte[] buf=new byte[1024], tmp; for(int i=0; i < buf.length; i++) buf[i]=0; ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); Util.writeByteBuffer(buf, dos); dos.close(); tmp=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(tmp); DataInputStream dis=new DataInputStream(instream); byte[] buf2=Util.readByteBuffer(dis); assertNotNull(buf2); assertEquals(buf.length, buf2.length); } public void testMatch() { long[] a={1,2,3}; long[] b={2,3,4}; long[] c=null; long[] d={1,2,3,4}; long[] e={1,2,3}; assertTrue(Util.match(a,a)); assertFalse(Util.match(a,b)); assertFalse(Util.match(a,c)); assertFalse(Util.match(a,d)); assertTrue(Util.match(a,e)); assertTrue(Util.match(c,c)); assertFalse(Util.match(c, a)); } public void testPickRandomElement() { Vector v=new Vector(); for(int i=0; i < 10; i++) { v.add(new Integer(i)); } Integer el; for(int i=0; i < 10000; i++) { el=(Integer)Util.pickRandomElement(v); assertTrue(el.intValue() >= 0 && el.intValue() < 10); } } public void testAll() { ArrayList l=new ArrayList(); l.add("one"); l.add("two"); l.add("one"); System.out.println("-- list is " + l); assertFalse(Util.all(l, "one")); l.remove("two"); System.out.println("-- list is " + l); assertTrue(Util.all(l, "one")); } public void testParseCommaDelimitedString() { String input="1,2,3,4,5,6,7,8,9,10 , 11, 12 ,13"; List list=Util.parseCommaDelimitedStrings(input); System.out.println("list: " + list); assertEquals(13, list.size()); assertEquals("1", list.get(0)); assertEquals("13", list.get(list.size() -1)); } public void testParseSemicolonDelimitedString() { String input="one;two ; three; four ; five;six"; List list=Util.parseStringList(input, ";"); System.out.println("list: " + list); assertEquals(6, list.size()); assertEquals("one", list.get(0)); assertEquals("six", list.get(list.size() -1)); } public void testParseSemicolonDelimitedString2() { String input=" myID1::subID1 ; myID2::mySubID2; myID3 ;myID4::blaSubID4"; List list=Util.parseStringList(input, ";"); System.out.println("list: " + list); assertEquals(4, list.size()); assertEquals("myID1::subID1", list.get(0)); assertEquals("myID4::blaSubID4", list.get(list.size() -1)); } public void testVariableSubstitution() { String val="hello world", replacement; replacement=Util.substituteVariable(val); assertEquals(val, replacement); // no substitution val="my name is ${user.name}"; replacement=Util.substituteVariable(val); assertNotSame(val, replacement); assertFalse(val.equals(replacement)); val="my name is ${user.name} and ${user.name}"; replacement=Util.substituteVariable(val); assertFalse(val.equals(replacement)); assertEquals(-1, replacement.indexOf("${")); val="my name is ${unknown.var:Bela Ban}"; replacement=Util.substituteVariable(val); assertTrue(replacement.contains("Bela Ban")); assertEquals(-1, replacement.indexOf("${")); val="my name is ${unknown.var}"; replacement=Util.substituteVariable(val); assertTrue(replacement.contains("${")); val="here is an invalid ${argument because it doesn't contains a closing bracket"; try { replacement=Util.substituteVariable(val); fail("should be an IllegalArgumentException"); } catch(Throwable t) { assertEquals(IllegalArgumentException.class, t.getClass()); } } public static Test suite() { return new TestSuite(UtilTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/NakackTest.java0000644000175000017500000001114211366547366031305 0ustar twernertwerner// $Id: NakackTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.*; import org.jgroups.debug.ProtocolTester; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import java.util.Hashtable; import java.util.Vector; public class NakackTest extends TestCase { static final long WAIT_TIME=5000; public static final long NUM_MSGS=10000; long num_msgs_received=0; long num_msgs_sent=0; public NakackTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); num_msgs_received=0; num_msgs_sent=0; } public void test0() throws Exception { Object mutex=new Object(); CheckNoGaps check=new CheckNoGaps(1, this, mutex); ProtocolTester t=new ProtocolTester("pbcast.NAKACK", check); Address my_addr=new IpAddress("localhost", 10000); ViewId vid=new ViewId(my_addr, 322649); Vector mbrs=new Vector(); View view; mbrs.addElement(my_addr); view=new View(vid, mbrs); t.start(); t.getBottom().up(new Event(Event.SET_LOCAL_ADDRESS, my_addr)); check.down(new Event(Event.BECOME_SERVER)); check.down(new Event(Event.VIEW_CHANGE, view)); for(long i=1; i <= NUM_MSGS; i++) { if(i % 1000 == 0) System.out.println("sending msg #" + i); check.down(new Event(Event.MSG, new Message(null, my_addr, new Long(i)))); num_msgs_sent++; } synchronized(mutex) { while(!check.isDone()) mutex.wait(WAIT_TIME); } System.out.println("\nMessages sent: " + num_msgs_sent + ", messages received: " + num_msgs_received); assertEquals(num_msgs_received, num_msgs_sent); t.stop(); } public static Test suite() { return new TestSuite(NakackTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } private static class CheckNoGaps extends Protocol { long starting_seqno=1; Hashtable senders=new Hashtable(); // sender --> highest seqno received so far NakackTest t=null; final Object mut; long highest_seqno=starting_seqno; boolean done=false; CheckNoGaps(long seqno, NakackTest t, Object mut) { starting_seqno=seqno; this.t=t; this.mut=mut; } public String getName() { return "CheckNoGaps"; } public boolean isDone() { return done; } public Object up(Event evt) { Message msg=null; Address sender; long received_seqno; Long s; if(evt == null) return null; if(evt.getType() == Event.SET_LOCAL_ADDRESS) { System.out.println("local address is " + evt.getArg()); } if(evt.getType() != Event.MSG) return null; msg=(Message)evt.getArg(); sender=msg.getSrc(); if(sender == null) { log.error("NakackTest.CheckNoGaps.up(): sender is null; discarding msg"); return null; } s=(Long)senders.get(sender); if(s == null) { s=new Long(starting_seqno); senders.put(sender, s); } try { s=(Long)msg.getObject(); received_seqno=s.longValue(); if(received_seqno == highest_seqno) { // correct if(received_seqno % 1000 == 0 && received_seqno > 0) System.out.println("PASS: received msg #" + received_seqno); senders.put(sender, new Long(highest_seqno)); highest_seqno++; synchronized(mut) { t.num_msgs_received++; if(t.num_msgs_received >= NakackTest.NUM_MSGS) { done=true; mut.notifyAll(); } } } else { // error, terminate test fail("FAIL: received msg #" + received_seqno + ", expected " + highest_seqno); } } catch(Exception ex) { log.error("NakackTest.CheckNoGaps.up()", ex); } return null; } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckReceiverWindowTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckReceiverWindowTest.jav0000644000175000017500000001107011366547366033327 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Message; import org.jgroups.stack.AckReceiverWindow; /** * @author Bela Ban * @version $Id: AckReceiverWindowTest.java,v 1.1.4.6 2009/09/18 07:58:29 belaban Exp $ */ public class AckReceiverWindowTest extends TestCase { AckReceiverWindow win; public AckReceiverWindowTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); win=new AckReceiverWindow(1); } public void tearDown() throws Exception { win.reset(); super.tearDown(); } public void testAdd() { win.reset(); win=new AckReceiverWindow(10); Message msg; assertEquals(0, win.size()); win.add(9, msg()); assertEquals(0, win.size()); win.add(10, msg()); assertEquals(1, win.size()); win.add(13, msg()); assertEquals(2, win.size()); msg=win.remove(); assertNotNull(msg); assertEquals(1, win.size()); msg=win.remove(); assertNull(msg); assertEquals(1, win.size()); win.add(11, msg()); win.add(12, msg()); assertEquals(3, win.size()); msg=win.remove(); assertNotNull(msg); msg=win.remove(); assertNotNull(msg); msg=win.remove(); assertNotNull(msg); assertEquals(0, win.size()); msg=win.remove(); assertNull(msg); } public void testAddExisting() { win.add(1, msg()); assertEquals(1, win.size()); win.add(1, msg()); assertEquals(1, win.size()); win.add(2, msg()); assertEquals(2, win.size()); } public void testAddLowerThanNextToRemove() { win.add(1, msg()); win.add(2, msg()); win.remove(); // next_to_remove is 2 win.add(1, msg()); assertEquals(1, win.size()); } public void testRemove() { win.add(2, msg()); Message msg=win.remove(); assertNull(msg); assertEquals(1, win.size()); win.add(1, msg()); assertEquals(2, win.size()); assertNotNull(win.remove()); assertEquals(1, win.size()); assertNotNull(win.remove()); assertEquals(0, win.size()); assertNull(win.remove()); } public void testRemoveOOBMessage() { win.add(2, msg()); System.out.println("win = " + win); assertNull(win.removeOOBMessage()); assertNull(win.remove()); win.add(1, msg(true)); System.out.println("win = " + win); assertNotNull(win.removeOOBMessage()); System.out.println("win = " + win); assertNull(win.removeOOBMessage()); assertNotNull(win.remove()); assertNull(win.remove()); assertNull(win.removeOOBMessage()); } public void testDuplicates() { boolean rc; win=new AckReceiverWindow(2); assertEquals(0, win.size()); rc=win.add(9, msg()); assertTrue(rc); rc=win.add(1, msg()); assertFalse(rc); rc=win.add(2, msg()); assertTrue(rc); rc=win.add(2, msg()); assertFalse(rc); rc=win.add(0, msg()); assertFalse(rc); rc=win.add(3, msg()); assertTrue(rc); rc=win.add(4, msg()); assertTrue(rc); rc=win.add(4, msg()); assertFalse(rc); } public void testMessageReadyForRemoval() { assertFalse(win.hasMessagesToRemove()); win.add(2, msg()); System.out.println("win = " + win); assertFalse(win.hasMessagesToRemove()); win.add(3, msg()); System.out.println("win = " + win); assertFalse(win.hasMessagesToRemove()); win.add(1, msg()); System.out.println("win = " + win); assertTrue(win.hasMessagesToRemove()); win.remove(); System.out.println("win = " + win); assertTrue(win.hasMessagesToRemove()); win.remove(); System.out.println("win = " + win); assertTrue(win.hasMessagesToRemove()); win.remove(); System.out.println("win = " + win); assertFalse(win.hasMessagesToRemove()); } private static Message msg() { return msg(false); } private static Message msg(boolean oob) { Message retval=new Message(); if(oob) retval.setFlag(Message.OOB); return retval; } public static Test suite() { return new TestSuite(AckReceiverWindowTest.class); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/MessageTest.java0000644000175000017500000003276711366547366031521 0ustar twernertwerner// $Id: MessageTest.java,v 1.1.4.1 2008/08/18 11:14:35 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Message; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.UdpHeader; import org.jgroups.protocols.TpHeader; import org.jgroups.protocols.PingHeader; import org.jgroups.protocols.pbcast.NakAckHeader; import org.jgroups.stack.IpAddress; import org.jgroups.util.Range; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class MessageTest extends TestCase { Message m1, m2; public MessageTest(String name) { super(name); } public void testFlags() { m1=new Message(); assertFalse(m1.isFlagSet(Message.OOB)); try { m1.setFlag((byte)1002); fail("1002 is not a byte value"); } catch(IllegalArgumentException ex) { } assertEquals(0, m1.getFlags()); } public void testFlags2() { m1=new Message(); m1.setFlag(Message.OOB); assertTrue(m1.isFlagSet(Message.OOB)); assertEquals(Message.OOB, (m1.getFlags() & Message.OOB)); assertFalse(m1.isFlagSet(Message.LOW_PRIO)); assertNotSame((m1.getFlags() & Message.LOW_PRIO), Message.LOW_PRIO); } public static void testFlags3() { Message msg=new Message(); assertFalse(msg.isFlagSet(Message.OOB)); msg.setFlag(Message.OOB); assertTrue(msg.isFlagSet(Message.OOB)); msg.setFlag(Message.OOB); assertTrue(msg.isFlagSet(Message.OOB)); } public static void testClearFlags() { Message msg=new Message(); msg.setFlag(Message.OOB); assertTrue(msg.isFlagSet(Message.OOB)); msg.clearFlag(Message.OOB); assertFalse(msg.isFlagSet(Message.OOB)); msg.clearFlag(Message.OOB); assertFalse(msg.isFlagSet(Message.OOB)); msg.setFlag(Message.OOB); assertTrue(msg.isFlagSet(Message.OOB)); } public static void testClearFlags2() { Message msg=new Message(); msg.setFlag(Message.OOB); msg.setFlag(Message.HIGH_PRIO); assertFalse(msg.isFlagSet(Message.LOW_PRIO)); assertTrue(msg.isFlagSet(Message.OOB)); assertTrue(msg.isFlagSet(Message.HIGH_PRIO)); msg.clearFlag(Message.OOB); assertFalse(msg.isFlagSet(Message.OOB)); msg.setFlag(Message.LOW_PRIO); assertTrue(msg.isFlagSet(Message.LOW_PRIO)); assertTrue(msg.isFlagSet(Message.HIGH_PRIO)); msg.clearFlag(Message.HIGH_PRIO); assertFalse(msg.isFlagSet(Message.HIGH_PRIO)); msg.clearFlag(Message.HIGH_PRIO); assertFalse(msg.isFlagSet(Message.HIGH_PRIO)); msg.clearFlag(Message.LOW_PRIO); msg.clearFlag(Message.OOB); assertEquals(0, msg.getFlags()); assertFalse(msg.isFlagSet(Message.OOB)); assertFalse(msg.isFlagSet(Message.LOW_PRIO)); assertFalse(msg.isFlagSet(Message.HIGH_PRIO)); msg.setFlag(Message.LOW_PRIO); assertTrue(msg.isFlagSet(Message.LOW_PRIO)); msg.setFlag(Message.LOW_PRIO); assertTrue(msg.isFlagSet(Message.LOW_PRIO)); } public static void testBufferSize() throws Exception { Message m1=new Message(null, null, "bela"); assertNotNull(m1.getRawBuffer()); assertNotNull(m1.getBuffer()); assertEquals(m1.getBuffer().length, m1.getLength()); byte[] new_buf={'m', 'i', 'c', 'h', 'e', 'l', 'l', 'e'}; m1.setBuffer(new_buf); assertNotNull(m1.getRawBuffer()); assertNotNull(m1.getBuffer()); assertEquals(new_buf.length, m1.getLength()); assertEquals(m1.getBuffer().length, m1.getLength()); } public void testBufferOffset() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; m1=new Message(null, null, buf, 0, 4); m2=new Message(null, null, buf, 4, 3); byte[] b1, b2; b1=new byte[m1.getLength()]; System.arraycopy(m1.getRawBuffer(), m1.getOffset(), b1, 0, m1.getLength()); b2=new byte[m2.getLength()]; System.arraycopy(m2.getRawBuffer(), m2.getOffset(), b2, 0, m2.getLength()); assertEquals(4, b1.length); assertEquals(3, b2.length); } public void testSetBufferWithNullBuffer() { byte[] buf={'b', 'e', 'l', 'a'}; m1=new Message(); m1.setBuffer(buf, 1, 2); // dummy data with non 0 oiffset and length assertEquals(1, m1.getOffset()); assertEquals(2, m1.getLength()); m1.setBuffer(null, 1, 2); // dummy offset and length, is ignored assertEquals(0, m1.getOffset()); assertEquals(0, m1.getLength()); } public void testInvalidOffset() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; try { m1=new Message(null, null, buf, -1, 4); fail("we should not get here (offset is -1)"); } catch(ArrayIndexOutOfBoundsException ex) { assertTrue("correct: offset is invalid (caught correctly)", true); } } public void testInvalidLength() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; try { m1=new Message(null, null, buf, 3, 6); fail("we should not get here (length is 9)"); } catch(ArrayIndexOutOfBoundsException ex) { assertTrue("correct: length is invalid (caught correctly)", true); } } public void testGetRawBuffer() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; m1=new Message(null, null, buf, 0, 4); m2=new Message(null, null, buf, 4, 3); assertEquals(buf.length, m1.getRawBuffer().length); assertEquals(4, m1.getBuffer().length); assertEquals(4, m1.getLength()); assertEquals(buf.length, m2.getRawBuffer().length); assertEquals(3, m2.getBuffer().length); assertEquals(3, m2.getLength()); } public void testSerialization() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; byte[] tmp; m1=new Message(null, null, buf, 0, 4); m2=new Message(null, null, buf, 4, 3); ByteArrayOutputStream output=new ByteArrayOutputStream(); ObjectOutputStream out=new ObjectOutputStream(output); out.writeObject(m1); output.close(); tmp=output.toByteArray(); ByteArrayInputStream input=new ByteArrayInputStream(tmp); ObjectInputStream in=new ObjectInputStream(input); Message m3, m4; m3=(Message)in.readObject(); assertEquals(4, m3.getLength()); assertEquals(4, m3.getRawBuffer().length); assertEquals(4, m3.getBuffer().length); assertEquals(0, m3.getOffset()); output=new ByteArrayOutputStream(); out=new ObjectOutputStream(output); out.writeObject(m2); output.close(); tmp=output.toByteArray(); input=new ByteArrayInputStream(tmp); in=new ObjectInputStream(input); m4=(Message)in.readObject(); assertEquals(3, m4.getLength()); assertEquals(3, m4.getBuffer().length); assertEquals(3, m4.getRawBuffer().length); assertEquals(0, m4.getOffset()); } public void testSetObject() { String s1="Bela Ban"; m1=new Message(null, null, s1); assertEquals(0, m1.getOffset()); assertEquals(m1.getBuffer().length, m1.getLength()); String s2=(String)m1.getObject(); assertEquals(s2, s1); } public void testCopy() { m1=new Message(null, null, "Bela Ban"); m2=m1.copy(); assertEquals(m1.getOffset(), m2.getOffset()); assertEquals(m1.getLength(), m2.getLength()); } public void testCopyWithOffset() { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; m1=new Message(null, null, buf, 0, 4); m2=new Message(null, null, buf, 4, 3); Message m3, m4; m3=m1.copy(); m4=m2.copy(); assertEquals(0, m3.getOffset()); assertEquals(4, m3.getLength()); assertEquals(4, m3.getBuffer().length); assertEquals(4, m4.getOffset()); assertEquals(3, m4.getLength()); assertEquals(3, m4.getBuffer().length); } public void testComputeFragOffsets() { Range r; byte[] buf={0,1,2,3,4,5,6,7,8,9}; java.util.List retval=Util.computeFragOffsets(buf, 4); System.out.println("list is " + retval); assertEquals(3, retval.size()); r=(Range)retval.get(0); assertEquals(0, r.low); assertEquals(4, r.high); r=(Range)retval.get(1); assertEquals(4, r.low); assertEquals(4, r.high); r=(Range)retval.get(2); assertEquals(8, r.low); assertEquals(2, r.high); } public void testComputeFragOffsetsWithOffsets() { Range r; // byte[] buf={'p', 'a', 'd', 0,1,2,3,4,5,6,7,8,9, 'p', 'a', 'd', 'd', 'i', 'e'}; java.util.List retval=Util.computeFragOffsets(3, 10, 4); System.out.println("list is " + retval); assertEquals(3, retval.size()); r=(Range)retval.get(0); assertEquals(3, r.low); assertEquals(4, r.high); r=(Range)retval.get(1); assertEquals(7, r.low); assertEquals(4, r.high); r=(Range)retval.get(2); assertEquals(11, r.low); assertEquals(2, r.high); } public void testComputeFragOffsets2() { Range r; byte[] buf={0,1,2,3,4,5,6,7,8,9}; java.util.List retval=Util.computeFragOffsets(buf, 10); System.out.println("list is " + retval); assertEquals(1, retval.size()); r=(Range)retval.get(0); assertEquals(0, r.low); assertEquals(10, r.high); } public void testComputeFragOffsets3() { Range r; byte[] buf={0,1,2,3,4,5,6,7,8,9}; java.util.List retval=Util.computeFragOffsets(buf, 100); System.out.println("list is " + retval); assertEquals(1, retval.size()); r=(Range)retval.get(0); assertEquals(0, r.low); assertEquals(10, r.high); } public void testComputeFragOffsets4() { Range r; byte[] buf={0,1,2,3,4,5,6,7,8,9}; java.util.List retval=Util.computeFragOffsets(buf, 5); System.out.println("list is " + retval); assertEquals(2, retval.size()); r=(Range)retval.get(0); assertEquals(0, r.low); assertEquals(5, r.high); r=(Range)retval.get(1); assertEquals(5, r.low); assertEquals(5, r.high); } public void testSizeNullMessage() throws Exception { Message msg=new Message(); _testSize(msg); } public void testSizeMessageWithDest() throws Exception { Message msg=new Message(new IpAddress("127.0.0.1", 3333), null, null); _testSize(msg); } public void testSizeMessageWithSrc() throws Exception { Message msg=new Message(null, new IpAddress("127.0.0.1", 4444), null); _testSize(msg); } public void testSizeMessageWithDestAndSrc() throws Exception { Message msg=new Message(new IpAddress("127.0.0.1", 3333), new IpAddress("127.0.0.1", 4444), null); _testSize(msg); } public void testSizeMessageWithDestAndSrcAndFlags() throws Exception { Message msg=new Message(new IpAddress("127.0.0.1", 3333), new IpAddress("127.0.0.1", 4444), null); msg.setFlag(Message.OOB); msg.setFlag(Message.LOW_PRIO); _testSize(msg); } public void testSizeMessageWithBuffer() throws Exception { Message msg=new Message(null, null, "bela".getBytes()); _testSize(msg); } public void testSizeMessageWithBuffer2() throws Exception { Message msg=new Message(null, null, new byte[]{'b', 'e', 'l', 'a'}); _testSize(msg); } public void testSizeMessageWithBuffer3() throws Exception { Message msg=new Message(null, null, "bela"); _testSize(msg); } public void testSizeMessageWithAdditionalData() throws Exception { IpAddress dest=new IpAddress("127.0.0.1", 5555); dest.setAdditionalData("bela".getBytes()); Message msg=new Message(dest, null, null); _testSize(msg); } public void testSizeMessageWithDestAndSrcAndHeaders() throws Exception { ClassConfigurator.getInstance(true); Message msg=new Message(new IpAddress("127.0.0.1", 3333), new IpAddress("127.0.0.1", 4444), "bela".getBytes()); addHeaders(msg); _testSize(msg); } private void addHeaders(Message msg) { UdpHeader udp_hdr=new UdpHeader("DemoChannel"); msg.putHeader("UDP", udp_hdr); TpHeader tp_hdr=new TpHeader("DemoChannel2"); msg.putHeader("TP", tp_hdr); PingHeader ping_hdr=new PingHeader(PingHeader.GET_MBRS_REQ, null); msg.putHeader("PING", ping_hdr); NakAckHeader nak_hdr=new NakAckHeader(NakAckHeader.XMIT_REQ, 100, 104); msg.putHeader("NAKACK", nak_hdr); } private void _testSize(Message msg) throws Exception { long size=msg.size(); byte[] serialized_form=Util.streamableToByteBuffer(msg); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(size, serialized_form.length); } public static Test suite() { return new TestSuite(MessageTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/NAKACK_SET_DIGEST_Test.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/NAKACK_SET_DIGEST_Test.ja0000644000175000017500000000616711366547366032442 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.*; import org.jgroups.protocols.pbcast.NAKACK; import org.jgroups.protocols.TP; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import org.jgroups.util.TimeScheduler; import java.util.Arrays; import java.util.Vector; /** * Tests setting of digest NAKACK.down(SET_DIGEST), JIRA issue is https://jira.jboss.org/jira/browse/JGRP-1060 * @author Bela Ban * @version $Id: NAKACK_SET_DIGEST_Test.java,v 1.1.2.1 2009/09/25 08:40:20 belaban Exp $ */ public class NAKACK_SET_DIGEST_Test extends TestCase { private NAKACK nak; private MutableDigest d1, d2; private Address a, b, c; protected void setUp() throws Exception { super.setUp(); a=new IpAddress(5000); b=new IpAddress(6000); c=new IpAddress(7000); nak=new NAKACK(); d1=new MutableDigest(2); d1.add(a, 0, 10, 10); d1.add(b, 0, 30, 30); d2=new MutableDigest(3); d2.add(a, 0, 10, 11); d2.add(b, 0, 35, 35); d2.add(c, 10, 50, 50); nak.setDownProtocol(new TP() { public String getName() {return "blo";} public String getInfo() {return null;} public void postUnmarshalling(Message msg, Address dest, Address src, boolean multicast) {} public void postUnmarshallingList(Message msg, Address dest, boolean multicast) {} public Object down(Event evt) {return null;} public TimeScheduler getTimer() {return new TimeScheduler(1);} public void sendToAllMembers(byte[] data, int offset, int length) throws Exception {} public void sendToSingleMember(Address dest, byte[] data, int offset, int length) throws Exception {} }); nak.setUpProtocol(new Protocol() { public String getName() {return "bla";} public Object up(Event evt) {return null;} }); nak.start(); View view=new View(a, 1, new Vector
        (Arrays.asList(a, b, c))); nak.down(new Event(Event.VIEW_CHANGE, view)); nak.up(new Event(Event.SET_LOCAL_ADDRESS, a)); } public void testSetDigest() throws TimeoutException { System.out.println("d1: " + d1); System.out.println("d2: " + d2); System.out.println("setting d2:"); nak.down(new Event(Event.SET_DIGEST, d2)); Digest digest=(Digest)nak.down(new Event(Event.GET_DIGEST)); System.out.println("digest = " + digest); assertEquals(3, digest.size()); assertTrue(digest.contains(a)); assertTrue(digest.contains(b)); assertTrue(digest.contains(c)); System.out.println("setting d1:"); nak.down(new Event(Event.SET_DIGEST, d1)); digest=(Digest)nak.down(new Event(Event.GET_DIGEST)); System.out.println("digest = " + digest); assertEquals(3, digest.size()); // https://jira.jboss.org/jira/browse/JGRP-1060 assertTrue(digest.contains(a)); assertTrue(digest.contains(b)); assertTrue(digest.contains(c)); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/InterruptTest.java0000644000175000017500000001332311366547366032114 0ustar twernertwerner// $Id: InterruptTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * Tests Thread.interrupt() against InputStream.read(), Object.wait() and Thread.sleep() * * @author Bela Ban Oct 5 2001 */ public class InterruptTest extends TestCase { static final long TIMEOUT=3000; static final int SLEEP=1; static final int WAIT=2; static final int READ=3; static final int SOCKET_READ=4; public InterruptTest(String name) { super(name); } String modeToString(int m) { switch(m) { case SLEEP: return "SLEEP"; case WAIT: return "WAIT"; case READ: return "READ"; case SOCKET_READ: return "SOCKET_READ"; default: return ""; } } /** * Starts the Interruptible and interrupts after TIMEOUT milliseconds. Then joins thread * (waiting for TIMEOUT msecs). PASS if thread dead, FAIL if still alive */ public void testSleepInterrupt() { SleeperThread thread=new SleeperThread(SLEEP); runTest(thread); } public void testWaitInterrupt() { SleeperThread thread=new SleeperThread(WAIT); runTest(thread); } /* public void testSocketReadInterrupt() { SleeperThread thread=new SleeperThread(SOCKET_READ); runTest(thread); } public void testReadInterrupt() { SleeperThread thread=new SleeperThread(READ); runTest(thread); }*/ void runTest(SleeperThread thread) { System.out.println(); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): starting other thread"); thread.start(); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): starting other thread -- done"); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): sleeping for " + TIMEOUT + " msecs"); sleep(TIMEOUT); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): sleeping -- done"); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): interrupting other thread"); thread.interrupt(); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): interrupting other thread -- done"); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): joining other thread (timeout=" + TIMEOUT + " msecs"); try { thread.join(TIMEOUT); } catch(InterruptedException e) { e.printStackTrace(); } System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): joining other thread -- done"); System.out.println("InterruptTest.runTest(" + modeToString(thread.getMode()) + "): thread.isAlive()=" + thread.isAlive()); assertTrue(!thread.isAlive()); } void sleep(long msecs) { try { Thread.sleep(msecs); } catch(Exception ex) { System.err.println("InterruptTest.sleep(): " + ex); } } class SleeperThread extends Thread { int mode; DatagramSocket sock=null; SleeperThread(int mode) { this.mode=mode; } public int getMode() { return mode; } public void run() { switch(mode) { case SLEEP: runSleep(); break; case WAIT: runWait(); break; case READ: runRead(); break; case SOCKET_READ: runSocketRead(); break; default: break; } } void runSleep() { try { Thread.sleep(TIMEOUT); } catch(InterruptedException ex) { System.err.println("InterruptTest.SleeperThread.runSleep(): " + ex); } } void runWait() { Object mutex=new Object(); synchronized(mutex) { try { mutex.wait(); } catch(InterruptedException ex) { System.err.println("InterruptTest.SleeperThread.runWait(): " + ex); } } } void runRead() { try { System.in.read(); } catch(Exception ex) { System.err.println("InterruptTest.SleeperThread.runRead(): " + ex); } } void runSocketRead() { byte[] buf=new byte[2]; DatagramPacket packet; try { sock=new DatagramSocket(12345, InetAddress.getLocalHost()); // System.out.println("** mcast_sock=" + mcast_sock.getLocalAddress() + ":" + mcast_sock.getLocalPort()); packet=new DatagramPacket(buf, buf.length); //System.out.println("** receive(): start"); sock.receive(packet); //System.out.println("** receive(): done"); } catch(Exception e) { //System.out.println("** receive(): done, exception=" + e); System.err.println(e); } } } public static Test suite() { TestSuite s=new TestSuite(InterruptTest.class); return s; } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/VersionTest.java0000644000175000017500000000455111366547366031550 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.Version; /** * @author Bela Ban April 4 2003 * @version $Revision: 1.1 $ */ public class VersionTest extends TestCase { public VersionTest(String s) { super(s); } public void testVersionPrint() { System.out.println("version is " +Version.printVersion()); assertTrue(true); } public void testNullVersion() { assertFalse(Version.isSame((short)0)); } public void testDifferentLengthVersion1() { short version2=Version.encode(2,0,7); assertFalse(Version.isSame(version2)); } public void testDifferentVersion() { short version1=Version.encode(2,0,7), version2=Version.encode(2,0,6); assertFalse(version1 == version2); } public void testSameVersion() { assertTrue(match(0,0,1, 0,0,1)); assertTrue(match(1,0,0, 1,0,0)); assertTrue(match(10,2,60, 10,2,60)); assertFalse(match(1,2,3, 1,2,0)); assertFalse(match(0,0,0, 0,0,1)); assertFalse(match(2,5,0, 2,5,1)); } public void testBinaryCompatibility() { assertTrue(isBinaryCompatible(0,0,0, 0,0,0)); assertTrue(isBinaryCompatible(1,2,0, 1,2,1)); assertTrue(isBinaryCompatible(1,2,0, 1,2,60)); assertFalse(isBinaryCompatible(2,5,0, 2,4,1)); assertFalse(isBinaryCompatible(2,5,0, 2,6,0)); } private boolean match(int major_1, int minor_1, int micro_1, int major_2, int minor_2, int micro_2) { short version1=Version.encode(major_1, minor_1, micro_1); short version2=Version.encode(major_2, minor_2, micro_2); return version1 == version2; } private boolean isBinaryCompatible(int major_1, int minor_1, int micro_1, int major_2, int minor_2, int micro_2) { short version1=Version.encode(major_1, minor_1, micro_1); short version2=Version.encode(major_2, minor_2, micro_2); boolean retval=Version.isBinaryCompatible(version1, version2); System.out.println(Version.print(version1) + " binary compatibel to " + Version.print(version2) + (retval? " OK" : " FAIL")); return Version.isBinaryCompatible(version1, version2); } public static void main(String[] args) { String[] testCaseName = {VersionTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/HeadersTest.java0000644000175000017500000001156711366547366031503 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.Header; import org.jgroups.util.Headers; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Map; /** * Tests the functionality of the Headers class * @author Bela Ban * @version $Id: HeadersTest.java,v 1.3.2.3 2008/07/31 12:55:22 belaban Exp $ */ public class HeadersTest extends TestCase { private static final String UDP="UDP", FRAG="FRAG", NAKACK="NAKACK"; private static final MyHeader h1=new MyHeader(), h2=new MyHeader(), h3=new MyHeader(); public static void testConstructor() { Headers hdrs=new Headers(5); System.out.println("hdrs = " + hdrs); assert hdrs.capacity() == 5 : "capacity must be 5 but was " + hdrs.capacity(); Object[] data=hdrs.getRawData(); assert data.length == hdrs.capacity() * 2; assert hdrs.size() == 0; } public static void testContructor2() { Headers old=createHeaders(3); Headers hdrs=new Headers(old); System.out.println("hdrs = " + hdrs); assert hdrs.capacity() == 3 : "capacity must be 3 but was " + hdrs.capacity(); Object[] data=hdrs.getRawData(); assert data.length == hdrs.capacity() * 2; assert hdrs.size() == 3; // make sure 'hdrs' is not changed when 'old' is modified, as 'hdrs' is a copy old.putHeader("BLA", new MyHeader()); assert hdrs.capacity() == 3 : "capacity must be 3 but was " + hdrs.capacity(); data=hdrs.getRawData(); assert data.length == hdrs.capacity() * 2; assert hdrs.size() == 3; } public static void testGetRawData() { Headers hdrs=createHeaders(3); Object[] data=hdrs.getRawData(); assert data.length == 6; assert data[0].equals(NAKACK); assert data[1].equals(h1); assert data[2].equals(FRAG); assert data[3].equals(h2); assert data[4].equals(UDP); assert data[5].equals(h3); assert data.length == hdrs.capacity() * 2; assert hdrs.size() == 3; } public static void testGetHeaders() { Headers hdrs=createHeaders(3); Map map=hdrs.getHeaders(); System.out.println("map = " + map); assert map != null && map.size() == 3; assert map.get(NAKACK) == h1; assert map.get(FRAG) == h2; assert map.get(UDP) == h3; } public static void testSize() { Headers hdrs=createHeaders(3); assert hdrs.size() == 3; } private static Headers createHeaders(int initial_capacity) { Headers hdrs=new Headers(initial_capacity); hdrs.putHeader(NAKACK, h1); hdrs.putHeader(FRAG, h2); hdrs.putHeader(UDP, h3); return hdrs; } public static void testPutHeader() { Headers hdrs=createHeaders(3); assert hdrs.getHeader(NAKACK) == h1; hdrs.putHeader(NAKACK, new MyHeader()); assert hdrs.size() == 3; assert hdrs.getHeader(NAKACK) != h1; assert hdrs.capacity() == 3; hdrs.putHeader("NEW", new MyHeader()); assert hdrs.size() == 4; assert hdrs.capacity() > 3; } public static void testPutHeaderIfAbsent() { Headers hdrs=createHeaders(3); Header hdr=hdrs.putHeaderIfAbsent(FRAG, new MyHeader()); assert hdr == h2; assert hdr == hdrs.getHeader(FRAG); assert hdrs.size() == 3; assert hdrs.capacity() == 3; hdr=hdrs.putHeaderIfAbsent("NEW", new MyHeader()); System.out.println("hdrs = " + hdrs); assert hdr == null; assert hdrs.size() == 4; assert hdrs.capacity() == 6; } public static void testGetHeader() { Headers hdrs=createHeaders(3); assert null == hdrs.getHeader("NOTAVAILABLE"); assert hdrs.getHeader(UDP) == h3; } public static void testResize() { Headers hdrs=createHeaders(3); int capacity=hdrs.capacity(); System.out.println("hdrs = " + hdrs + ", capacity=" + capacity); hdrs.putHeader("NEW", new MyHeader()); System.out.println("hdrs = " + hdrs + ", capacity=" + hdrs.capacity()); assert hdrs.capacity() > capacity; capacity=hdrs.capacity(); for(int i=0; i < 3; i++) hdrs.putHeader("#" + i, new MyHeader()); System.out.println("hdrs = " + hdrs + ", capacity=" + hdrs.capacity()); assert hdrs.capacity() > capacity; } public static class MyHeader extends Header { private static final long serialVersionUID=-7164974484154022976L; public MyHeader() { } public String toString() { return "MyHeader"; } public void writeExternal(ObjectOutput out) throws IOException { } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/IpAddressTest.java0000644000175000017500000003002511366547366031774 0ustar twernertwerner// $Id: IpAddressTest.java,v 1.2.2.1 2008/04/14 06:28:04 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.io.*; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.HashSet; public class IpAddressTest extends TestCase { IpAddress a, b, c, d, e, f, g, h, i, j, k; public IpAddressTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); a=new IpAddress("localhost", 5555); b=new IpAddress("localhost", 5555); c=b; d=new IpAddress("localhost", 5556); e=new IpAddress("127.0.0.1", 5555); f=new IpAddress("www.ibm.com", 80); g=new IpAddress("www.ibm.com", 8080); h=new IpAddress("224.0.0.1", 5555); } public void testUnknownAddress() { try { IpAddress tmp=new IpAddress("idontknow.com", 55); fail("should throw an UnknownHostException for " + tmp); } catch(UnknownHostException e1) { } } public void testEquality() throws Exception { assertEquals(a, b); assertEquals(c, b); assertEquals(a, e); assertEquals(c, e); } public void testEqualityWithDnsRoundRobin() throws UnknownHostException { IpAddress x1, x2, x3; InetAddress addr=InetAddress.getByName("127.0.0.1"); byte[] rawAddr=addr.getAddress(); InetAddress inet1=InetAddress.getByAddress("MyHost1", rawAddr); InetAddress inet2=InetAddress.getByAddress("MyHost2", rawAddr); InetAddress inet3=InetAddress.getByAddress("MyHost3", rawAddr); assertEquals(inet1, inet2); x1=new IpAddress(inet1, 5555); x2=new IpAddress(inet2, 5555); x3=new IpAddress(inet3, 5555); assertEquals(x1, x2); assertEquals(x3, x1); HashSet s=new HashSet(); s.add(x1); s.add(x2); s.add(x3); System.out.println("s=" + s); assertEquals(1, s.size()); HashMap m=new HashMap(); m.put(x1, "Bela"); m.put(x2, "Michelle"); m.put(x3, "Nicole"); assertEquals(1, m.size()); assertEquals("Nicole", m.get(x1)); } public void testInequality() throws Exception { IpAddress tmp=null; assertTrue(!a.equals(d)); assertTrue(!c.equals(d)); assertTrue(!a.equals(f)); assertTrue(!e.equals(f)); assertTrue(!f.equals(g)); assertFalse(a.equals(tmp)); } public void testSameHost() throws Exception { assertTrue(Util.sameHost(a, b)); assertTrue(Util.sameHost(a, c)); assertTrue(Util.sameHost(a, d)); assertTrue(Util.sameHost(a, e)); assertTrue(Util.sameHost(f, g)); } public void testNotSameHost() throws Exception { assertTrue(!Util.sameHost(a, f)); assertTrue(!Util.sameHost(e, f)); assertTrue(!Util.sameHost(e, null)); assertTrue(!Util.sameHost(null, null)); } public void testMcast() { assertTrue(h.isMulticastAddress()); assertTrue(!a.isMulticastAddress()); assertTrue(!e.isMulticastAddress()); assertTrue(!g.isMulticastAddress()); } public void testCompareTo() { assertEquals(0, a.compareTo(b)); assertTrue(a.compareTo(d) < 0); assertTrue(d.compareTo(a) > 0); } public void testCompareTime() { final int NUM=1000000; _testCompareTime(a, a, NUM); _testCompareTime(a, b, NUM); _testCompareTime(a, c, NUM); _testCompareTime(a, d, NUM); } private static void _testCompareTime(IpAddress one, IpAddress two, int num) { int rc=-99; long start=System.currentTimeMillis(), stop; for(int x=0; x < num; x++) { rc=one.compareTo(two); } stop=System.currentTimeMillis(); long diff=stop-start; System.out.println("calling compareTo(" + one + ", " + two + ") " + num + " times took " + diff + "ms, result=" + rc); } public void testHashcode() { int hcode_a=a.hashCode(); int hcode_b=b.hashCode(); assertEquals(hcode_a, hcode_b); } public void testHashcodeTime() { int hash=-1; final int NUM=10000000; long start=System.currentTimeMillis(), stop; for(int x=0; x < NUM; x++) { hash=a.hashCode(); } stop=System.currentTimeMillis(); long diff=stop-start; System.out.println("taking the hash code of " + a + "(" + hash + ") took " + diff + "ms"); } public void testIPv6WithExternalization() throws IOException, ClassNotFoundException { InetAddress tmp=Util.getFirstNonLoopbackIPv6Address(); IpAddress ip=new IpAddress(tmp, 5555); ByteArrayOutputStream bos=new ByteArrayOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; ObjectInputStream ois; System.out.println("-- address is " + tmp); oos.writeObject(ip); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); ois=new ObjectInputStream(bis); IpAddress ip2=(IpAddress)ois.readObject(); assertEquals(ip, ip2); } public void testIPv6WithStreamable() throws IOException, ClassNotFoundException { InetAddress tmp=Util.getFirstNonLoopbackIPv6Address(); IpAddress ip=new IpAddress(tmp, 5555); ByteArrayOutputStream bos=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; DataInputStream dis; System.out.println("-- address is " + tmp); ip.writeTo(dos); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); dis=new DataInputStream(bis); IpAddress ip2=new IpAddress(); ip2.readFrom(dis); assertEquals(ip, ip2); } public void testExternalization() throws Exception { ByteArrayOutputStream bos=new ByteArrayOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; ObjectInputStream ois; IpAddress a2, b2; a.setAdditionalData(null); b.setAdditionalData("Bela Ban".getBytes()); oos.writeObject(a); oos.writeObject(b); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); ois=new ObjectInputStream(bis); a2=(IpAddress)ois.readObject(); b2=(IpAddress)ois.readObject(); assertEquals(a, a2); assertEquals(b, b2); assertNull(a2.getAdditionalData()); assertEquals("Bela Ban", new String(b2.getAdditionalData())); } public void testExternalizationAdditionalData() throws Exception { ByteArrayOutputStream bos=new ByteArrayOutputStream(); ObjectOutputStream oos=new ObjectOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; ObjectInputStream ois; IpAddress a2, b2, c2, d2, e2, f2, g2, h2; oos.writeObject(a); oos.writeObject(b); oos.writeObject(c); oos.writeObject(d); oos.writeObject(e); oos.writeObject(f); oos.writeObject(g); oos.writeObject(h); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); ois=new ObjectInputStream(bis); a2=(IpAddress)ois.readObject(); b2=(IpAddress)ois.readObject(); c2=(IpAddress)ois.readObject(); d2=(IpAddress)ois.readObject(); e2=(IpAddress)ois.readObject(); f2=(IpAddress)ois.readObject(); g2=(IpAddress)ois.readObject(); h2=(IpAddress)ois.readObject(); assertEquals(b2, c2); assertEquals(a, a2); assertEquals(b, b2); assertEquals(c, c2); assertEquals(d, d2); assertEquals(e, e2); assertEquals(f, f2); assertEquals(g, g2); assertEquals(h, h2); } public void testStreamable() throws Exception { ByteArrayOutputStream bos=new ByteArrayOutputStream(); DataOutputStream oos=new DataOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; DataInputStream ois; IpAddress a2, b2, x, x2, y, y2; x=new IpAddress(5555); x.setAdditionalData(new byte[]{'b','e','l','a'}); y=new IpAddress(1000); y.setAdditionalData(new byte[]{'b','e','l','a'}); a.setAdditionalData(null); b.setAdditionalData("Bela Ban".getBytes()); a.writeTo(oos); b.writeTo(oos); x.writeTo(oos); y.writeTo(oos); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); ois=new DataInputStream(bis); a2=new IpAddress(); a2.readFrom(ois); b2=new IpAddress(); b2.readFrom(ois); x2=new IpAddress(); x2.readFrom(ois); y2=new IpAddress(); y2.readFrom(ois); assertEquals(a, a2); assertEquals(b, b2); assertNull(a2.getAdditionalData()); assertEquals("Bela Ban", new String(b2.getAdditionalData())); assertNotNull(x2.getAdditionalData()); assertEquals(4, x2.getAdditionalData().length); assertNotNull(y2.getIpAddress()); assertEquals(1000, y2.getPort()); assertNotNull(y2.getAdditionalData()); assertEquals(4, y2.getAdditionalData().length); } public void testStreamableWithHighPort() throws Exception { ByteArrayOutputStream bos=new ByteArrayOutputStream(); DataOutputStream oos=new DataOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; DataInputStream dis; IpAddress x, x2; x=new IpAddress(65535); x.writeTo(oos); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); dis=new DataInputStream(bis); x2=new IpAddress(); x2.readFrom(dis); System.out.println("x: " + x + ", x2: " + x2); assertTrue(x2.getPort() > 0); assertEquals(x.getPort(), x2.getPort()); } public void testStreamableAdditionalData() throws Exception { ByteArrayOutputStream bos=new ByteArrayOutputStream(); DataOutputStream oos=new DataOutputStream(bos); byte[] buf=null; ByteArrayInputStream bis=null; DataInputStream ois; IpAddress a2, b2, c2, d2, e2, f2, g2, h2; a.writeTo(oos); b.writeTo(oos); c.writeTo(oos); d.writeTo(oos); e.writeTo(oos); f.writeTo(oos); g.writeTo(oos); h.writeTo(oos); buf=bos.toByteArray(); bis=new ByteArrayInputStream(buf); ois=new DataInputStream(bis); a2=new IpAddress(); a2.readFrom(ois); b2=new IpAddress(); b2.readFrom(ois); c2=new IpAddress(); c2.readFrom(ois); d2=new IpAddress(); d2.readFrom(ois); e2=new IpAddress(); e2.readFrom(ois); f2=new IpAddress(); f2.readFrom(ois); g2=new IpAddress(); g2.readFrom(ois); h2=new IpAddress(); h2.readFrom(ois); assertEquals(b2, c2); assertEquals(a, a2); assertEquals(b, b2); assertEquals(c, c2); assertEquals(d, d2); assertEquals(e, e2); assertEquals(f, f2); assertEquals(g, g2); assertEquals(h, h2); } public static Test suite() { return new TestSuite(IpAddressTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/StreamableTest.java0000644000175000017500000002025611366547366032202 0ustar twernertwerner// $Id: StreamableTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.*; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.PingHeader; import org.jgroups.protocols.PingRsp; import org.jgroups.protocols.UdpHeader; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.Vector; public class StreamableTest extends TestCase { Message m1, m2; public StreamableTest(String name) { super(name); } static { try { ClassConfigurator.getInstance(true); } catch(ChannelException e) { e.printStackTrace(); } } public void testStreamable() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; byte[] tmp; m1=new Message(null, null, buf, 0, 4); m2=new Message(null, null, buf, 4, 3); ByteArrayOutputStream output=new ByteArrayOutputStream(); DataOutputStream out=new DataOutputStream(output); m1.writeTo(out); out.close(); tmp=output.toByteArray(); output.close(); ByteArrayInputStream input=new ByteArrayInputStream(tmp); DataInputStream in=new DataInputStream(input); Message m3, m4; m3=new Message(false); m3.readFrom(in); assertEquals(4, m3.getLength()); assertEquals(4, m3.getRawBuffer().length); assertEquals(4, m3.getBuffer().length); assertEquals(0, m3.getOffset()); output=new ByteArrayOutputStream(); out=new DataOutputStream(output); // out.writeObject(m2); m2.writeTo(out); out.close(); tmp=output.toByteArray(); output.close(); System.out.println("-- serialized buffer is " + tmp.length + " bytes"); input=new ByteArrayInputStream(tmp); in=new DataInputStream(input); // m4=(Message)in.readObject(); m4=new Message(); m4.readFrom(in); assertEquals(3, m4.getLength()); assertEquals(3, m4.getBuffer().length); assertEquals(3, m4.getRawBuffer().length); assertEquals(0, m4.getOffset()); } public void testStreamable2() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message msg=new Message(null, null, buf, 0, 4); stream(msg); } public void testStreamable3() throws Exception { byte[] buf={'b', 'e', 'l', 'a', 'b', 'a', 'n'}; Message msg=new Message(null, null, buf, 4, 3); stream(msg); } public void testNullBuffer() throws Exception { Message msg=new Message(); stream(msg); } public void testNonNullBuffer() throws Exception { Message msg=new Message(null, null, "Hello world".getBytes()); stream(msg); } public void testNonNullAddress() throws Exception { Address dest, src; dest=new IpAddress("228.1.2.3", 5555); src=new IpAddress("127.0.0.1", 6666); Message msg=new Message(dest, src, "Hello world".getBytes()); stream(msg); } public void testHeaders() throws Exception { Address dest, src; dest=new IpAddress("228.1.2.3", 5555); src=new IpAddress("127.0.0.1", 6666); Message msg=new Message(dest, src, "Hello world".getBytes()); PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, new PingRsp(src, src, true)); msg.putHeader("ping-header", hdr); UdpHeader udp_hdr=new UdpHeader("bla"); msg.putHeader("udp-header", udp_hdr); stream(msg); } public void testAdditionalData() throws Exception { IpAddress dest, src; dest=new IpAddress("228.1.2.3", 5555); dest.setAdditionalData("foo".getBytes()); src=new IpAddress("127.0.0.1", 6666); src.setAdditionalData("foobar".getBytes()); Message msg=new Message(dest, src, "Hello world".getBytes()); PingHeader hdr=new PingHeader(PingHeader.GET_MBRS_REQ, new PingRsp(src, src, false)); msg.putHeader("ping-header", hdr); UdpHeader udp_hdr=new UdpHeader("bla"); msg.putHeader("udp-header", udp_hdr); stream(msg); } public void testMergeView() throws Exception { Vector tmp_m1, tmp_m2 , m3, all, subgroups; Address a,b,c,d,e,f; View v1, v2, v3, v4, v5, view_all; a=new IpAddress(1000); b=new IpAddress(2000); c=new IpAddress(3000); d=new IpAddress(4000); e=new IpAddress(5000); f=new IpAddress(6000); tmp_m1=new Vector(); tmp_m2=new Vector(); m3=new Vector(); all=new Vector(); subgroups=new Vector(); tmp_m1.add(a); tmp_m1.add(b); tmp_m1.add(c); tmp_m2.add(d); m3.add(e); m3.add(f); all.add(a); all.add(b); all.add(c); all.add(d); all.add(e); all.add(f); v1=new View(a, 1, tmp_m1); v2=new MergeView(d, 2, tmp_m2, new Vector()); v3=new View(e, 3, m3); v4=new MergeView(e, 4, m3, null); v5=new View(e, 5, m3); subgroups.add(v1); subgroups.add(v2); subgroups.add(v3); subgroups.add(v4); subgroups.add(v5); view_all=new MergeView(a, 5, all, subgroups); System.out.println("MergeView: " + view_all); Vector sub=((MergeView)view_all).getSubgroups(); assertTrue(sub.get(0) instanceof View); assertTrue(sub.get(1) instanceof MergeView); assertTrue(sub.get(2) instanceof View); assertTrue(sub.get(3) instanceof MergeView); assertTrue(sub.get(4) instanceof View); byte[] buf=Util.streamableToByteBuffer(view_all); assertNotNull(buf); assertTrue(buf.length > 0); MergeView merge_view=(MergeView)Util.streamableFromByteBuffer(MergeView.class, buf); assertNotNull(merge_view); System.out.println("MergeView: " + merge_view); sub=merge_view.getSubgroups(); assertTrue(sub.get(0) instanceof View); assertTrue(sub.get(1) instanceof MergeView); assertTrue(sub.get(2) instanceof View); assertTrue(sub.get(3) instanceof MergeView); assertTrue(sub.get(4) instanceof View); } private void stream(Message msg) throws Exception { int length, bufLength; byte[] tmp; Message msg2; Address src; int num_headers=getNumHeaders(msg); length=msg.getLength(); bufLength=getBufLength(msg); src=msg.getSrc(); ByteArrayOutputStream output=new ByteArrayOutputStream(); DataOutputStream out=new DataOutputStream(output); msg.writeTo(out); out.close(); tmp=output.toByteArray(); output.close(); System.out.println("-- serialized buffer is " + tmp.length + " bytes"); ByteArrayInputStream input=new ByteArrayInputStream(tmp); DataInputStream in=new DataInputStream(input); msg2=new Message(); msg2.readFrom(in); assertEquals(length, msg2.getLength()); assertEquals(bufLength, getBufLength(msg2)); // assertTrue(match(dest, msg2.getDest())); assertNull(msg2.getDest()); // we don't marshal the destination address assertTrue(match(src, msg2.getSrc())); assertEquals(num_headers, getNumHeaders(msg2)); } private int getNumHeaders(Message msg) { return msg.getNumHeaders(); } private boolean match(Address a1, Address a2) { if(a1 == null && a2 == null) return true; if(a1 != null) return a1.equals(a2); else return a2.equals(a1); } // private int getRawBufLength(Message msg) { // return msg.getRawBuffer() != null? msg.getRawBuffer().length : 0; // } private int getBufLength(Message msg) { return msg.getBuffer() != null? msg.getBuffer().length : 0; } public static Test suite() { return new TestSuite(StreamableTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/SeqnoTableTest.java0000644000175000017500000001020211366547366032146 0ustar twernertwerner package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import org.jgroups.util.SeqnoTable; import java.net.UnknownHostException; public class SeqnoTableTest extends TestCase { SeqnoTable tab; private static Address MBR; static { try { MBR=new IpAddress("127.0.0.1", 5555); } catch(UnknownHostException e) { throw new RuntimeException(e); } } public SeqnoTableTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); } protected void tearDown() throws Exception { super.tearDown(); tab.clear(); } public void testInit() { tab=new SeqnoTable(0); tab.add(MBR, 0); assertEquals(0, tab.getHighestReceived(MBR)); assertEquals(1, tab.getNextToReceive(MBR)); tab.clear(); tab=new SeqnoTable(50); tab.add(MBR, 50); assertEquals(50, tab.getHighestReceived(MBR)); assertEquals(51, tab.getNextToReceive(MBR)); } public void testAdd() { tab=new SeqnoTable(0); tab.add(MBR, 0); tab.add(MBR, 1); tab.add(MBR, 2); assertEquals(2, tab.getHighestReceived(MBR)); assertEquals(3, tab.getNextToReceive(MBR)); } public void testAddWithGaps() { tab=new SeqnoTable(0); boolean rc=tab.add(MBR, 0); assertTrue(rc); rc=tab.add(MBR, 1); assertTrue(rc); rc=tab.add(MBR, 2); assertTrue(rc); rc=tab.add(MBR, 4); assertTrue(rc); rc=tab.add(MBR, 5); assertTrue(rc); System.out.println("tab: " + tab); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(3, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 3); assertTrue(rc); System.out.println("tab: " + tab); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(6, tab.getNextToReceive(MBR)); } public void testAddWithGaps2() { tab=new SeqnoTable(0); boolean rc=tab.add(MBR, 5); System.out.println("tab: " + tab); assertTrue(rc); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 4); System.out.println("tab: " + tab); assertTrue(rc); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 3); System.out.println("tab: " + tab); assertTrue(rc); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 2); System.out.println("tab: " + tab); assertTrue(rc); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 1); System.out.println("tab: " + tab); assertTrue(rc); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(0, tab.getNextToReceive(MBR)); rc=tab.add(MBR, 0); System.out.println("tab: " + tab); assertTrue(rc); assertEquals(5, tab.getHighestReceived(MBR)); assertEquals(6, tab.getNextToReceive(MBR)); } public void testInsertionOfDuplicates() { tab=new SeqnoTable(0); boolean rc=tab.add(MBR, 0); assertTrue(rc); rc=tab.add(MBR, 0); assertFalse(rc); rc=tab.add(MBR, 1); assertTrue(rc); rc=tab.add(MBR, 2); assertTrue(rc); rc=tab.add(MBR, 4); assertTrue(rc); rc=tab.add(MBR, 5); assertTrue(rc); System.out.println("tab: " + tab); rc=tab.add(MBR, 2); assertFalse(rc); rc=tab.add(MBR, 3); assertTrue(rc); rc=tab.add(MBR, 3); assertFalse(rc); } public static Test suite() { return new TestSuite(SeqnoTableTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } }libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/DigestTest.java0000644000175000017500000003667111366547366031352 0ustar twernertwerner// $Id: DigestTest.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.util.Digest; import org.jgroups.util.MutableDigest; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import org.jgroups.Address; import java.io.*; import java.util.Map; import java.util.HashMap; public class DigestTest extends TestCase { Digest d, d2; MutableDigest md; IpAddress a1, a2, a3; public DigestTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); Map map=new HashMap(); a1=new IpAddress(5555); a2=new IpAddress(6666); a3=new IpAddress(7777); map.put(a1, new Digest.Entry(4, 500, 501)); map.put(a2, new Digest.Entry(25, 26, 26)); map.put(a3, new Digest.Entry(20, 25, 33)); d=new Digest(map); md=new MutableDigest(map); } public void testSize() { d2=new Digest(3); assertEquals(0, d2.size()); } public void testEquals() { d2=d.copy(); System.out.println("d: " + d + "\nd2= " + d2); assertEquals(d, d); assertEquals(d, d2); } public void testDifference(){ Map map=new HashMap(); a1=new IpAddress(5555); a2=new IpAddress(6666); a3=new IpAddress(7777); map.put(a1, new Digest.Entry(4, 500, 501)); map.put(a2, new Digest.Entry(25, 26, 26)); map.put(a3, new Digest.Entry(20, 25, 33)); Digest digest =new Digest(map); Map map2=new HashMap(); map2.put(a1, new Digest.Entry(4, 500, 501)); map2.put(a2, new Digest.Entry(25, 26, 26)); map2.put(a3, new Digest.Entry(20, 37, 33)); Digest digest2 =new Digest(map2); assertNotSame(digest, digest2); Digest diff = digest2.difference(digest); System.out.println(diff); assertTrue(diff.contains(a3)); assertEquals(1, diff.size()); Map map3=new HashMap(); map3.put(a1, new Digest.Entry(4, 500, 501)); map3.put(a2, new Digest.Entry(25, 26, 26)); map3.put(a3, new Digest.Entry(20, 37, 33)); map3.put(new IpAddress(8888), new Digest.Entry(1, 2, 3)); Digest digest3 =new Digest(map3); diff = digest3.difference(digest); System.out.println(diff); assertEquals(2, diff.size()); diff = digest3.difference(digest2); System.out.println(diff); assertEquals(1, diff.size()); Digest diff2 = digest2.difference(digest3); System.out.println(diff2); assertEquals(1, diff2.size()); assertEquals(diff, diff2); } public void testIsGreaterThanOrEqual() { Map map=new HashMap(); map.put(a1, new Digest.Entry(4, 500, 501)); map.put(a2, new Digest.Entry(25, 26, 26)); map.put(a3, new Digest.Entry(20, 25, 33)); Digest my=new Digest(map); System.out.println("\nd: " + d + "\nmy: " + my); assertTrue(my.isGreaterThanOrEqual(d)); map.remove(a3); map.put(a3, new Digest.Entry(20, 26, 33)); my=new Digest(map); System.out.println("\nd: " + d + "\nmy: " + my); assertTrue(my.isGreaterThanOrEqual(d)); map.remove(a3); map.put(a3, new Digest.Entry(20, 22, 32)); my=new Digest(map); System.out.println("\nd: " + d + "\nmy: " + my); assertFalse(my.isGreaterThanOrEqual(d)); } public void testEquals2() { md=new MutableDigest(d); System.out.println("d: " + d + "\nmd= " + md); assertEquals(d, d); assertEquals(d, md); md.incrementHighestDeliveredSeqno(a1); System.out.println("d: " + d + "\nmd= " + md); assertFalse(d.equals(md)); } public void testMutability() { Digest md2=md; assertEquals(md, md2); md.incrementHighestDeliveredSeqno(a2); assertEquals(md, md2); } public void testImmutability() { MutableDigest tmp=new MutableDigest(d); assertEquals(d, tmp); tmp.incrementHighestDeliveredSeqno(a2); assertFalse(d.equals(tmp)); } public void testImmutability2() { Digest tmp=d.copy(); assertEquals(d, tmp); } public void testImmutability3() { Digest tmp=new Digest(d); assertEquals(tmp, d); } public void testImmutability4() { Digest copy=md.copy(); assertEquals(copy, md); md.incrementHighestDeliveredSeqno(a1); assertFalse(copy.equals(md)); } public void testSeal() { MutableDigest tmp=new MutableDigest(3); tmp.add(a2, 1,2,3); assertEquals(1, tmp.size()); tmp.seal(); try { tmp.add(a2, 4,5,6); fail("should run into an exception"); } catch(IllegalAccessError e) { System.out.println("received exception \"" + e.toString() + "\" - as expected"); } assertEquals(1, tmp.size()); } public void testSeal2() { md.incrementHighestDeliveredSeqno(a1); md.seal(); try { md.incrementHighestDeliveredSeqno(a3); fail("should run into an exception"); } catch(IllegalAccessError e) { System.out.println("received exception \"" + e.toString() + "\" - as expected"); } MutableDigest tmp=new MutableDigest(md); tmp.incrementHighestDeliveredSeqno(a3); } public void testAdd() { assertEquals(3, md.size()); md.add(a1, 100, 200, 201); assertEquals(3, md.size()); md.add(new IpAddress(14526), 1,2,3); assertEquals(4, md.size()); } public void testAddDigest() { Digest tmp=md.copy(); md.add(tmp); assertEquals(3, md.size()); } public void testAddDigest2() { MutableDigest tmp=new MutableDigest(4); tmp.add(new IpAddress(1111), 1,2,3); tmp.add(new IpAddress(2222), 1,2,3); tmp.add(new IpAddress(5555), 1,2,3); tmp.add(new IpAddress(6666), 1,2,3); md.add(tmp); assertEquals(5, md.size()); } public void testGet() { Digest.Entry entry; entry=d.get(a1); assertEquals(entry, new Digest.Entry(4,500,501)); entry=d.get(a2); assertEquals(entry, new Digest.Entry(25,26,26)); entry=d.get(a3); assertEquals(entry, new Digest.Entry(20,25,33)); } public void testIncrementHighSeqno() { md=new MutableDigest(3); md.add(a1, 1, 100); md.add(a2, 3, 300); md.add(a3, 7, 700); long tmp=md.highestDeliveredSeqnoAt(a1); md.incrementHighestDeliveredSeqno(a1); assertEquals(md.highestDeliveredSeqnoAt(a1), tmp+1); tmp=md.highestDeliveredSeqnoAt(a2); md.incrementHighestDeliveredSeqno(a2); assertEquals(md.highestDeliveredSeqnoAt(a2), tmp+1); tmp=md.highestDeliveredSeqnoAt(a3); md.incrementHighestDeliveredSeqno(a3); assertEquals(md.highestDeliveredSeqnoAt(a3), tmp+1); } public void testConstructor() { assertEquals(3, md.size()); md.clear(); assertEquals(0, md.size()); md.clear(); assertEquals(0, md.size()); } public void testConstructor2() { Digest dd=new Digest(3); assertEquals(0, dd.size()); } public void testConstructor3() { Digest dd=new MutableDigest(3); assertEquals(0, dd.size()); } public void testContains() { assertTrue(d.contains(a1)); assertTrue(d.contains(a2)); assertTrue(d.contains(a3)); } public void testResetAt() { md.resetAt(a1); assertEquals(0, md.lowSeqnoAt(a1)); assertEquals(0, md.highestDeliveredSeqnoAt(a1)); assertEquals(0, md.highestReceivedSeqnoAt(a1)); } public void testLowSeqnoAt() { assertEquals(4, d.lowSeqnoAt(a1)); assertEquals(25, d.lowSeqnoAt(a2)); assertEquals(20, d.lowSeqnoAt(a3)); } public void testHighSeqnoAt() { assertEquals(500, d.highestDeliveredSeqnoAt(a1)); assertEquals(26, d.highestDeliveredSeqnoAt(a2)); assertEquals(25, d.highestDeliveredSeqnoAt(a3)); } // public void testSetHighSeqnoAt() { // assertEquals(500, md.highSeqnoAt(a1)); // md.setHighSeqnoAt(a1, 555); // assertEquals(555, md.highSeqnoAt(a1)); // } public void testHighSeqnoSeenAt() { assertEquals(501, d.highestReceivedSeqnoAt(a1)); assertEquals(26, d.highestReceivedSeqnoAt(a2)); assertEquals(33, d.highestReceivedSeqnoAt(a3)); } // public void testSetHighSeenSeqnoAt() { // assertEquals(26, md.highSeqnoSeenAt(a2)); // md.setHighSeqnoSeenAt(a2, 100); // assertEquals(100, md.highSeqnoSeenAt(a2)); // } public void testSetHighestDeliveredAndSeenSeqnoAt() { assertEquals(4, d.lowSeqnoAt(a1)); assertEquals(500, d.highestDeliveredSeqnoAt(a1)); assertEquals(501, md.highestReceivedSeqnoAt(a1)); md.setHighestDeliveredAndSeenSeqnos(a1, 2, 10, 20); assertEquals(2, md.lowSeqnoAt(a1)); assertEquals(10, md.highestDeliveredSeqnoAt(a1)); assertEquals(20, md.highestReceivedSeqnoAt(a1)); } public void testCopy() { d=d.copy(); testLowSeqnoAt(); testHighSeqnoAt(); testHighSeqnoSeenAt(); testContains(); testResetAt(); } public void testCopy2() { Digest tmp=d.copy(); assertEquals(tmp, d); } public void testMutableCopy() { Digest copy=md.copy(); System.out.println("md=" + md + "\ncopy=" + copy); assertEquals(md, copy); md.add(a1, 4, 500, 1000); System.out.println("md=" + md + "\ncopy=" + copy); assertFalse(md.equals(copy)); } public void testMerge() { Map map=new HashMap(); map.put(a1, new Digest.Entry(3, 499, 502)); map.put(a2, new Digest.Entry(20, 26, 27)); map.put(a3, new Digest.Entry(21, 26, 35)); MutableDigest digest=new MutableDigest(map); System.out.println("d: " + d); System.out.println("digest: " + digest); digest.merge(d); System.out.println("merged digest: " + digest); assertEquals(3, d.size()); assertEquals(3, digest.size()); assertEquals(3, digest.lowSeqnoAt(a1)); assertEquals(500, digest.highestDeliveredSeqnoAt(a1)); assertEquals(502, digest.highestReceivedSeqnoAt(a1)); assertEquals(20, digest.lowSeqnoAt(a2)); assertEquals(26, digest.highestDeliveredSeqnoAt(a2)); assertEquals(27, digest.highestReceivedSeqnoAt(a2)); assertEquals(20, digest.lowSeqnoAt(a3)); assertEquals(26, digest.highestDeliveredSeqnoAt(a3)); assertEquals(35, digest.highestReceivedSeqnoAt(a3)); } public void testNonConflictingMerge() { MutableDigest cons_d=new MutableDigest(5); IpAddress ip1=new IpAddress(1111), ip2=new IpAddress(2222); cons_d.add(ip1, 1, 10, 10); cons_d.add(ip2, 2, 20, 20); // System.out.println("\ncons_d before: " + cons_d); cons_d.merge(d); assertEquals(5, cons_d.size()); //System.out.println("\ncons_d after: " + cons_d); assertEquals(1, cons_d.lowSeqnoAt(ip1)); assertEquals(2, cons_d.lowSeqnoAt(ip2)); assertEquals(4, cons_d.lowSeqnoAt(a1)); assertEquals(25, cons_d.lowSeqnoAt(a2)); assertEquals(20, cons_d.lowSeqnoAt(a3)); assertEquals(10, cons_d.highestDeliveredSeqnoAt(ip1)); assertEquals(20, cons_d.highestDeliveredSeqnoAt(ip2)); assertEquals(500, cons_d.highestDeliveredSeqnoAt(a1)); assertEquals(26, cons_d.highestDeliveredSeqnoAt(a2)); assertEquals(25, cons_d.highestDeliveredSeqnoAt(a3)); assertEquals(10, cons_d.highestReceivedSeqnoAt(ip1)); assertEquals(20, cons_d.highestReceivedSeqnoAt(ip2)); assertEquals(501, cons_d.highestReceivedSeqnoAt(a1)); assertEquals(26, cons_d.highestReceivedSeqnoAt(a2)); assertEquals(33, cons_d.highestReceivedSeqnoAt(a3)); } public void testConflictingMerge() { MutableDigest new_d=new MutableDigest(2); new_d.add(a1, 5, 450, 501); new_d.add(a3, 18, 28, 35); //System.out.println("\nd before: " + d); //System.out.println("new_: " + new_d); md.merge(new_d); assertEquals(3, md.size()); //System.out.println("d after: " + d); assertEquals(4, md.lowSeqnoAt(a1)); // low_seqno should *not* have changed assertEquals(500, md.highestDeliveredSeqnoAt(a1)); // high_seqno should *not* have changed assertEquals(501, md.highestReceivedSeqnoAt(a1)); // high_seqno_seen should *not* have changed assertEquals(25, md.lowSeqnoAt(a2)); // low_seqno should *not* have changed assertEquals(26, md.highestDeliveredSeqnoAt(a2)); // high_seqno should *not* have changed assertEquals(26, md.highestReceivedSeqnoAt(a2)); // high_seqno_seen should *not* have changed assertEquals(18, md.lowSeqnoAt(a3)); // low_seqno should *not* have changed assertEquals(28, md.highestDeliveredSeqnoAt(a3)); // high_seqno should *not* have changed assertEquals(35, md.highestReceivedSeqnoAt(a3)); // high_seqno_seen should *not* have changed } public void testSameSendersOtherIsNull() { assertFalse(d.sameSenders(null)); } public void testSameSenders1MNullDifferentLenth() { d2=new Digest(1); assertFalse(d2.sameSenders(d)); } public void testSameSenders1MNullSameLength() { d2=new Digest(3); assertFalse(d2.sameSenders(d)); } public void testSameSendersIdentical() { d2=d.copy(); assertTrue(d.sameSenders(d2)); } public void testSameSendersNotIdentical() { MutableDigest tmp=new MutableDigest(3); tmp.add(a1, 4, 500, 501); tmp.add(a3, 20, 25, 33); tmp.add(a2, 25, 26, 26); assertTrue(md.sameSenders(tmp)); assertTrue(d.sameSenders(tmp)); } public void testSameSendersNotSameLength() { md=new MutableDigest(3); md.add(a1, 4, 500, 501); md.add(a2, 25, 26, 26); assertFalse(d.sameSenders(md)); } public void testStreamable() throws IOException, IllegalAccessException, InstantiationException { ByteArrayOutputStream outstream=new ByteArrayOutputStream(); DataOutputStream dos=new DataOutputStream(outstream); d.writeTo(dos); dos.close(); byte[] buf=outstream.toByteArray(); ByteArrayInputStream instream=new ByteArrayInputStream(buf); DataInputStream dis=new DataInputStream(instream); Digest tmp=new Digest(); tmp.readFrom(dis); assertEquals(d, tmp); } public void testSerializedSize() throws Exception { long len=d.serializedSize(); byte[] buf=Util.streamableToByteBuffer(d); assertEquals(len, buf.length); } public static Test suite() { return new TestSuite(DigestTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ReusableThreadTest.java0000644000175000017500000000667411366547366033025 0ustar twernertwerner// $Id: ReusableThreadTest.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.ReusableThread; import org.jgroups.util.Util; public class ReusableThreadTest extends TestCase { public ReusableThreadTest(String name) { super(name); } static class MyThread implements Runnable { int num=0; String name=null; public MyThread(int num) { this.num=num; this.name="Thread #" + num; } public void run() { System.out.println("Thread " + name + " is run"); long sleep_time=(long)(Math.random() * 5000); System.out.print("Thread #" + num + ": sleeping " + sleep_time + ':'); Util.sleep(sleep_time); //throw new Error("hello world"); System.out.println(" -- done"); } } static class LongRunningThread extends MyThread { long sleep_time=10000; public LongRunningThread(int num) { super(num); name="LongRunningThread #" + num; } public LongRunningThread(int num, long sleep_time) { super(num); this.sleep_time=sleep_time; name="LongRunningThread #" + num; } public void run() { System.out.println("LongRunningThread " + name + " is run"); System.out.println("LongRunningThread #" + num + ": sleeping " + sleep_time + " msecs"); Util.sleep(sleep_time); } } public void testReusableThread() { ReusableThread t=new ReusableThread("Demo ReusableThread"); MyThread m1=new MyThread(1); MyThread m2=new MyThread(2); LongRunningThread m4=new LongRunningThread(4); System.out.println("Assigning task"); t.assignTask(m4); System.out.println("Sleeping 2 secs"); Util.sleep(2000); System.out.println("stop()"); t.stop(); System.out.println("stop() -- done"); Util.printThreads(); System.out.println("\nAssigning task 1"); t.assignTask(m1); t.waitUntilDone(); // passive wait System.out.println("done with task 1"); Util.printThreads(); System.out.println("\nAssigning task 2"); t.assignTask(m2); t.waitUntilDone(); System.out.println("done with task 2"); System.out.println("Stopping thread"); t.stop(); System.out.println("done"); Util.printThreads(); } public void testAssignMultipleTimes() { ReusableThread t=new ReusableThread("Demo ReusableThread"); LongRunningThread t1, t2; t1=new LongRunningThread(1, 500); t2=new LongRunningThread(2, 300); t.start(); t.assignTask(t1); t.waitUntilDone(); assertTrue(t.done()); t.assignTask(t2); t.waitUntilDone(); assertTrue(t.done()); } public void testStop() { ReusableThread t=new ReusableThread("Demo ReusableThread"); LongRunningThread t1; t1=new LongRunningThread(1, 20000); t.assignTask(t1); Util.sleep(1000); t.stop(); t.waitUntilDone(); assertTrue(t.done()); assertFalse(t.isAlive()); } public static void main(String[] args) { String[] testCaseName={ReusableThreadTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/CustomProtocolTest.java0000644000175000017500000000412011366547366033107 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.JChannel; import org.jgroups.stack.Protocol; /** * Tests custom protocol. * Author: Lenny Phan * Version: $Id: CustomProtocolTest.java,v 1.1.4.1 2007/11/20 08:53:45 belaban Exp $ */ public class CustomProtocolTest extends TestCase { static final String PROTOCOL_STACK = "UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=32):" + "org.jgroups.tests.CustomProtocolTest$MyProtocol:" + "PING(timeout=3000;num_initial_members=6):" + "FD(timeout=3000):" + "VERIFY_SUSPECT(timeout=1500):" + "pbcast.NAKACK(gc_lag=10;retransmit_timeout=600,1200,2400,4800):" + "UNICAST(timeout=600,1200,2400,4800):" + "pbcast.STABLE(desired_avg_gossip=10000):" + "FRAG:" + "pbcast.GMS(join_timeout=5000;" + "shun=true;print_local_addr=true)"; public void testMyProtocol() throws Exception { System.out.println("PROTOCOL_STACK: " + PROTOCOL_STACK); JChannel channel = new JChannel(PROTOCOL_STACK); assertTrue(true); } // --------- BOILERPLATE CODE ----------------------------------- /** * Constructor. Normally called by JUnit framework. * * @param testName The test name. * @throws Exception */ public CustomProtocolTest(String testName) throws Exception { super(testName); } /** * A main() to allow running this test case by itself. * * @param args The command-line arguments. */ public static void main(String args[]) { String[] testCaseName = {"com.oracle.jgroups.protocols.CustomProtocolTest"}; junit.textui.TestRunner.main(testCaseName); } /** * Compose a suite of test cases. * * @return The suite. */ public static TestSuite suite() { return new TestSuite(CustomProtocolTest.class); } public static class MyProtocol extends Protocol { public String getName() { return "MyProtocol"; } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/QueueTest.java0000644000175000017500000007016711366547366031215 0ustar twernertwerner// $Id: QueueTest.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.TimeoutException; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import org.jgroups.util.Util; import java.util.LinkedList; import java.util.ArrayList; public class QueueTest extends TestCase { private Queue queue=null; public QueueTest(String Name_) { super(Name_); } public void setUp() throws Exception { super.setUp(); queue=new Queue(); } public void tearDown() throws Exception { super.tearDown(); if(queue != null) { queue.reset(); } } public void testQueue() { try { queue.add("Q1"); queue.add("Q2"); queue.add("Q3"); assertEquals("Q1", queue.peek()); assertEquals("Q1", queue.remove()); assertEquals("Q2", queue.peek()); assertEquals("Q2", queue.remove()); queue.addAtHead("Q4"); queue.add("Q5"); assertEquals("Q4", queue.peek()); assertEquals("Q4", queue.remove()); queue.close(true); try { queue.add("Q6"); fail("should not get here"); } catch(org.jgroups.util.QueueClosedException qc) { assertTrue(true); } int size=queue.size(); queue.removeElement("Q5"); assertEquals((size - 1), queue.size()); assertEquals("Q3", queue.peek()); assertEquals("Q3", queue.remove()); assertTrue(queue.closed()); System.out.println("Everything is ok"); } catch(Exception x) { System.out.println(x); fail(); } } public void testCloseWithoutFlush() { queue.close(false); try { queue.remove(); fail("we should have gotten a QueueClosedException trying to remove an element from a closed queue"); } catch(QueueClosedException e) { assertTrue("queue is closed, this is okay", queue.closed()); } } public void testCloseWithFlush() { queue.close(true); try { queue.remove(); fail("we should have gotten a QueueClosedException trying to remove an element from a closed queue"); } catch(QueueClosedException e) { assertTrue("queue is closed, this is okay", queue.closed()); } } public void testCloseWithFlush2() throws QueueClosedException { queue.add(new Integer(1)); queue.add(new Integer(2)); queue.add(new Integer(3)); queue.close(true); try { for(int i=1; i <= 3; i++) { Object obj=queue.remove(); assertNotNull(obj); assertEquals(obj, new Integer(i)); } queue.remove(); fail("we should have gotten a QueueClosedException trying to remove an element from a closed queue"); } catch(QueueClosedException e) { assertTrue("queue is closed, this is okay", queue.closed()); } } public void testValues() throws QueueClosedException { queue.add(new Integer(1)); queue.add(new Integer(3)); queue.add(new Integer(99)); queue.add(new Integer(8)); System.out.println("queue: " + Util.dumpQueue(queue)); int size=queue.size(); assertEquals(4, size); LinkedList values=queue.values(); assertEquals(size, values.size()); } public void testLargeInsertion() { String element="MyElement"; long start, stop; try { System.out.println("Inserting 100000 elements"); start=System.currentTimeMillis(); for(int i=0; i < 100000; i++) queue.add(element); stop=System.currentTimeMillis(); System.out.println("Took " + (stop - start) + " msecs"); System.out.println("Removing 100000 elements"); start=System.currentTimeMillis(); while(queue.size() > 0) queue.remove(); stop=System.currentTimeMillis(); System.out.println("Took " + (stop - start) + " msecs"); } catch(Exception ex) { System.err.println(ex); fail(); } } public void testEmptyQueue() { assertNull(queue.getFirst()); assertNull(queue.getLast()); assertEquals(queue.getFirst(), queue.getLast()); // both are null; they're equal } public void testAddAll() throws QueueClosedException { ArrayList l=new ArrayList(); l.add("one"); l.add("two"); l.add("three"); queue.addAll(l); System.out.println("queue is " + queue); assertEquals(3, queue.size()); assertEquals("one", queue.remove()); assertEquals(2, queue.size()); assertEquals("two", queue.remove()); assertEquals(1, queue.size()); assertEquals("three", queue.remove()); assertEquals(0, queue.size()); } public void testInsertionAndRemoval() throws Exception { String s1="Q1", s2="Q2"; queue.add(s1); assertTrue(queue.getFirst() != null); assertTrue(queue.getLast() != null); assertEquals(queue.getFirst(), queue.getLast()); queue.add(s2); assertTrue(queue.getFirst() != queue.getLast()); Object o1=queue.peek(); Object o2=queue.getFirst(); System.out.println("o1=" + o1 + ", o2=" + o2 + ", o1.equals(o2)=" + o1.equals(o2)); assertEquals(queue.peek(), queue.getFirst()); queue.remove(); assertEquals(1, queue.size()); assertEquals(queue.getFirst(), queue.getLast()); queue.remove(); assertEquals(0, queue.size()); assertTrue(queue.getFirst() == null); assertTrue(queue.getLast() == null); } public void testWaitUntilClosed() { queue.close(true); queue.waitUntilClosed(0); assertEquals(0, queue.size()); } public void testWaitUntilClosed2() { queue.close(true); try { queue.peek(); fail("peek() should throw a QueueClosedException"); } catch(QueueClosedException e) { assertTrue(e != null); } assertEquals(0, queue.size()); } public void testWaitUntilClosed3() throws QueueClosedException { queue.add("one"); queue.close(true); Object obj=queue.peek(); assertEquals("one", obj); assertEquals(1, queue.size()); queue.remove(); try { queue.peek(); fail("peek() should throw a QueueClosedException"); } catch(QueueClosedException e) { assertTrue(e != null); } assertEquals(0, queue.size()); } public void testWaitUntilClosed4() throws QueueClosedException { for(int i=0; i < 10; i++) queue.add(new Integer(i)); new Thread() { public void run() { while(!queue.closed()) { try { System.out.println("-- removed " + queue.remove()); Util.sleep(200); } catch(QueueClosedException e) { break; } } } }.start(); queue.close(true); queue.waitUntilClosed(0); assertEquals(0, queue.size()); } public void testWaitUntilClosed5() throws QueueClosedException { for(int i=0; i < 10; i++) queue.add(new Integer(i)); new Thread() { public void run() { while(!queue.closed()) { try { System.out.println("-- removed " + queue.remove()); Util.sleep(200); } catch(QueueClosedException e) { System.out.println("-- queue is closed, cannot remove element"); break; } } } }.start(); Util.sleep(600); queue.close(false); queue.waitUntilClosed(0); assertTrue(queue.size() > 0); } public void testRemoveElementNoElement() { String s1="Q1"; try { queue.removeElement(s1); assertFalse(queue.closed()); assertEquals(0, queue.size()); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveElementOneElement() { String s1="Q1"; try { queue.add(s1); queue.removeElement(s1); assertEquals(0, queue.size()); assertTrue(queue.getFirst() == null); assertTrue(queue.getLast() == null); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveElementTwoElementsFirstFound() { String s1="Q1", s2="Q2"; try { queue.add(s1); queue.add(s2); queue.removeElement(s1); assertEquals(1, queue.size()); assertEquals(queue.getFirst(), s2); assertEquals(queue.getLast(), s2); assertEquals(queue.getFirst(), queue.getLast()); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveElementTwoElementsSecondFound() { String s1="Q1", s2="Q2"; try { queue.add(s1); queue.add(s2); queue.removeElement(s2); assertEquals(1, queue.size()); assertEquals(queue.getFirst(), s1); assertEquals(queue.getLast(), s1); assertEquals(queue.getFirst(), queue.getLast()); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveElementThreeElementsFirstFound() { String s1="Q1", s2="Q2", s3="Q3"; try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s1); assertEquals(2, queue.size()); assertEquals(queue.getFirst(), s2); assertEquals(queue.getLast(), s3); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveElementThreeElementsSecondFound() { String s1="Q1", s2="Q2", s3="Q3"; try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s2); assertEquals(2, queue.size()); assertEquals(queue.getFirst(), s1); assertEquals(queue.getLast(), s3); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveElementThreeElementsThirdFound() { String s1="Q1", s2="Q2", s3="Q3"; try { queue.add(s1); queue.add(s2); queue.add(s3); queue.removeElement(s3); assertEquals(2, queue.size()); assertEquals(queue.getFirst(), s1); assertEquals(queue.getLast(), s2); } catch(QueueClosedException ex) { fail(ex.toString()); } } public void testRemoveAndClose() { try { new Thread() { public void run() { Util.sleep(1000); queue.close(true); // close gracefully } }.start(); queue.remove(); fail("we should not be able to remove an object from a closed queue"); } catch(QueueClosedException ex) { assertTrue(ex instanceof QueueClosedException); // of course, stupid comparison... } } public void testRemoveAndCloseWithTimeout() throws TimeoutException { try { new Thread() { public void run() { Util.sleep(1000); queue.close(true); // close gracefully } }.start(); queue.remove(5000); fail("we should not be able to remove an object from a closed queue"); } catch(QueueClosedException ex) { assertTrue(ex instanceof QueueClosedException); // of course, stupid comparison... } catch(TimeoutException timeout) { fail("we should not get a TimeoutException, but a QueueClosedException here"); } } public void testInterruptAndRemove() throws QueueClosedException { Thread.currentThread().interrupt(); Object el=null; try { el=queue.remove(2000); fail("we should not get here"); } catch(TimeoutException e) { assertNull(el); } } public void testRemoveAndInterrupt() { Thread closer=new Thread() { public void run() { Util.sleep(1000); System.out.println("-- closing queue"); queue.close(false); } }; closer.start(); System.out.println("-- removing element"); try { queue.remove(); fail("we should not get here, as the queue is closed"); } catch(QueueClosedException e) { System.out.println("-- received queue closed exception - as expected"); } } public void testClear() throws QueueClosedException { queue.add("one"); queue.add("two"); assertEquals(2, queue.size()); queue.close(true); assertEquals(2, queue.size()); queue.clear(); assertEquals(0, queue.size()); queue=new Queue(); queue.add("one"); queue.add("two"); queue.clear(); assertEquals(0, queue.size()); queue.add("one"); queue.add("two"); assertEquals(2, queue.size()); queue.clear(); assertEquals(0, queue.size()); } // public void testWaitUntilEmpty() { // try { // queue.add("one"); // queue.add("two"); // queue.add("three"); // // new Thread() { // public void run() { // try { // sleep(1000); // queue.remove(); // queue.remove(); // queue.remove(); // } // catch(Exception e) { // } // } // }.start(); // // queue.waitUntilEmpty(0); // assertEquals(queue.size(), 0); // } // catch(Exception e) { // e.printStackTrace(); // fail(e.toString()); // } // } // // public void testWaitUntilEmpty2() { // try { // queue.add("one"); // queue.add("two"); // queue.add("three"); // // new Thread() { // public void run() { // try { // sleep(1000); // queue.remove(); // queue.remove(); // } // catch(Exception e) { // } // } // }.start(); // // queue.waitUntilEmpty(3000); // fail("shouldn't get here; we should have caught a TimeoutException"); // } // catch(TimeoutException timeout) { // assertTrue(true); // } // catch(Exception e) { // e.printStackTrace(); // fail(e.toString()); // } // } // // // public void testWaitUntilQueueClosed() { // try { // queue.add("one"); // queue.add("two"); // queue.add("three"); // // new Thread() { // public void run() { // try { // sleep(1000); // queue.close(false); // } // catch(Exception e) { // } // } // }.start(); // // queue.waitUntilEmpty(0); // fail("shouldn't get here; we should have caught a QueueClosedException"); // } // catch(TimeoutException timeout) { // fail("we should not have gottem here"); // } // catch(QueueClosedException ex2) { // assertTrue(true); // } // catch(Exception e) { // e.printStackTrace(); // fail(); // } // } /** Multiple threads call remove(), one threads then adds an element. Only 1 thread should actually terminate * (the one that has the element) */ public void testBarrier() { RemoveOneItem[] removers=new RemoveOneItem[10]; int num_dead=0; for(int i=0; i < removers.length; i++) { removers[i]=new RemoveOneItem(i); removers[i].start(); } Util.sleep(1000); System.out.println("-- adding element 99"); try { queue.add(new Long(99)); } catch(Exception ex) { System.err.println(ex); } Util.sleep(5000); System.out.println("-- adding element 100"); try { queue.add(new Long(100)); } catch(Exception ex) { System.err.println(ex); } Util.sleep(1000); for(int i=0; i < removers.length; i++) { System.out.println("remover #" + i + " is " + (removers[i].isAlive() ? "alive" : "terminated")); if(!removers[i].isAlive()) { num_dead++; } } assertEquals(2, num_dead); } /** Multiple threads call remove(), one threads then adds an element. Only 1 thread should actually terminate * (the one that has the element) */ public void testBarrierWithTimeOut() { RemoveOneItemWithTimeout[] removers = new RemoveOneItemWithTimeout[10]; int num_dead = 0; for (int i = 0; i < removers.length; i++) { removers[i] = new RemoveOneItemWithTimeout(i, 1000); removers[i].start(); } Util.sleep(5000); System.out.println("-- adding element 99"); try { queue.add(new Long(99)); } catch (Exception ex) { System.err.println(ex); } Util.sleep(5000); System.out.println("-- adding element 100"); try { queue.add(new Long(100)); } catch (Exception ex) { System.err.println(ex); } Util.sleep(1000); for (int i = 0; i < removers.length; i++) { System.out.println("remover #" + i + " is " + (removers[i].isAlive() ? "alive" : "terminated")); if (!removers[i].isAlive()) { num_dead++; } } assertEquals(2, num_dead); queue.close(false); // will cause all threads still blocking on peek() to return Util.sleep(2000); num_dead = 0; for (int i = 0; i < removers.length; i++) { System.out.println("remover #" + i + " is " + (removers[i].isAlive() ? "alive" : "terminated")); if (!removers[i].isAlive()) { num_dead++; } } assertEquals(10, num_dead); } /** Multiple threads add one element, one thread read them all. * (the one that has the element) */ public void testMultipleWriterOneReader() { AddOneItem[] adders = new AddOneItem[10]; int num_dead = 0; int num_items = 0; int items = 1000; for (int i = 0; i < adders.length; i++) { adders[i] = new AddOneItem(i, items); adders[i].start(); } while (num_items < (adders.length*items)) { try { queue.remove(); num_items++; } catch (Exception ex) { System.err.println(ex); } } Util.sleep(1000); for (int i = 0; i < adders.length; i++) { System.out.println("adder #" + i + " is " + (adders[i].isAlive() ? "alive" : "terminated")); if (!adders[i].isAlive()) { num_dead++; } } assertEquals(10, num_dead); queue.close(false); // will cause all threads still blocking on peek() to return } /** * Times how long it takes to add and remove 1000000 elements concurrently (1 reader, 1 writer) */ public void testConcurrentAddRemove() { final long NUM=1000000; long num_received=0; Object ret; long start, stop; start=System.currentTimeMillis(); new Thread() { public void run() { for(int i=0; i < NUM; i++) { try { queue.add(new Object()); } catch(QueueClosedException e) { } } } }.start(); while(num_received < NUM) { try { ret=queue.remove(); if(ret != null) num_received++; } catch(QueueClosedException e) { e.printStackTrace(); fail(); } } assertEquals(NUM, num_received); stop=System.currentTimeMillis(); System.out.println("time to add/remove " + NUM + " elements: " + (stop-start)); } /** Has multiple threads add(), remove() and peek() elements to/from the queue */ public void testConcurrentAccess() { final int NUM_THREADS=10; final int INTERVAL=20000; Writer[] writers=new Writer[NUM_THREADS]; Reader[] readers=new Reader[NUM_THREADS]; int[] writes=new int[NUM_THREADS]; int[] reads=new int[NUM_THREADS]; long total_reads=0, total_writes=0; for(int i=0; i < writers.length; i++) { readers[i]=new Reader(i, reads); readers[i].start(); writers[i]=new Writer(i, writes); writers[i].start(); } Util.sleep(INTERVAL); System.out.println("current queue size=" + queue.size()); for(int i=0; i < writers.length; i++) { writers[i].stopThread(); } for(int i=0; i < readers.length; i++) { readers[i].stopThread(); } queue.close(false); // will cause all threads still blocking on peek() to return System.out.println("current queue size=" + queue.size()); for(int i=0; i < writers.length; i++) { try { writers[i].join(300); readers[i].join(300); } catch(Exception ex) { System.err.println(ex); } } for(int i=0; i < writes.length; i++) { System.out.println("Thread #" + i + ": " + writes[i] + " writes, " + reads[i] + " reads"); total_writes+=writes[i]; total_reads+=reads[i]; } System.out.println("total writes=" + total_writes + ", total_reads=" + total_reads + ", diff=" + Math.abs(total_writes - total_reads)); } class AddOneItem extends Thread { Long retval = null; int rank = 0; int iteration = 0; AddOneItem(int rank, int iteration) { super("AddOneItem thread #" + rank); this.rank = rank; this.iteration = iteration; setDaemon(true); } public void run() { try { for (int i = 0; i < iteration; i++) { queue.add(new Long(rank)); // Util.sleepRandom(1); // System.out.println("Thread #" + rank + " added element (" + rank + ")"); } } catch (QueueClosedException closed) { System.err.println("Thread #" + rank + ": queue was closed"); } } } class RemoveOneItem extends Thread { Long retval=null; int rank=0; RemoveOneItem(int rank) { super("RemoveOneItem thread #" + rank); this.rank=rank; setDaemon(true); } public void run() { try { retval=(Long)queue.remove(); // System.out.println("Thread #" + rank + " removed element (" + retval + ")"); } catch(QueueClosedException closed) { System.err.println("Thread #" + rank + ": queue was closed"); } } Long getRetval() { return retval; } } class RemoveOneItemWithTimeout extends Thread { Long retval = null; int rank = 0; long timeout = 0; RemoveOneItemWithTimeout(int rank, long timeout) { super("RemoveOneItem thread #" + rank); this.rank = rank; this.timeout=timeout; setDaemon(true); } public void run() { boolean finished = false; while (!finished) { try { retval = (Long) queue.remove(timeout); // System.out.println("Thread #" + rank + " removed element (" + retval + ")"); finished = true; } catch (QueueClosedException closed) { System.err.println("Thread #" + rank + ": queue was closed"); finished = true; } catch (TimeoutException e) { } } } Long getRetval() { return retval; } } class Writer extends Thread { int rank=0; int num_writes=0; boolean running=true; int[] writes=null; Writer(int i, int[] writes) { super("WriterThread"); rank=i; this.writes=writes; setDaemon(true); } public void run() { while(running) { try { queue.add(new Long(System.currentTimeMillis())); num_writes++; } catch(QueueClosedException closed) { running=false; } catch(Throwable t) { System.err.println("QueueTest.Writer.run(): exception=" + t); } } writes[rank]=num_writes; } void stopThread() { running=false; } } class Reader extends Thread { int rank; int num_reads=0; int[] reads=null; boolean running=true; Reader(int i, int[] reads) { super("ReaderThread"); rank=i; this.reads=reads; setDaemon(true); } public void run() { Long el; while(running) { try { el=(Long)queue.remove(); if(el == null) { // @remove System.out.println("QueueTest.Reader.run(): peek() returned null element. " + "queue.size()=" + queue.size() + ", queue.closed()=" + queue.closed()); } assertNotNull(el); num_reads++; } catch(QueueClosedException closed) { running=false; } catch(Throwable t) { System.err.println("QueueTest.Reader.run(): exception=" + t); } } reads[rank]=num_reads; } void stopThread() { running=false; } } public static void main(String[] args) { String[] testCaseName={QueueTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckSenderWindowStressTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckSenderWindowStressTest0000644000175000017500000001350511366547366033435 0ustar twernertwerner// $Id: AckSenderWindowStressTest.java,v 1.1.2.1 2009/09/14 15:31:01 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Message; import org.jgroups.stack.AckSenderWindow; import org.jgroups.stack.StaticInterval; import org.jgroups.util.Util; import org.jgroups.util.TimeScheduler; import java.util.HashMap; /** * Test cases for AckSenderWindow * @author Bela Ban */ public class AckSenderWindowStressTest extends TestCase { AckSenderWindow win=null; final int NUM_MSGS=100; long[] xmit_timeouts={1000, 2000, 4000, 8000}; double PERCENTAGE_OFF=1.3; // how much can expected xmit_timeout and real timeout differ to still be okay ? HashMap msgs=new HashMap(); // keys=seqnos (Long), values=Entries protected TimeScheduler timer=null; protected void setUp() throws Exception { super.setUp(); timer=new TimeScheduler(10); } protected void tearDown() throws Exception { timer.stop(); super.tearDown(); } class Entry { long start_time=0; // time message was added long first_xmit=0; // time between start_time and first_xmit should be ca. 1000ms long second_xmit=0; // time between first_xmit and second_xmit should be ca. 2000ms long third_xmit=0; // time between third_xmit and second_xmit should be ca. 4000ms long fourth_xmit=0; // time between third_xmit and second_xmit should be ca. 8000ms Entry() { start_time=System.currentTimeMillis(); } /** * Entry is correct if xmit timeouts are not more than 30% off the mark */ boolean isCorrect(long seqno) { long t; long expected; long diff, delta; boolean off=false; t=first_xmit - start_time; expected=xmit_timeouts[0]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=second_xmit - first_xmit; expected=xmit_timeouts[1]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=third_xmit - second_xmit; expected=xmit_timeouts[2]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=fourth_xmit - third_xmit; expected=xmit_timeouts[3]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; if(off) { System.err.println("#" + seqno + ": " + this + ": (" + "entry is more than " + PERCENTAGE_OFF + " percentage off "); return false; } return true; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(first_xmit - start_time).append(", ").append(second_xmit - first_xmit).append(", "); sb.append(third_xmit - second_xmit).append(", ").append(fourth_xmit - third_xmit); return sb.toString(); } } class MyRetransmitCommand implements AckSenderWindow.RetransmitCommand { public void retransmit(long seqno, Message msg) { Entry entry=(Entry)msgs.get(new Long(seqno)); // System.out.println(" -- retransmit(" + seqno + ")"); if(entry != null) { if(entry.first_xmit == 0) { entry.first_xmit=System.currentTimeMillis(); return; } if(entry.second_xmit == 0) { entry.second_xmit=System.currentTimeMillis(); return; } if(entry.third_xmit == 0) { entry.third_xmit=System.currentTimeMillis(); return; } if(entry.fourth_xmit == 0) { entry.fourth_xmit=System.currentTimeMillis(); return; } } } } public AckSenderWindowStressTest(String name) { super(name); } /** * Tests whether retransmits are called at correct times for 1000 messages */ public void testRetransmits() { Entry entry; int num_non_correct_entries=0; win=new AckSenderWindow(new MyRetransmitCommand(), new StaticInterval(xmit_timeouts), timer); // 1. Send NUM_MSGS messages: System.out.println("-- sending " + NUM_MSGS + " messages:"); for(long i=0; i < NUM_MSGS; i++) { msgs.put(new Long(i), new Entry()); win.add(i, new Message()); } System.out.println("-- done"); // 2. Wait for at least 4 xmits/msg: total of 1000 + 2000 + 4000 + 8000ms = 15000ms; wait for 20000ms System.out.println("-- waiting for 20 secs for all retransmits"); Util.sleep(20000); // 3. Check whether all Entries have correct retransmission times for(long i=0; i < NUM_MSGS; i++) { entry=(Entry)msgs.get(new Long(i)); if(!entry.isCorrect(i)) { num_non_correct_entries++; } } if(num_non_correct_entries > 0) System.err.println("Number of incorrect retransmission timeouts: " + num_non_correct_entries); assertTrue(num_non_correct_entries == 0); win.reset(); } public static Test suite() { TestSuite suite; suite=new TestSuite(AckSenderWindowStressTest.class); return (suite); } public static void main(String[] args) { String[] name={AckSenderWindowStressTest.class.getName()}; junit.textui.TestRunner.main(name); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/FragTest.java0000644000175000017500000000575011366547366031004 0ustar twernertwerner// $Id: FragTest.java,v 1.1 2007/07/04 07:29:34 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.debug.ProtocolTester; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; /** * Class to test FRAG protocol. It uses ProtocolTester to assemble a minimal stack which only consists of * FRAG and LOOPBACK (messages are immediately resent up the stack). Sends NUM_MSGS with MSG_SIZE size down * the stack, they should be received as well. * * @author Bela Ban */ public class FragTest extends TestCase { public static final long NUM_MSGS=10; public static final int MSG_SIZE=100000; public static final int FRAG_SIZE=24000; public FragTest(String name) { super(name); } private Message createBigMessage(int size) { byte[] buf=new byte[size]; for(int i=0; i < buf.length; i++) buf[i]=(byte)'x'; return new Message(null, null, buf); } public void test0() throws Exception { Object mutex=new Object(); FragReceiver frag_receiver=new FragReceiver(this, mutex); ProtocolTester t=new ProtocolTester("FRAG(frag_size=" + FRAG_SIZE + ')', frag_receiver); Message big_msg; IpAddress local_addr=new IpAddress(5555); System.out.println("\nProtocol for protocol tester: " + t.getProtocolSpec() + '\n'); synchronized(mutex) { for(int i=0; i < NUM_MSGS; i++) { big_msg=createBigMessage(MSG_SIZE); big_msg.setSrc(local_addr); System.out.println("sending msg #" + i + " [" + big_msg.getLength() + " bytes]"); frag_receiver.down(new Event(Event.MSG, big_msg)); Util.sleep(10); } } t.stop(); } public static Test suite() { return new TestSuite(FragTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } private static class FragReceiver extends Protocol { long num_msgs=0; FragTest t=null; Object mut=null; FragReceiver(FragTest t, Object mut) { this.t=t; this.mut=mut; } public String getName() { return "FragReceiver"; } public Object up(Event evt) { Message msg=null; Address sender; if(evt == null || evt.getType() != Event.MSG) return null; msg=(Message)evt.getArg(); sender=msg.getSrc(); if(sender == null) { log.error("FragTest.FragReceiver.up(): sender is null; discarding msg"); return null; } System.out.println("Received msg from " + sender + " [" + msg.getLength() + " bytes]"); return null; } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ViewTest.java0000644000175000017500000000560011366547366031031 0ustar twernertwerner// $Id: ViewTest.java,v 1.2 2007/08/08 10:14:00 belaban Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.View; import org.jgroups.ViewId; import org.jgroups.stack.IpAddress; import java.util.Vector; public class ViewTest extends TestCase { IpAddress a, b, c, d, e, f, g, h, i, j, k; View view; Vector members; public ViewTest(String Name_) { super(Name_); } public void setUp() throws Exception { super.setUp(); a=new IpAddress("localhost", 5555); b=new IpAddress("localhost", 5555); c=b; d=new IpAddress("localhost", 5556); e=new IpAddress("127.0.0.1", 5555); f=new IpAddress("127.0.0.1", 80); g=new IpAddress("127.0.0.1", 8080); h=new IpAddress("224.0.0.1", 5555); i=new IpAddress("224.0.0.2", 5555); ViewId id=new ViewId(a, 34); members=new java.util.Vector(); members.addElement(a); members.addElement(b); members.addElement(d); members.addElement(e); members.addElement(f); members.addElement(g); members.addElement(h); view=new View(id, members); } public void testContainsMember() { assertTrue("Member should be in view", view.containsMember(a)); assertTrue("Member should be in view", view.containsMember(b)); assertTrue("Member should be in view", view.containsMember(c)); assertTrue("Member should be in view", view.containsMember(d)); assertTrue("Member should be in view", view.containsMember(e)); assertTrue("Member should be in view", view.containsMember(f)); assertTrue("Member should not be in view", !view.containsMember(i)); } public void testEqualsCreator() { assertEquals("Creator should be a:", view.getCreator(), a); assertTrue("Creator should not be d", !view.getCreator().equals(d)); } public void testEquals() { assertEquals(view, view); } public void testEquals2() { View v1=new View(new ViewId(a, 12345), (Vector)members.clone()); View v2=new View(a, 12345, (Vector)members.clone()); assertEquals(v1, v2); View v3=new View(a, 12543, (Vector)members.clone()); assertFalse(v1.equals(v3)); } public void testEquals3() { View v1, v2; v1=new View(); v2=new View(); assertEquals(v1, v2); } public void tearDown() throws Exception { a=null; b=null; c=null; d=null; e=null; f=null; g=null; h=null; i=null; view=null; super.tearDown(); } public static void main(String[] args) { String[] testCaseName={ViewTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } //public static void main(String[] args) } //public class ViewTest extends TestCase ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckMcastSenderWindowTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckMcastSenderWindowTest.0000644000175000017500000001720611366547366033301 0ustar twernertwerner// $Id: AckMcastSenderWindowTest.java,v 1.3 2007/08/13 12:48:56 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.Message; import org.jgroups.stack.AckMcastSenderWindow; import org.jgroups.stack.IpAddress; import org.jgroups.stack.StaticInterval; import java.net.UnknownHostException; import java.util.*; /** * Test AckMcastSenderWindow *

        * testAck():
        * 1. Create two messages {1,2} each with 3 distinct destinations.
        * 2. Start a thread that acknowledges messages between sleeping * intervals.
        * 3. When the callback retransmission function is called, check that the * request is for a destination that is still associated with the given * message sequence number.
        *

        * Since AckMcastSenderWindow does not export its state, keep * track of seqnos and address lists in a hashtable. */ public class AckMcastSenderWindowTest extends TestCase { private class Cmd implements AckMcastSenderWindow.RetransmitCommand { public void retransmit(long seqno, Message msg, Address addr) { _retransmit(seqno, msg, addr); } } private class Acker extends Thread { public void run() { _ackerRun(); } } /** * A list of destination addresses */ private static Address[] _RECVS={ new IpAddress(5000), new IpAddress(5001), new IpAddress(5002) }; /** * The retransmit command */ private AckMcastSenderWindow.RetransmitCommand _cmd; /** * The mcast retransmit window */ private AckMcastSenderWindow _win; /** * 2-level table * seqNo -> list of destinations */ private final Hashtable _tbl=new Hashtable(); /** * Associate the given addess with this sequence number. This is to * reflect the state of the AckMcastSenderWindow as its state * is not exported * @param seqno the sequence number * @param addr the address to associate with the seqno */ private void _put(long seqno, Address addr) { List list; synchronized(_tbl) { if((list=(List)_tbl.get(new Long(seqno))) == null) { list=new ArrayList(); _tbl.put(new Long(seqno), list); } if(!list.contains(addr)) list.add(addr); else { if(list.isEmpty()) _tbl.remove(new Long(seqno)); } } // synchronized(_tbl) } /** * Remove the given address from the list of addresses for this seqno * @param seqno the sequence number associated with a list of addresses * @param addr the address to remove from the list of addresses mapped * to this seqno */ private void _remove(long seqno, Address addr) { List list; synchronized(_tbl) { if((list=(List)_tbl.get(new Long(seqno))) == null) return; list.remove(addr); if(list.isEmpty()) _tbl.remove(new Long(seqno)); } // synchronized(_tbl) } /** * @return true if addr is associated with seqno */ private boolean _contains(long seqno, Address addr) { List list; synchronized(_tbl) { if((list=(List)_tbl.get(new Long(seqno))) == null) return (false); return (list.contains(addr)); } // synchronized(_tbl) } /** * Thread acknowledging messages */ private void _ackerRun() { // Ack {2, _RECVS[2]} _win.ack(2, _RECVS[2]); _remove(2, _RECVS[2]); try { Thread.sleep(1000); } catch(InterruptedException ex) { ex.printStackTrace(); } // Ack {1, _RECVS[1]} _win.ack(1, _RECVS[1]); _remove(1, _RECVS[1]); try { Thread.sleep(500); } catch(InterruptedException ex) { ex.printStackTrace(); } // Ack {1, _RECVS[0]} // Ack {2, _RECVS[0]} // Ack {2, _RECVS[1]} _win.ack(1, _RECVS[0]); _remove(1, _RECVS[0]); _win.ack(2, _RECVS[0]); _remove(2, _RECVS[0]); _win.ack(2, _RECVS[1]); _remove(2, _RECVS[1]); try { Thread.sleep(500); } catch(InterruptedException ex) { ex.printStackTrace(); } // Ack {1, _RECVS[2]} _win.ack(1, _RECVS[2]); _remove(1, _RECVS[2]); } /** * Check if retransmission is expected */ private void _retransmit(long seqno, Message msg, Address addr) { if(!_contains(seqno, addr)) fail("Acknowledging a non-existent msg, great!"); else System.out.println("retransmitting " + seqno + ", msg=" + msg); } /** * Add 2 messages to 3 destinations *

        * Start acknowledging messages while checking the validity of * retransmissions */ public void test1() { Vector dests=new Vector(); Message msg=new Message(); Acker acker=new Acker(); long seqno; dests.addAll(Arrays.asList(_RECVS)); // seqno/1 seqno=1; for(int i=0; i < _RECVS.length; ++i) _put(seqno, _RECVS[i]); _win.add(seqno, msg, dests); // seqno/2 seqno=2; for(int i=0; i < _RECVS.length; ++i) _put(seqno, _RECVS[i]); _win.add(seqno, msg, dests); // start acker.start(); try { acker.join(); } catch(InterruptedException ex) { ex.printStackTrace(); } _win.stop(); } // testAck() public void testRemove() throws UnknownHostException { AckMcastSenderWindow mywin=new AckMcastSenderWindow(new MyCommand(), new StaticInterval(1000, 2000, 3000)); Address sender1=new IpAddress("127.0.0.1", 10000); Address sender2=new IpAddress("127.0.0.1", 10001); Address sender3=new IpAddress("127.0.0.1", 10002); Vector senders=new Vector(); Message msg=new Message(); long seqno=322649; senders.addElement(sender1); senders.addElement(sender2); senders.addElement(sender3); mywin.add(seqno, msg, (Vector)senders.clone()); // clone() for the fun of it... mywin.ack(seqno, sender1); mywin.ack(seqno, sender2); System.out.println("entry is " + mywin.printDetails(seqno)); assertEquals(3, mywin.getNumberOfResponsesExpected(seqno)); assertEquals(2, mywin.getNumberOfResponsesReceived(seqno)); mywin.waitUntilAllAcksReceived(4000); mywin.suspect(sender3); assertEquals(0, mywin.size()); // because suspect() removed that entry } public AckMcastSenderWindowTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); _cmd=new Cmd(); _win=new AckMcastSenderWindow(_cmd); } public void tearDown() throws Exception { _win.stop(); super.tearDown(); } static class MyCommand implements AckMcastSenderWindow.RetransmitCommand { public void retransmit(long seqno, Message msg, Address dest) { System.out.println("-- retransmitting " + seqno); } } public static Test suite() { TestSuite suite; suite=new TestSuite(AckMcastSenderWindowTest.class); return (suite); } public static void main(String[] args) { String[] name={AckMcastSenderWindowTest.class.getName()}; junit.textui.TestRunner.main(name); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/AckSenderWindowTest.java0000644000175000017500000000544711366547366033157 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Global; import org.jgroups.Message; import org.jgroups.stack.AckSenderWindow; import org.jgroups.stack.StaticInterval; import org.jgroups.util.TimeScheduler; /** * Test cases for AckSenderWindow * @author Bela Ban * @version $Id: AckSenderWindowTest.java,v 1.2.2.9 2009/09/15 07:40:45 belaban Exp $ */ public class AckSenderWindowTest extends TestCase { private AckSenderWindow win; long[] xmit_timeouts={1000, 2000, 4000, 8000}; protected TimeScheduler timer=null; protected void setUp() throws Exception { super.setUp(); timer=new TimeScheduler(10); win=new AckSenderWindow(null, new StaticInterval(xmit_timeouts), timer); } protected void tearDown() throws Exception { timer.stop(); win.reset(); super.tearDown(); } public void testLowest() { for(long i=1; i < 5; i++) win.add(i, new Message()); System.out.println("win = " + win + ", lowest=" + win.getLowest()); assertEquals(Global.DEFAULT_FIRST_UNICAST_SEQNO, win.getLowest()); win.ack(3); System.out.println("win = " + win + ", lowest=" + win.getLowest()); assertEquals(4, win.getLowest()); win.ack(4); System.out.println("win = " + win + ", lowest=" + win.getLowest()); assertEquals(5, win.getLowest()); win.ack(2); assertEquals(5, win.getLowest()); } public void testGetLowestMessage() { long[] seqnos={1,2,3,4,5}; Message[] msgs=new Message[]{new Message(),new Message(),new Message(),new Message(),new Message()}; for(int i=0; i < seqnos.length; i++) { win.add(seqnos[i], msgs[i]); } System.out.println("win = " + win); Message msg=win.getLowestMessage(); assertSame(msg, msgs[0]); win.ack(2); msg=win.getLowestMessage(); assertSame(msg, msgs[2]); win.ack(7); msg=win.getLowestMessage(); assertNull(msg); } public void testAdd() { for(int i=1; i <= 10; i++) win.add(i, new Message()); System.out.println("win = " + win); assertEquals(10, win.size()); win.ack(7); assertEquals(3, win.size()); } public void testAck() { for(int i=1; i <= 3; i++) win.add(i, new Message()); assertEquals(3, win.size()); win.ack(1); assertEquals(2, win.size()); win.ack(2); assertEquals(1, win.size()); win.ack(3); assertEquals(0, win.size()); } public static Test suite() { TestSuite suite; suite=new TestSuite(AckSenderWindowTest.class); return (suite); } }libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/MembershipTest.java0000644000175000017500000001230611366547366032213 0ustar twernertwerner// $Id: MembershipTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Address; import org.jgroups.Membership; import org.jgroups.stack.IpAddress; import java.util.Vector; public class MembershipTest extends TestCase { Membership m1, m2; Vector v1, v2; Address a1, a2, a3, a4, a5; public MembershipTest(String name) { super(name); } public void setUp() { a1=new IpAddress(5555); a2=new IpAddress(6666); a3=new IpAddress(6666); a4=new IpAddress(7777); a5=new IpAddress(8888); m1=new Membership(); } public void tearDown() { } public void testConstructor() { v1=new Vector(); v1.addElement(a1); v1.addElement(a2); v1.addElement(a3); m2=new Membership(v1); assertTrue(m2.size() == 2); assertTrue(m2.contains(a1)); assertTrue(m2.contains(a2)); assertTrue(m2.contains(a3)); } public void testClone() { v1=new Vector(); v1.addElement(a1); v1.addElement(a2); v1.addElement(a3); m2=new Membership(v1); m1=(Membership)m2.clone(); assertEquals(m1.size(), m2.size()); assertTrue(m1.contains(a1)); assertTrue(m1.contains(a2)); assertTrue(m2.contains(a1)); assertTrue(m2.contains(a2)); } public void testCopy() { v1=new Vector(); v1.addElement(a1); v1.addElement(a2); v1.addElement(a3); m2=new Membership(v1); m1=m2.copy(); assertEquals(m1.size(), m2.size()); assertTrue(m1.contains(a1)); assertTrue(m1.contains(a2)); assertTrue(m2.contains(a1)); assertTrue(m2.contains(a2)); } public void testAdd() { m1.add(a1); m1.add(a2); m1.add(a3); assertTrue(m1.size() == 2); // a3 was *not* added because already present (a2) assertTrue(m1.contains(a1)); assertTrue(m1.contains(a2)); assertTrue(m1.contains(a3)); // a3 is not present, but is equal to a2 ! } public void testAddVector() { v1=new Vector(); v1.addElement(a1); v1.addElement(a2); v1.addElement(a3); m1.add(v1); assertTrue(m1.size() == 2); assertTrue(m1.contains(a1)); assertTrue(m1.contains(a2)); } public void testAddVectorDupl() { v1=new Vector(); v1.addElement(a1); v1.addElement(a2); v1.addElement(a3); v1.addElement(a4); v1.addElement(a5); m1.add(a5); m1.add(a1); m1.add(v1); assertTrue(m1.size() == 4); assertTrue(m1.contains(a1)); assertTrue(m1.contains(a2)); assertTrue(m1.contains(a4)); assertTrue(m1.contains(a5)); } public void testRemove() { m1.add(a1); m1.add(a2); m1.add(a3); m1.add(a4); m1.add(a5); m1.remove(a2); assertTrue(m1.size() == 3); } public void testGetMembers() { testAdd(); Vector v=m1.getMembers(); assertTrue(v.size() == 2); } public void testSet() { v1=new Vector(); v1.addElement(a1); v1.addElement(a2); m1.add(a1); m1.add(a2); m1.add(a4); m1.add(a5); m1.set(v1); assertTrue(m1.size() == 2); assertTrue(m1.contains(a1)); assertTrue(m1.contains(a2)); } public void testSet2() { m1=new Membership(); m2=new Membership(); m1.add(a1); m1.add(a2); m1.add(a4); m2.add(a5); m2.set(m1); assertTrue(m2.size() == 3); assertTrue(m2.contains(a1)); assertTrue(m2.contains(a2)); assertTrue(m2.contains(a4)); assertTrue(!m2.contains(a5)); } public void testMerge() { v1=new Vector(); v2=new Vector(); m1.add(a1); m1.add(a2); m1.add(a3); m1.add(a4); v1.addElement(a5); v2.addElement(a2); v2.addElement(a3); m1.merge(v1, v2); assertTrue(m1.size() == 3); assertTrue(m1.contains(a1)); assertTrue(m1.contains(a4)); assertTrue(m1.contains(a5)); } public void testSort() { m1.add(a3); m1.add(a4); m1.add(a2); m1.add(a1); m1.add(a5); m1.add(a2); // dupl System.out.println("membership: " + m1); assertEquals(4, m1.size()); assertEquals(a3, m1.elementAt(0)); assertEquals(a4, m1.elementAt(1)); assertEquals(a1, m1.elementAt(2)); assertEquals(a5, m1.elementAt(3)); m1.sort(); System.out.println("sorted: " + m1); assertEquals(4, m1.size()); assertEquals(a1, m1.elementAt(0)); assertEquals(a2, m1.elementAt(1)); assertEquals(a4, m1.elementAt(2)); assertEquals(a5, m1.elementAt(3)); } public static Test suite() { TestSuite s=new TestSuite(MembershipTest.class); return s; } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/PortManagerTest.java0000644000175000017500000000525711366547366032346 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.PortsManager; import org.jgroups.util.Util; import java.util.LinkedList; import java.util.List; /** * @author Bela Ban * @version $Id: PortManagerTest.java,v 1.3.2.2 2007/11/28 09:17:33 belaban Exp $ */ public class PortManagerTest extends TestCase { PortsManager pm; final static int START_PORT=15550; protected void setUp() throws Exception { super.setUp(); pm=new PortsManager(); pm.deleteFile(); } protected void tearDown() throws Exception { pm=new PortsManager(); pm.deleteFile(); super.tearDown(); } public void testAddition() { pm=new PortsManager(30000); List ports=new LinkedList(); for(int i=0; i < 10; i++) { int port=pm.getNextAvailablePort(START_PORT); assertTrue(port > 0); ports.add(port); } System.out.println("ports: " + ports); assertEquals(10, ports.size()); } public void testNonDuplicateAddition() { pm=new PortsManager(30000); int port=pm.getNextAvailablePort(START_PORT); System.out.println("port=" + port); assertEquals(START_PORT, port); int port2=pm.getNextAvailablePort(START_PORT); System.out.println("port2 = " + port2); assertNotSame(port, port2); } public void testExpiration() { pm=new PortsManager(800); int port=pm.getNextAvailablePort(START_PORT); System.out.println("port = " + port); Util.sleep(900); int port2=pm.getNextAvailablePort(START_PORT); System.out.println("port2 = " + port2); assertEquals(port, port2); Util.sleep(900); port=pm.getNextAvailablePort(START_PORT); port2=pm.getNextAvailablePort(START_PORT); System.out.println("port=" + port + ", port2=" + port2); assertNotSame(port, port2); } public void testRemove() { pm=new PortsManager(10000); int port=pm.getNextAvailablePort(START_PORT); int old_port=port; System.out.println("port = " + port); assertEquals(START_PORT, port); int port2=pm.getNextAvailablePort(START_PORT); System.out.println("port2 = " + port2); assertNotSame(port, port2); pm.removePort(port); port2=pm.getNextAvailablePort(START_PORT); System.out.println("port2 = " + port2); assertEquals(port, port2); pm.removePort(port); pm.removePort(port2); port=pm.getNextAvailablePort(START_PORT); System.out.println("port = " + port); assertEquals(old_port, port); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/TimeSchedulerTest.java0000644000175000017500000003635411366547366032666 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.TimeoutException; import org.jgroups.stack.Interval; import org.jgroups.stack.StaticInterval; import org.jgroups.util.Promise; import org.jgroups.util.TimeScheduler; import org.jgroups.util.Util; import java.util.Date; import java.util.HashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.Future; /** * Test cases for TimeScheduler * @author Bela Ban * @version $Id: TimeSchedulerTest.java,v 1.2 2007/08/10 12:32:14 belaban Exp $ */ public class TimeSchedulerTest extends TestCase { TimeScheduler timer=null; static final int NUM_MSGS=1000; static long[] xmit_timeouts={1000, 2000, 4000, 8000}; static double PERCENTAGE_OFF=0.3; // how much can expected xmit_timeout and real timeout differ to still be okay ? HashMap msgs=new HashMap(); // keys=seqnos (Long), values=Entries public TimeSchedulerTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); timer=new TimeScheduler(); } public void tearDown() throws Exception { super.tearDown(); try { timer.stop(); } catch(InterruptedException e) { } } public void testCancel() throws InterruptedException { for(int i=0; i < 10; i++) timer.scheduleWithDynamicInterval(new OneTimeTask(1000)); assertEquals(10, timer.size()); timer.stop(); assertEquals(0, timer.size()); } public void testTaskCancellationBeforeTaskHasRun() { Future future; StressTask task=new StressTask(); future=timer.scheduleWithDynamicInterval(task); assertEquals(1, timer.size()); future.cancel(true); assertEquals(1, timer.size()); Util.sleep(200); int num_executions=task.getNum_executions(); System.out.println("number of task executions=" + num_executions); assertEquals("task should never have executed as it was cancelled before execution", 0, num_executions); timer.purge(); // removes cancelled tasks assertEquals(0, timer.size()); } public void testTaskCancellationAfterHasRun() { Future future; StressTask task=new StressTask(); future=timer.scheduleWithDynamicInterval(task); assertEquals(1, timer.size()); Util.sleep(200); // wait until task has executed future.cancel(true); assertEquals(1, timer.size()); int num_executions=task.getNum_executions(); System.out.println("number of task executions=" + num_executions); assertTrue("task should have executed at least 1 time, as it was cancelled after 200ms", num_executions >= 1); timer.purge(); // removes cancelled tasks assertEquals(0, timer.size()); } public void testRepeatingTask() { Future future; System.out.println(System.currentTimeMillis() + ": adding task"); RepeatingTask task=new RepeatingTask(500); future=timer.scheduleWithDynamicInterval(task); Util.sleep(5000); System.out.println("<<< cancelling task"); future.cancel(true); Util.sleep(2000); int num=task.getNum(); System.out.println("task executed " + num + " times"); assertTrue(num >= 9 && num < 11); } public void testStress() { StressTask t; for(int i=0; i < 1000; i++) { for(int j=0; j < 1000; j++) { t=new StressTask(); Future future=timer.scheduleWithDynamicInterval(t); future.cancel(true); } System.out.println(i + ": " + timer.size()); } for(int i=0; i < 10; i++) { System.out.println(timer.size()); Util.sleep(500); } assertEquals(0, timer.size()); } public void testDynamicTask() throws InterruptedException { TimeScheduler.Task task=new DynamicTask(); ScheduledFuture future=timer.scheduleWithDynamicInterval(task); assertEquals(1, timer.getQueue().size()); assertFalse(future.isCancelled()); assertFalse(future.isDone()); Thread.sleep(3000); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(true); assertTrue(future.isCancelled()); assertTrue(future.isDone()); } public void testDynamicTaskCancel() throws InterruptedException { TimeScheduler.Task task=new DynamicTask(); ScheduledFuture future=timer.scheduleWithDynamicInterval(task); assertFalse(future.isCancelled()); assertFalse(future.isDone()); Thread.sleep(3000); assertFalse(future.isCancelled()); assertFalse(future.isDone()); boolean success=future.cancel(true); assertTrue(success); assertTrue(future.isCancelled()); assertTrue(future.isDone()); success=future.cancel(true); assertTrue(success); // we try to cancel an already cancelled task } public void testIsDone() throws InterruptedException { TimeScheduler.Task task=new DynamicTask(); ScheduledFuture future=timer.scheduleWithDynamicInterval(task); assertFalse(future.isCancelled()); assertFalse(future.isDone()); Thread.sleep(3000); assertFalse(future.isCancelled()); assertFalse(future.isDone()); future.cancel(true); assertTrue(future.isCancelled()); assertTrue(future.isDone()); } public void testIsDone2() throws InterruptedException { TimeScheduler.Task task=new DynamicTask(new long[]{1000,2000,-1}); ScheduledFuture future=timer.scheduleWithDynamicInterval(task); assertFalse(future.isCancelled()); assertFalse(future.isDone()); Thread.sleep(3500); assertFalse(future.isCancelled()); assertTrue(future.isDone()); boolean success=future.cancel(true); if(success) assertTrue(future.isCancelled()); else assertFalse(future.isCancelled()); assertTrue(future.isDone()); } public void testIsDone3() throws InterruptedException { TimeScheduler.Task task=new DynamicTask(new long[]{-1}); ScheduledFuture future=timer.scheduleWithDynamicInterval(task); Thread.sleep(100); assertFalse(future.isCancelled()); assertTrue(future.isDone()); boolean success=future.cancel(true); if(success) assertTrue(future.isCancelled()); else assertFalse(future.isCancelled()); assertTrue(future.isDone()); } public void testImmediateExecution() { Promise p=new Promise(); ImmediateTask task=new ImmediateTask(p); timer.execute(task); try { long start=System.currentTimeMillis(), stop; p.getResultWithTimeout(5); stop=System.currentTimeMillis(); System.out.println("task took " + (stop-start) + "ms"); } catch(TimeoutException e) { fail("ran into timeout - task should have executed immediately"); } } public void test2Tasks() throws InterruptedException { int size; System.out.println(System.currentTimeMillis() + ": adding task"); timer.schedule(new MyTask(), 500, TimeUnit.MILLISECONDS); size=timer.size(); System.out.println("queue size=" + size); assertEquals(1, size); Thread.sleep(1000); size=timer.size(); System.out.println("queue size=" + size); assertEquals(0, size); Thread.sleep(1500); System.out.println(System.currentTimeMillis() + ": adding task"); timer.schedule(new MyTask(), 500, TimeUnit.MILLISECONDS); System.out.println(System.currentTimeMillis() + ": adding task"); timer.schedule(new MyTask(), 500, TimeUnit.MILLISECONDS); System.out.println(System.currentTimeMillis() + ": adding task"); timer.schedule(new MyTask(), 500, TimeUnit.MILLISECONDS); size=timer.size(); System.out.println("queue size=" + size); assertEquals(3, size); Thread.sleep(1000); size=timer.size(); System.out.println("queue size=" + size); assertEquals(0, size); } /** * Tests whether retransmits are called at correct times for 1000 messages. A retransmit should not be * more than 30% earlier or later than the scheduled retransmission time */ public void testRetransmits() throws InterruptedException { Entry entry; int num_non_correct_entries=0; // 1. Add NUM_MSGS messages: System.out.println("-- adding " + NUM_MSGS + " messages:"); for(long i=0; i < NUM_MSGS; i++) { entry=new Entry(i); msgs.put(new Long(i), entry); timer.scheduleWithDynamicInterval(entry); } System.out.println("-- done"); // 2. Wait for at least 4 xmits/msg: total of 1000 + 2000 + 4000 + 8000ms = 15000ms; wait for 20000ms System.out.println("-- waiting for 20 secs for all retransmits"); Thread.sleep(20000); // 3. Check whether all Entries have correct retransmission times for(long i=0; i < NUM_MSGS; i++) { entry=msgs.get(new Long(i)); if(!entry.isCorrect()) { num_non_correct_entries++; } } if(num_non_correct_entries > 0) System.err.println("Number of incorrect retransmission timeouts: " + num_non_correct_entries); else { for(long i=0; i < NUM_MSGS; i++) { entry=msgs.get(new Long(i)); if(entry != null) System.out.println(i + ": " + entry); } } assertEquals(0, num_non_correct_entries); } static private class ImmediateTask implements Runnable { Promise p; public ImmediateTask(Promise p) { this.p=p; } public void run() { p.setResult(Boolean.TRUE); } } static class MyTask implements Runnable { public void run() { System.out.println(System.currentTimeMillis() + ": this is MyTask running - done"); } } static class DynamicTask implements TimeScheduler.Task { long[] times={1000,2000,4000}; int index=0; public DynamicTask() { } public DynamicTask(long[] times) { this.times=times; } public long nextInterval() { if(index == times.length -1) return times[index]; else return times[index++]; } public void run() { System.out.println("dynamic task run at " + new Date()); } } static class OneTimeTask implements TimeScheduler.Task { boolean done=false; private long timeout=0; OneTimeTask(long timeout) { this.timeout=timeout; } public long nextInterval() { return timeout; } public void run() { System.out.println(System.currentTimeMillis() + ": this is MyTask running - done"); done=true; } } static class RepeatingTask extends OneTimeTask { int num=0; RepeatingTask(long timeout) { super(timeout); } public int getNum() { return num; } public void run() { System.out.println((num +1) + ": this is the repeating task"); num++; } } static class StressTask implements TimeScheduler.Task { boolean cancelled=false; int num_executions=0; public int getNum_executions() { return num_executions; } public long nextInterval() { return 50; } public void run() { num_executions++; } } private static class Entry implements TimeScheduler.Task { long start_time=0; // time message was added long first_xmit=0; // time between start_time and first_xmit should be ca. 1000ms long second_xmit=0; // time between first_xmit and second_xmit should be ca. 2000ms long third_xmit=0; // time between third_xmit and second_xmit should be ca. 4000ms long fourth_xmit=0; // time between third_xmit and second_xmit should be ca. 8000ms boolean cancelled=false; Interval interval=new StaticInterval(xmit_timeouts); long seqno=0; Entry(long seqno) { this.seqno=seqno; start_time=System.currentTimeMillis(); } public long nextInterval() { return interval.next(); } public void run() { if(first_xmit == 0) first_xmit=System.currentTimeMillis(); else if(second_xmit == 0) second_xmit=System.currentTimeMillis(); else if(third_xmit == 0) third_xmit=System.currentTimeMillis(); else if(fourth_xmit == 0) fourth_xmit=System.currentTimeMillis(); } /** * Entry is correct if xmit timeouts are not more than 30% off the mark */ boolean isCorrect() { long t; long expected; long diff, delta; boolean off=false; t=first_xmit - start_time; expected=xmit_timeouts[0]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=second_xmit - first_xmit; expected=xmit_timeouts[1]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=third_xmit - second_xmit; expected=xmit_timeouts[2]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; t=fourth_xmit - third_xmit; expected=xmit_timeouts[3]; diff=Math.abs(expected - t); delta=(long)(expected * PERCENTAGE_OFF); if(diff >= delta) off=true; if(off) { System.err.println("#" + seqno + ": " + this + ": (" + "entry is more than " + PERCENTAGE_OFF + " percentage off "); return false; } return true; } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(first_xmit - start_time).append(", ").append(second_xmit - first_xmit).append(", "); sb.append(third_xmit - second_xmit).append(", ").append(fourth_xmit - third_xmit); return sb.toString(); } } public static Test suite() { TestSuite suite; suite=new TestSuite(TimeSchedulerTest.class); return (suite); } public static void main(String[] args) { String[] name={TimeSchedulerTest.class.getName()}; junit.textui.TestRunner.main(name); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/RetransmitterTest.java0000644000175000017500000000577011366547366032772 0ustar twernertwerner// $Id: RetransmitterTest.java,v 1.2.2.1 2009/09/11 11:31:48 belaban Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.Address; import org.jgroups.util.TimeScheduler; import org.jgroups.stack.Retransmitter; import org.jgroups.stack.StaticInterval; public class RetransmitterTest extends TestCase { private final Address sender=new org.jgroups.stack.IpAddress(5555); Retransmitter xmitter; TimeScheduler timer=new TimeScheduler(); protected void setUp() throws Exception { super.setUp(); xmitter=new Retransmitter(sender, new MyXmitter(), timer); xmitter.setRetransmitTimeouts(new StaticInterval(1000,2000,4000,8000)); } public static void main(String[] args) { String[] testCaseName={RetransmitterTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } public void testNoEntry() { int size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(0, size); } public void testSingleEntry() { xmitter.add(1, 1); int size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(1, size); } public void testEntry() { xmitter.add(1, 10); int size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(10, size); } public void testMultipleEntries() { xmitter.add(1, 10); int size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(10, size); xmitter.add(12,13); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(12, size); xmitter.remove(5); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(11, size); xmitter.remove(13); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(10, size); xmitter.remove(1); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(9, size); xmitter.remove(13); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(9, size); xmitter.remove(12); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(8, size); for(int i=8; i >= 0; i--) xmitter.remove(i); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(2, size); xmitter.remove(10); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(1, size); xmitter.remove(9); size=xmitter.size(); System.out.println("xmitter: " + xmitter); assertEquals(0, size); } static class MyXmitter implements Retransmitter.RetransmitCommand { public void retransmit(long first_seqno, long last_seqno, Address sender) { } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/BoundedListTest.java0000644000175000017500000000420511366547366032333 0ustar twernertwerner package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.util.BoundedList; public class BoundedListTest extends TestCase { private BoundedList list=null; private BoundedList strlist=null; public BoundedListTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); list=new BoundedList(3); strlist=new BoundedList(3); } public void tearDown() throws Exception { super.tearDown(); list.clear(); list=null; } public void testAdd() throws Exception { assertEquals(0, list.size()); list.add(new Integer(1)); System.out.println(list); assertEquals(1, list.size()); list.add(new Integer(2)); System.out.println(list); list.add(new Integer(3)); System.out.println(list); assertEquals(3, list.size()); list.add(new Integer(4)); System.out.println(list); assertEquals(3, list.size()); int tmp; tmp=list.removeFromHead().intValue(); assertEquals(2, tmp); tmp=list.removeFromHead().intValue(); assertEquals(3, tmp); tmp=list.removeFromHead().intValue(); assertEquals(4, tmp); } public void testContains() throws Exception { strlist.add("Bela"); System.out.println(strlist); strlist.add("Michelle"); System.out.println(strlist); strlist.add("Jeannette"); System.out.println(strlist); strlist.add("Nicole"); System.out.println(strlist); assertFalse(strlist.contains("Bela")); assertTrue(strlist.contains("Nicole")); assertTrue(strlist.contains("Michelle")); } public void testWithManyElements() { for(int i=0; i < 100000; i++) { list.add(i); } System.out.println("list: " + list); assertEquals(3, list.size()); } public static void main(String[] args) { String[] testCaseName={BoundedListTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/BARRIERTest.java0000644000175000017500000001014711366547366031207 0ustar twernertwernerpackage org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.Address; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.debug.Simulator; import org.jgroups.protocols.BARRIER; import org.jgroups.protocols.PING; import org.jgroups.protocols.VIEW_SYNC; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.util.Vector; /** * Tests the BARRIER protocol * @author Bela Ban * @version $Id: BARRIERTest.java,v 1.1.4.2 2008/02/28 13:23:29 belaban Exp $ */ public class BARRIERTest extends TestCase { IpAddress a1; Vector

        members; View v; Simulator s; BARRIER barrier_prot=new BARRIER(); PING bottom_prot; public BARRIERTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); a1=new IpAddress(1111); members=new Vector
        (); members.add(a1); v=new View(a1, 1, members); s=new Simulator(); s.setLocalAddress(a1); s.setView(v); s.addMember(a1); bottom_prot=new PING(); Protocol[] stack=new Protocol[]{new VIEW_SYNC(), barrier_prot, bottom_prot}; s.setProtocolStack(stack); s.start(); } public void tearDown() throws Exception { super.tearDown(); s.stop(); } public void testBlocking() { assertFalse(barrier_prot.isClosed()); s.send(new Event(Event.CLOSE_BARRIER)); assertTrue(barrier_prot.isClosed()); s.send(new Event(Event.OPEN_BARRIER)); assertFalse(barrier_prot.isClosed()); } public void testThreadsBlockedOnBarrier() { MyReceiver receiver=new MyReceiver(); s.setReceiver(receiver); s.send(new Event(Event.CLOSE_BARRIER)); for(int i=0; i < 5; i++) { new Thread() { public void run() { bottom_prot.up(new Event(Event.MSG, new Message(null, null, null))); } }.start(); } Util.sleep(500); int num_in_flight_threads=barrier_prot.getNumberOfInFlightThreads(); assertEquals(0, num_in_flight_threads); s.send(new Event(Event.OPEN_BARRIER)); Util.sleep(500); num_in_flight_threads=barrier_prot.getNumberOfInFlightThreads(); assertEquals(0, num_in_flight_threads); assertEquals(5, receiver.getNumberOfReceivedMessages()); } public void testThreadsBlockedOnMutex() throws InterruptedException { BlockingReceiver receiver=new BlockingReceiver(); s.setReceiver(receiver); Thread thread=new Thread() { public void run() {bottom_prot.up(new Event(Event.MSG, new Message()));} }; Thread thread2=new Thread() { public void run() {bottom_prot.up(new Event(Event.MSG, new Message()));} }; thread.start(); thread2.start(); thread.join(); thread2.join(); } static class MyReceiver implements Simulator.Receiver { int num_mgs_received=0; public void receive(Event evt) { if(evt.getType() == Event.MSG) { num_mgs_received++; if(num_mgs_received % 1000 == 0) System.out.println("<== " + num_mgs_received); } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } class BlockingReceiver implements Simulator.Receiver { public void receive(Event evt) { System.out.println("Thread " + Thread.currentThread().getId() + " receive() called - about to enter mutex"); synchronized(this) { System.out.println("Thread " + Thread.currentThread().getId() + " entered mutex"); Util.sleep(2000); System.out.println("Thread " + Thread.currentThread().getId() + " closing barrier"); s.send(new Event(Event.CLOSE_BARRIER)); System.out.println("Thread " + Thread.currentThread().getId() + " closed barrier"); } } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/FCTest.java0000644000175000017500000000616611366547366030417 0ustar twernertwerner// $Id: FCTest.java,v 1.1 2007/07/04 07:29:33 belaban Exp $ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.Event; import org.jgroups.Message; import org.jgroups.View; import org.jgroups.debug.Simulator; import org.jgroups.protocols.FC; import org.jgroups.stack.IpAddress; import org.jgroups.stack.Protocol; import org.jgroups.util.Util; import java.util.Properties; import java.util.Vector; /** * Tests the flow control (FC) protocol * @author Bela Ban */ public class FCTest extends TestCase { IpAddress a1; Vector members; View v; Simulator s; final int SIZE=1000; // bytes final int NUM_MSGS=100000; public FCTest(String name) { super(name); } public void setUp() throws Exception { super.setUp(); a1=new IpAddress(1111); members=new Vector(); members.add(a1); v=new View(a1, 1, members); s=new Simulator(); s.setLocalAddress(a1); s.setView(v); s.addMember(a1); FC fc=new FC(); Properties props=new Properties(); props.setProperty("max_credits", "10000"); props.setProperty("min_credits", "1000"); props.setProperty("max_block_time", "1000"); fc.setProperties(props); Protocol[] stack=new Protocol[]{fc}; s.setProtocolStack(stack); s.start(); } public void tearDown() throws Exception { super.tearDown(); s.stop(); } public void testReceptionOfAllMessages() { int num_received=0; Receiver r=new Receiver(); s.setReceiver(r); for(int i=1; i <= NUM_MSGS; i++) { Message msg=new Message(null, null, createPayload(SIZE)); Event evt=new Event(Event.MSG, msg); s.send(evt); if(i % 1000 == 0) System.out.println("==> " + i); } int num_tries=10; while(num_tries > 0) { Util.sleep(1000); num_received=r.getNumberOfReceivedMessages(); System.out.println("-- num received=" + num_received + ", stats:\n" + s.dumpStats()); if(num_received >= NUM_MSGS) break; num_tries--; } assertEquals(num_received, NUM_MSGS); } private static byte[] createPayload(int size) { byte[] retval=new byte[size]; for(int i=0; i < size; i++) retval[i]='0'; return retval; } static class Receiver implements Simulator.Receiver { int num_mgs_received=0; public void receive(Event evt) { if(evt.getType() == Event.MSG) { num_mgs_received++; if(num_mgs_received % 1000 == 0) System.out.println("<== " + num_mgs_received); } } public int getNumberOfReceivedMessages() { return num_mgs_received; } } public static Test suite() { return new TestSuite(FCTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootlibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ConnectionTableUnitTest.javalibjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/ConnectionTableUnitTest.j0000644000175000017500000001417711366547366033347 0ustar twernertwerner// $Id: ConnectionTableUnitTest.java,v 1.1.4.2 2007/11/27 12:45:46 vlada Exp $ package org.jgroups.tests; import junit.framework.TestCase; import org.jgroups.Address; import org.jgroups.blocks.ConnectionTable; /** */ public class ConnectionTableUnitTest extends TestCase { ConnectionTable ct1, ct2; final int port1=5555, port2=6666; public ConnectionTableUnitTest(String name) { super(name); } protected void setUp() throws Exception { super.setUp(); ct1=new ConnectionTable(port1); ct1.setUseSendQueues(false); ct1.start(); log("address of ct1: " + ct1.getLocalAddress()); ct2=new ConnectionTable(port2); ct2.setUseSendQueues(false); ct2.start(); log("address of ct2: " + ct2.getLocalAddress()); } public void tearDown() throws Exception { super.tearDown(); if(ct1 != null) { ct1.stop(); ct1=null; } if(ct2 != null) { ct2.stop(); ct2=null; } } public void testSetup() { assertNotSame(ct1.getLocalAddress(), ct2.getLocalAddress()); } public void testSendToNullReceiver() throws Exception { byte[] data=new byte[0]; ct1.send(null, data, 0, data.length); } public void testSendEmptyData() throws Exception { byte[] data=new byte[0]; Address myself=ct1.getLocalAddress(); ct1.send(myself, data, 0, data.length); } public void testSendNullData() throws Exception { Address myself=ct1.getLocalAddress(); ct1.send(myself, null, 0, 0); } public void testSendToSelf() throws Exception { long NUM=1000, total_time; Address myself=ct1.getLocalAddress(); MyReceiver r=new MyReceiver(ct1, NUM, false); byte[] data=new byte[] {'b', 'e', 'l', 'a'}; ct1.setReceiver(r); for(int i=0; i < NUM; i++) { ct1.send(myself, data, 0, 0); } log("sent " + NUM + " msgs"); r.waitForCompletion(); total_time=r.stop_time - r.start_time; log("number expected=" + r.getNumExpected() + ", number received=" + r.getNumReceived() + ", total time=" + total_time + " (" + (double)total_time / r.getNumReceived() + " ms/msg)"); assertEquals(r.getNumExpected(), r.getNumReceived()); } public void testSendToOther() throws Exception { long NUM=1000, total_time; Address other=ct2.getLocalAddress(); MyReceiver r=new MyReceiver(ct2, NUM, false); byte[] data=new byte[] {'b', 'e', 'l', 'a'}; ct2.setReceiver(r); for(int i=0; i < NUM; i++) { ct1.send(other, data, 0, 0); } log("sent " + NUM + " msgs"); r.waitForCompletion(); total_time=r.stop_time - r.start_time; log("number expected=" + r.getNumExpected() + ", number received=" + r.getNumReceived() + ", total time=" + total_time + " (" + (double)total_time / r.getNumReceived() + " ms/msg)"); assertEquals(r.getNumExpected(), r.getNumReceived()); } public void testSendToOtherGetResponse() throws Exception { long NUM=1000, total_time; Address other=ct2.getLocalAddress(); MyReceiver r1=new MyReceiver(ct1, NUM, false); MyReceiver r2=new MyReceiver(ct2, NUM, true); // send response byte[] data=new byte[] {'b', 'e', 'l', 'a'}; ct1.setReceiver(r1); ct2.setReceiver(r2); for(int i=0; i < NUM; i++) { ct1.send(other, data, 0, 0); } log("sent " + NUM + " msgs"); r1.waitForCompletion(); total_time=r1.stop_time - r1.start_time; log("number expected=" + r1.getNumExpected() + ", number received=" + r1.getNumReceived() + ", total time=" + total_time + " (" + (double)total_time / r1.getNumReceived() + " ms/msg)"); assertEquals(r1.getNumExpected(), r1.getNumReceived()); } void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } public static void main(String[] args) { String[] testCaseName={ConnectionTableUnitTest.class.getName()}; junit.textui.TestRunner.main(testCaseName); } class MyReceiver implements ConnectionTable.Receiver { long num_expected=0, num_received=0, start_time=0, stop_time=0; boolean done=false, send_response=false; long modulo; ConnectionTable ct; MyReceiver(ConnectionTable ct, long num_expected, boolean send_response) { this.ct=ct; this.num_expected=num_expected; this.send_response=send_response; start_time=System.currentTimeMillis(); modulo=num_expected / 10; } public long getNumReceived() { return num_received; } public long getNumExpected() { return num_expected; } public void receive(Address sender, byte[] data, int offset, int length) { num_received++; if(num_received % modulo == 0) log("received msg# " + num_received); if(send_response) { if(ct != null) { try { byte[] rsp=new byte[0]; ct.send(sender, rsp, 0, 0); } catch(Exception e) { e.printStackTrace(); } } } if(num_received >= num_expected) { synchronized(this) { if(!done) { done=true; stop_time=System.currentTimeMillis(); notifyAll(); } } } } public void waitForCompletion() { synchronized(this) { while(!done) { try { wait(); } catch(InterruptedException e) { } } } } } } libjgroups2.6-java-2.6.15.GA.orig/tests/junit-functional/org/jgroups/tests/SizeTest.java0000644000175000017500000004614111366547366031036 0ustar twernertwerner// $Id: SizeTest.java,v 1.3.2.1 2008/08/15 18:31:05 rachmatowicz Exp $$ package org.jgroups.tests; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.jgroups.*; import org.jgroups.mux.ServiceInfo; import org.jgroups.mux.MuxHeader; import org.jgroups.blocks.RequestCorrelator; import org.jgroups.conf.ClassConfigurator; import org.jgroups.protocols.*; import org.jgroups.protocols.pbcast.*; import org.jgroups.util.Digest; import org.jgroups.protocols.pbcast.GMS; import org.jgroups.protocols.pbcast.STATE_TRANSFER; import org.jgroups.stack.IpAddress; import org.jgroups.util.Util; import org.jgroups.util.Streamable; import org.jgroups.util.MutableDigest; import java.util.*; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; /** * Tests whether method size() of a header and its serialized size correspond */ public class SizeTest extends TestCase { public SizeTest(String name) { super(name); } static { try { ClassConfigurator.getInstance(true); } catch(ChannelException e) { e.printStackTrace(); } } public void testUdpHeader() throws Exception { _testSize(new UdpHeader("DemoChannel")); } public void testTpHeader() throws Exception { _testSize(new TpHeader("DemoChannel")); } public void testPingHeader() throws Exception { _testSize(new PingHeader(PingHeader.GET_MBRS_REQ, null)); IpAddress self=new IpAddress("127.0.0.1", 5555); PingRsp rsp=new PingRsp(self, self, true); _testSize(new PingHeader(PingHeader.GET_MBRS_RSP, rsp)); } public void testNakackHeader() throws Exception { _testSize(new NakAckHeader(NakAckHeader.MSG, 322649)); _testSize(new NakAckHeader(NakAckHeader.XMIT_REQ, 100, 104, new IpAddress("127.0.0.1", 5655))); _testSize(new NakAckHeader(NakAckHeader.XMIT_RSP, 100, 104, new IpAddress("127.0.0.1", 5655))); _testSize(new NakAckHeader(NakAckHeader.XMIT_RSP, 322649)); } public void testFdHeaders() throws Exception { FD.FdHeader hdr=new FD.FdHeader(FD.FdHeader.HEARTBEAT_ACK); _testSize(hdr); IpAddress a1=new IpAddress("127.0.0.1", 5555); IpAddress a2=new IpAddress("127.0.0.1", 6666); Vector suspects=new Vector(); suspects.add(a1); suspects.add(a2); hdr=new FD.FdHeader(FD.FdHeader.SUSPECT, suspects, a1); _testSize(hdr); FD_SOCK.FdHeader sockhdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.GET_CACHE); _testSize(sockhdr); sockhdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.SUSPECT, new IpAddress("127.0.0.1", 5555)); _testSize(sockhdr); Set tmp=new HashSet(); tmp.add(a1); tmp.add(a2); sockhdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.SUSPECT, tmp); _testSize(sockhdr); Hashtable cache=new Hashtable(); cache.put(a1, a2); cache.put(a2, a1); sockhdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.SUSPECT, cache); _testSize(sockhdr); } public void testFdSockHeaders() throws Exception { FD_SOCK.FdHeader hdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.GET_CACHE); _testSize(hdr); hdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.GET_CACHE, new IpAddress("127.0.0.1", 4567)); _testSize(hdr); Set
        set=new HashSet
        (); set.add(new IpAddress(3452)); set.add(new IpAddress("127.0.0.1", 5000)); hdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.GET_CACHE, set); _testSize(hdr); Hashtable map=new Hashtable(); map.put(new IpAddress("127.0.0.1", 5000), new IpAddress(4553)); map.put(new IpAddress("127.0.0.1", 6000), new IpAddress(4523)); map.put(new IpAddress(7000), new IpAddress(4553)); hdr=new FD_SOCK.FdHeader(FD_SOCK.FdHeader.GET_CACHE, map); _testSize(hdr); // check that IpAddress is correctly sized in FD_SOCK.FdHeader hdr = new FD_SOCK.FdHeader(FD_SOCK.FdHeader.I_HAVE_SOCK, new IpAddress("127.0.0.1", 4567), new IpAddress("127.0.0.1", 4567)); _testSize(hdr) ; } public void testUnicastHeader() throws Exception { UNICAST.UnicastHeader hdr=new UNICAST.UnicastHeader(UNICAST.UnicastHeader.DATA, 322649); _testSize(hdr); } public void testStableHeader() throws Exception { org.jgroups.protocols.pbcast.STABLE.StableHeader hdr; IpAddress addr=new IpAddress("127.0.0.1", 5555); Map map=new HashMap(); map.put(addr, new Digest.Entry(100, 200, 205)); Digest digest=new Digest(map); hdr=new STABLE.StableHeader(STABLE.StableHeader.STABLE_GOSSIP, digest); _testSize(hdr); hdr=new STABLE.StableHeader(STABLE.StableHeader.STABILITY, null); _testSize(hdr); } public void testStableHeader2() throws Exception { org.jgroups.protocols.pbcast.STABLE.StableHeader hdr; IpAddress addr=new IpAddress("127.0.0.1", 5555); MutableDigest digest=new MutableDigest(2); digest.add(addr, 100, 200, 205); hdr=new STABLE.StableHeader(STABLE.StableHeader.STABLE_GOSSIP, digest); _testSize(hdr); hdr=new STABLE.StableHeader(STABLE.StableHeader.STABILITY, null); _testSize(hdr); } public void testSequencerHeader() throws Exception { org.jgroups.protocols.SEQUENCER.SequencerHeader hdr; IpAddress addr=new IpAddress("127.0.0.1", 5555); hdr=new SEQUENCER.SequencerHeader((byte)1, addr, 1L); _testSize(hdr); hdr=new SEQUENCER.SequencerHeader((byte)2, null, -1L); _testSize(hdr); } public void testAddressVector() throws Exception { Vector v=new Vector(); _testSize(v); v.add(new IpAddress(1111)); _testSize(v); v.add(new IpAddress(2222)); _testSize(v); } public void testViewId() throws Exception { ViewId vid=new ViewId(); _testSize(vid); vid=new ViewId(new IpAddress(5555)); _testSize(vid); vid=new ViewId(new IpAddress(5555), 322649); _testSize(vid); } public void testView() throws Exception { View v=new View(); _testSize(v); ViewId vid=new ViewId(new IpAddress(1111), 322649); Vector mbrs=new Vector(); v=new View(vid, mbrs); _testSize(v); mbrs.add(new IpAddress(3333)); _testSize(v); mbrs.add(new IpAddress(1111)); _testSize(v); } public void testViewPayload() throws Exception { View v=new View(); v.addPayload("name", "Bela Ban"); _testSize(v); ViewId vid=new ViewId(new IpAddress(1111), 322649); Vector mbrs=new Vector(); v=new View(vid, mbrs); v.addPayload("id", 322649); v.addPayload("name", "Michelle"); _testSize(v); mbrs.add(new IpAddress(3333)); _testSize(v); mbrs.add(new IpAddress(1111)); _testSize(v); } public void testMergeView() throws Exception { View v=new MergeView(); _testSize(v); ViewId vid=new ViewId(new IpAddress(1111), 322649); Vector mbrs=new Vector(); v=new MergeView(vid, mbrs, null); _testSize(v); mbrs.add(new IpAddress(3333)); _testSize(v); mbrs.add(new IpAddress(1111)); _testSize(v); } public void testMergeView2() throws Exception { Vector m1, m2 , m3, all, subgroups; Address a,b,c,d,e,f; View v1, v2, v3, view_all; a=new IpAddress(1000); b=new IpAddress(2000); c=new IpAddress(3000); d=new IpAddress(4000); e=new IpAddress(5000); f=new IpAddress(6000); m1=new Vector(); m2=new Vector(); m3=new Vector(); all=new Vector(); subgroups=new Vector(); m1.add(a); m1.add(b); m1.add(c); m2.add(d); m3.add(e); m3.add(f); all.add(a); all.add(b); all.add(c); all.add(d); all.add(e); all.add(f); v1=new View(a, 1, m1); v2=new View(d, 2, m2); v3=new View(e, 3, m3); subgroups.add(v1); subgroups.add(v2); subgroups.add(v3); view_all=new MergeView(a, 5, all, subgroups); System.out.println("MergeView: " + view_all); _testSize(view_all); } public void testMergeView3() throws Exception { Vector m1, m2 , m3, all, subgroups; Address a,b,c,d,e,f; View v1, v2, v3, v4, view_all; a=new IpAddress(1000); b=new IpAddress(2000); c=new IpAddress(3000); d=new IpAddress(4000); e=new IpAddress(5000); f=new IpAddress(6000); m1=new Vector(); m2=new Vector(); m3=new Vector(); all=new Vector(); subgroups=new Vector(); m1.add(a); m1.add(b); m1.add(c); m2.add(d); m3.add(e); m3.add(f); all.add(a); all.add(b); all.add(c); all.add(d); all.add(e); all.add(f); v1=new View(a, 1, m1); v2=new MergeView(d, 2, m2, new Vector()); v3=new View(e, 3, m3); v4=new MergeView(e, 4, m3, null); subgroups.add(v1); subgroups.add(v2); subgroups.add(v3); subgroups.add(v4); view_all=new MergeView(a, 5, all, subgroups); System.out.println("MergeView: " + view_all); _testSize(view_all); } public void testViewSyncHeader() throws Exception { Address creator=new IpAddress("localhost", 12345); Vector members=new Vector(); members.add(new IpAddress(5555)); members.add(creator); View view=new View(creator, 322649, members); VIEW_SYNC.ViewSyncHeader hdr=new VIEW_SYNC.ViewSyncHeader(VIEW_SYNC.ViewSyncHeader.VIEW_SYNC, view); _testSize(hdr); view=new MergeView(); hdr=new VIEW_SYNC.ViewSyncHeader(VIEW_SYNC.ViewSyncHeader.VIEW_SYNC, view); _testSize(hdr); Vector subgroups=new Vector(); subgroups.add(view); view=new MergeView(creator, 322649, members, subgroups); hdr=new VIEW_SYNC.ViewSyncHeader(VIEW_SYNC.ViewSyncHeader.VIEW_SYNC, view); _testSize(hdr); } public void testJoinRsp() throws Exception { JoinRsp rsp; Vector members=new Vector(); members.add(new IpAddress(1111)); members.add(new IpAddress(2222)); View v=new View(new IpAddress(1234), 322649, members); MutableDigest d=new MutableDigest(3); d.add(new IpAddress(3524), 1,2,3); d.add(new IpAddress(1324), 3,4,5); rsp=new JoinRsp(); _testSize(rsp); rsp=new JoinRsp(v, d); _testSize(rsp); rsp=new JoinRsp("this is a failure"); _testSize(rsp); } public void testGmsHeader() throws Exception { IpAddress addr=new IpAddress("127.0.0.1", 5555); GMS.GmsHeader hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_REQ, addr); _testSize(hdr); Vector members=new Vector(); members.add(addr); members.add(addr); View v=new View(addr, 33, members); hdr=new GMS.GmsHeader(GMS.GmsHeader.JOIN_RSP, v); _testSize(hdr); } public void testFCHeader() throws Exception { FC.FcHeader hdr=new FC.FcHeader(FC.FcHeader.REPLENISH); _testSize(hdr); } public void testFragHeader() throws Exception { FragHeader hdr=new FragHeader(322649, 1, 10); _testSize(hdr); } public void testCompressHeader() throws Exception { COMPRESS.CompressHeader hdr=new COMPRESS.CompressHeader(2002); _testSize(hdr); } public void testStateHeader() throws Exception { IpAddress addr=new IpAddress("127.0.0.1", 5555); STATE_TRANSFER.StateHeader hdr; hdr=new STATE_TRANSFER.StateHeader(STATE_TRANSFER.StateHeader.STATE_REQ, addr, 322649, null); _testSize(hdr); hdr=new STATE_TRANSFER.StateHeader(STATE_TRANSFER.StateHeader.STATE_REQ, addr, 322649, null, "my_state"); _testSize(hdr); MutableDigest digest=new MutableDigest(2); digest.add(addr, 100, 200, 205); digest.add(new IpAddress(2314), 102, 104, 105); hdr=new STATE_TRANSFER.StateHeader(STATE_TRANSFER.StateHeader.STATE_RSP, addr, 322649, digest); _testSize(hdr); hdr=new STATE_TRANSFER.StateHeader(STATE_TRANSFER.StateHeader.STATE_RSP, addr, 322649, digest, "my_state"); _testSize(hdr); } public void testEncryptHeader() throws Exception { ENCRYPT.EncryptHeader hdr=new ENCRYPT.EncryptHeader((short)1, null); _testSize(hdr); hdr=new ENCRYPT.EncryptHeader((short)2, "Hello world"); _testSize(hdr); } public void testIpAddress() throws Exception { IpAddress addr=new IpAddress(); _testSize(addr); } public void testIpAddress1() throws Exception { IpAddress addr=new IpAddress("127.0.0.1", 5555); _testSize(addr); } public void testIpAddressWithHighPort() throws Exception { IpAddress addr=new IpAddress("127.0.0.1", 65535); _testSize(addr); } public void testIpAddress2() throws Exception { IpAddress addr=new IpAddress(3456); _testSize(addr); } public void testIpAddress3() throws Exception { IpAddress addr=new IpAddress(5555, false); _testSize(addr); } public void testIpAddressWithAdditionalData() throws Exception { IpAddress addr=new IpAddress(5555, false); addr.setAdditionalData("bela".getBytes()); _testSize(addr); } public void testRequestCorrelatorHeader() throws Exception { RequestCorrelator.Header hdr; hdr=new RequestCorrelator.Header(RequestCorrelator.Header.REQ, 322649, false, "HelloWorld"); _testSize(hdr); hdr=new RequestCorrelator.Header(RequestCorrelator.Header.RSP, 322649, true, "bla"); java.util.List l=new LinkedList(); l.add(new IpAddress(1111)); l.add(new IpAddress(2222)); hdr.dest_mbrs=l; hdr.callStack=new Stack(); hdr.callStack.push(new IpAddress(2222)); hdr.callStack.push(new IpAddress(3333)); _testSize(hdr); hdr=new RequestCorrelator.Header(RequestCorrelator.Header.RSP, 322649, true, "bla"); hdr.callStack=new Stack(); hdr.callStack.push(new IpAddress(2222)); hdr.callStack.push(new IpAddress(3333)); ByteArrayOutputStream output=new ByteArrayOutputStream(); DataOutputStream out=new DataOutputStream(output); hdr.writeTo(out); out.flush(); byte[] buf=output.toByteArray(); out.close(); ByteArrayInputStream input=new ByteArrayInputStream(buf); DataInputStream in=new DataInputStream(input); hdr=new RequestCorrelator.Header(); hdr.readFrom(in); System.out.println("call stack is " + hdr.callStack); Address tmp=hdr.callStack.pop(); assertEquals(tmp, new IpAddress(3333)); tmp=hdr.callStack.pop(); assertEquals(tmp, new IpAddress(2222)); assertEquals(322649, hdr.id); assertTrue(hdr.rsp_expected); assertEquals("bla", hdr.corrName); assertEquals(RequestCorrelator.Header.RSP, hdr.type); } public void testServiceInfo() throws Exception { ServiceInfo si=new ServiceInfo(); _testSize(si); } public void testMuxHeader() throws Exception { MuxHeader hdr=new MuxHeader(); _testSize(hdr); hdr=new MuxHeader("bla"); _testSize(hdr); ServiceInfo si=new ServiceInfo(); hdr=new MuxHeader(si); _testSize(hdr); _testSize(new MuxHeader(si)); } private static void _testSize(Header hdr) throws Exception { long size=hdr.size(); byte[] serialized_form=Util.streamableToByteBuffer((Streamable)hdr); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(VIEW_SYNC.ViewSyncHeader hdr) throws Exception { long size=hdr.size(); byte[] serialized_form=Util.streamableToByteBuffer(hdr); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); VIEW_SYNC.ViewSyncHeader hdr2=(VIEW_SYNC.ViewSyncHeader)Util.streamableFromByteBuffer(VIEW_SYNC.ViewSyncHeader.class, serialized_form); int my_type=hdr.getType(), other_type=hdr2.getType(); View my_view=hdr.getView(), other_view=hdr2.getView(); System.out.println("my_type=" + my_type + ", other_type=" + other_type); System.out.println("my_view=" + my_view + ", other_view=" + other_view); assertEquals(my_type, other_type); assertEquals(my_view, other_view); } private static void _testSize(Address addr) throws Exception { long size=addr.size(); byte[] serialized_form=Util.streamableToByteBuffer(addr); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(ViewId vid) throws Exception { long size=vid.serializedSize(); byte[] serialized_form=Util.streamableToByteBuffer(vid); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(View v) throws Exception { long size=v.serializedSize(); byte[] serialized_form=Util.streamableToByteBuffer(v); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(Collection coll) throws Exception { long size=Util.size(coll); byte[] serialized_form=Util.collectionToByteBuffer(coll); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(JoinRsp rsp) throws Exception { long size=rsp.serializedSize(); byte[] serialized_form=Util.streamableToByteBuffer(rsp); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(ServiceInfo si) throws Exception { long size=si.size(); byte[] serialized_form=Util.streamableToByteBuffer(si); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } private static void _testSize(MuxHeader hdr) throws Exception { long size=hdr.size(); byte[] serialized_form=Util.streamableToByteBuffer(hdr); System.out.println("size=" + size + ", serialized size=" + serialized_form.length); assertEquals(serialized_form.length, size); } public static Test suite() { return new TestSuite(SizeTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } } libjgroups2.6-java-2.6.15.GA.orig/tests/other/0000755000175000017500000000000011621261110020567 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/other/org/0000755000175000017500000000000011621261110021356 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/other/org/jgroups/0000755000175000017500000000000011621261110023047 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/other/org/jgroups/tests/0000755000175000017500000000000011621261110024211 5ustar twernertwernerlibjgroups2.6-java-2.6.15.GA.orig/tests/other/org/jgroups/tests/QueueTest2.java0000644000175000017500000001057211366547366027121 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.util.Queue; import org.jgroups.util.QueueClosedException; import java.util.LinkedList; import java.util.concurrent.ConcurrentLinkedQueue; /** * * @author bela * Date: Jul 25, 2003 * Time: 2:14:32 PM */ public class QueueTest2 { Queueable q=null; long start, stop; long NUM=1000 * 1000; void start(Queueable q, String msg) throws Exception { this.q=q; System.out.println("-- starting test with " + q.getClass() + " (" + msg + ')'); start=System.currentTimeMillis(); Adder adder=new Adder(); Remover remover=new Remover(); remover.start(); adder.start(); adder.join(); remover.join(); System.out.println("-- done with " + q.getClass()); System.out.println(" total time for " + NUM + " elements: " + (stop-start) + " msecs\n\n"); } public interface Queueable { void addElement(Object o); Object removeElement(); } class Adder extends Thread { public void run() { for(int i=0; i < NUM; i++) { try { q.addElement(new Integer(i)); //if(i % 1000 == 0) // System.out.println("-- added " + i); } catch(Exception e) { e.printStackTrace(); } } //System.out.println("-- Adder: done"); } } class Remover extends Thread { int i=0; public void run() { do { try { if(q.removeElement() != null) i++; else { Thread.sleep(1); } //if(i % 1000 == 0) // System.out.println("-- removed " + i); } catch(Exception e) { e.printStackTrace(); } } while(i < NUM); stop=System.currentTimeMillis(); // System.out.println("-- Remover: done"); } } public static class JgQueue extends Queue implements Queueable { public void addElement(Object o) { try { add(o); } catch(QueueClosedException e) { e.printStackTrace(); } } public Object removeElement() { try { return remove(); } catch(QueueClosedException e) { e.printStackTrace(); return null; } } } public static class MyQueue extends LinkedList implements Queueable { final Object mutex=new Object(); boolean waiting=false; // remover waiting on mutex public void addElement(Object o) { synchronized(mutex) { super.add(o); if(waiting) mutex.notifyAll(); } } public Object removeElement() { synchronized(mutex) { if(size() > 0) { return removeFirst(); } else { waiting=true; try { mutex.wait(); return removeFirst(); } catch(InterruptedException e) { e.printStackTrace(); return null; } finally { waiting=false; } } } } } public static class MyLinkedQueue implements Queueable { ConcurrentLinkedQueue q=new ConcurrentLinkedQueue(); public void addElement(Object o) { q.add(o); } public Object removeElement() { return q.poll(); } } public static void main(String[] args) { try { QueueTest2 qt=new QueueTest2(); Queueable q=new JgQueue(); qt.start(q, "based on org.jgroups.util.Queue"); q=new MyQueue(); qt.start(q, "based on java.util.LinkedList"); q=new MyLinkedQueue(); qt.start(q, "java.util.concurrent.ConcurrentLinkedList"); } catch(Throwable t) { t.printStackTrace(); } } } libjgroups2.6-java-2.6.15.GA.orig/tests/other/org/jgroups/tests/TokenTest.java0000644000175000017500000001071211366547366027027 0ustar twernertwernerpackage org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import java.util.ArrayList; import java.util.List; /** * Passes a token between members as soon as it can, or with -sleep_time ms in-between. Currently, token recovery (on * member leaving is very crude). This test measures latency between token rotations across the ring. To get good * numbers, disable message bundling in the transport, or set max_bundle_time to a very low value. * @author Bela Ban * @version $Id: TokenTest.java,v 1.1 2007/10/05 14:43:10 belaban Exp $ */ public class TokenTest extends ReceiverAdapter { JChannel ch; Address next, local_addr; final List
        members=new ArrayList
        (10); int num_tokens=0; long last_token_timestamp, sleep_time=0; boolean token_sent=false; private final Recovery recovery=new Recovery(); private void start(String[] args) throws ChannelException { String props="udp.xml"; for(int i=0; i < args.length; i++) { if(args[i].equals("-props")) { props=args[++i]; continue; } if(args[i].equals("-sleep")) { sleep_time=Long.valueOf(args[++i]); continue; } System.out.println("TokenTest [-props ] [-sleep ]"); return; } ch=new JChannel(props); ch.setReceiver(this); ch.connect("token-group"); local_addr=ch.getLocalAddress(); next=determineNext(members, local_addr); if(isCoord() && !token_sent) { ch.send(next, null, null); token_sent=true; } recovery.start(); } public void receive(Message msg) { // System.out.println("received token from " + msg.getSrc()); num_tokens++; if(last_token_timestamp == 0) { last_token_timestamp=System.currentTimeMillis(); } else { if(num_tokens % 1000 == 0) { long diff=System.currentTimeMillis() - last_token_timestamp; System.out.println("round trip time=" + diff + " ms"); } } try { if(sleep_time > 0) { // System.out.println("sleeping for " + sleep_time + " ms"); Util.sleep(sleep_time); } // System.out.println("[" + local_addr + "]: sending token to " + next); while(!ch.isConnected()) { Util.sleep(10); } last_token_timestamp=System.currentTimeMillis(); ch.send(next, null, null); System.out.print(num_tokens + "\r"); } catch(Exception e) { e.printStackTrace(); } } public void viewAccepted(View new_view) { System.out.println("view: " + new_view); members.clear(); members.addAll(new_view.getMembers()); next=determineNext(members, local_addr); if(local_addr != null && isCoord() && !token_sent) { try { ch.send(next, null, null); } catch(Exception e) { e.printStackTrace(); } token_sent=true; } } private boolean isCoord() { return members.get(0).equals(local_addr); } private static Address determineNext(List
        members, Address local_addr) { int index=members.indexOf(local_addr); Address retval=members.get(++index % members.size()); if(retval == null) retval=local_addr; return retval; } private class Recovery implements Runnable { void start() { new Thread(this).start(); } public void run() { for(;;) { Util.sleep(5000); if(last_token_timestamp > 0 && System.currentTimeMillis() - last_token_timestamp > 5000) { if(local_addr != null && isCoord()) { try { System.out.println("*** recovering token"); ch.send(next, null, null); } catch(Exception e) { e.printStackTrace(); } token_sent=true; } } } } } public static void main(String[] args) throws ChannelException { new TokenTest().start(args); } } libjgroups2.6-java-2.6.15.GA.orig/tests/other/org/jgroups/tests/RpcDispatcherStressTest.java0000644000175000017500000001226711366547366031715 0ustar twernertwerner// $Id: RpcDispatcherStressTest.java,v 1.9.2.1 2007/11/20 08:53:42 belaban Exp $ package org.jgroups.tests; import org.jgroups.*; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.util.RspList; import org.jgroups.util.Util; /** * Example for RpcDispatcher (see also MessageDispatcher). Multiple threads will invoke print() on * all members and wait indefinitely for all responses (excluding of course crashed members). Run this * on 2 nodes for an extended period of time to see whether GroupRequest.doExecute() hangs. * @author Bela Ban */ public class RpcDispatcherStressTest implements MembershipListener { Channel channel; RpcDispatcher disp; RspList rsp_list; Publisher[] threads=null; int[] results; public int print(int number) throws Exception { return number * 2; } public void start(String props, int num_threads, long interval, boolean discard_local) throws Exception { channel=new JChannel(props); if(discard_local) channel.setOpt(Channel.LOCAL, Boolean.FALSE); disp=new RpcDispatcher(channel, null, this, this); channel.connect("RpcDispatcherStressTestGroup"); threads=new Publisher[num_threads]; results=new int[num_threads]; for(int i=0; i < threads.length; i++) { threads[i]=new Publisher(i, interval); results[i]=0; } System.out.println("-- Created " + threads.length + " threads. Press enter to start them " + "('-' for sent message, '+' for received message)"); System.out.println("-- Press enter to stop the threads"); System.out.flush(); System.in.read(); System.in.skip(System.in.available()); for(int i=0; i < threads.length; i++) threads[i].start(); System.out.flush(); System.in.read(); System.in.skip(System.in.available()); for(int i=0; i < threads.length; i++) { threads[i].stopThread(); threads[i].join(2000); } System.out.println("\n"); for(int i=0; i < threads.length; i++) { System.out.println("-- thread #" + i + ": called remote method " + results[i] + " times"); } System.out.println("Closing channel"); channel.close(); System.out.println("Closing channel: -- done"); System.out.println("Stopping dispatcher"); disp.stop(); System.out.println("Stopping dispatcher: -- done"); } /* --------------------------------- MembershipListener interface ---------------------------------- */ public void viewAccepted(View new_view) { System.out.println("-- new view: " + new_view); } public void suspect(Address suspected_mbr) { System.out.println("-- suspected " + suspected_mbr); } public void block() { ; } /* ------------------------------ End of MembershipListener interface -------------------------------- */ class Publisher extends Thread { int rank=0; boolean running=true; int num_calls=0; long interval=1000; Publisher(int rank, long interval) { super(); setDaemon(true); this.rank=rank; this.interval=interval; } public void stopThread() { running=false; } public void run() { while(running) { System.out.print(rank + "- "); disp.callRemoteMethods(null, "print", new Object[]{new Integer(num_calls)}, new Class[]{int.class}, GroupRequest.GET_ALL, 0); num_calls++; System.out.print(rank + "+ "); Util.sleep(interval); } results[rank]=num_calls; } } public static void main(String[] args) { String props; int num_threads=1; long interval=1000; boolean discard_local=false; props="udp.xml"; try { for(int i=0; i < args.length; i++) { if("-num_threads".equals(args[i])) { num_threads=Integer.parseInt(args[++i]); continue; } if("-interval".equals(args[i])) { interval=Long.parseLong(args[++i]); continue; } if("-props".equals(args[i])) { props=args[++i]; continue; } if("-discard_local".equals(args[i])) { discard_local=true; continue; } help(); return; } new RpcDispatcherStressTest().start(props, num_threads, interval, discard_local); } catch(Exception e) { System.err.println(e); } } static void help() { System.out.println("RpcDispatcherStressTest [-help] [-interval ] " + "[-num_threads ] [-props ] [-discard_local]"); } } libjgroups2.6-java-2.6.15.GA.orig/tests/other/org/jgroups/tests/UnicastTest.java0000644000175000017500000002644711366547366027371 0ustar twernertwerner// $Id: UnicastTest.java,v 1.10 2007/10/30 09:20:56 belaban Exp $ package org.jgroups.tests; import org.jgroups.*; import org.jgroups.util.Util; import java.io.*; import java.util.Vector; /** * Tests the UNICAST by sending unicast messages between a sender and a receiver * * @author Bela Ban */ public class UnicastTest implements Runnable { UnicastTest test; JChannel channel; static final String groupname="UnicastTest-Group"; Thread t=null; long sleep_time=0; boolean exit_on_end=false, busy_sleep=false; public static class Data implements Externalizable { private static final long serialVersionUID=1003736026732652933L; public Data() { } public void writeExternal(ObjectOutput out) throws IOException { } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { } } public static class StartData extends Data { long num_values=0; private static final long serialVersionUID=-516765916297174993L; public StartData() { super(); } StartData(long num_values) { this.num_values=num_values; } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(num_values); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { num_values=in.readLong(); } } public static class Value extends Data { long value=0; byte[] buf=null; private static final long serialVersionUID=-7003810772187537719L; public Value() { super(); } Value(long value, int len) { this.value=value; if(len > 0) this.buf=new byte[len]; } public void writeExternal(ObjectOutput out) throws IOException { out.writeLong(value); if(buf != null) { out.writeInt(buf.length); out.write(buf, 0, buf.length); } else { out.writeInt(0); } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { value=in.readLong(); int len=in.readInt(); if(len > 0) { buf=new byte[len]; in.read(buf, 0, len); } } } public void init(String props, long sleep_time, boolean exit_on_end, boolean busy_sleep) throws Exception { this.sleep_time=sleep_time; this.exit_on_end=exit_on_end; this.busy_sleep=busy_sleep; channel=new JChannel(props); channel.connect(groupname); t=new Thread(this, "UnicastTest - receiver thread"); t.start(); } public void run() { Data data; Message msg; Object obj; boolean started=false; long start=0, stop=0; long current_value=0, tmp=0, num_values=0; long total_time=0, msgs_per_sec; while(true) { try { obj=channel.receive(0); if(obj instanceof View) System.out.println("** view: " + obj); else if(obj instanceof Message) { msg=(Message)obj; data=(Data)msg.getObject(); if(data instanceof StartData) { if(started) { System.err.println("UnicastTest.run(): received START data, but am already processing data"); } else { started=true; current_value=0; // first value to be received tmp=0; num_values=((StartData)data).num_values; start=System.currentTimeMillis(); } } else if(data instanceof Value) { tmp=((Value)data).value; if(current_value + 1 != tmp) { System.err.println("-- message received (" + tmp + ") is not 1 greater than " + current_value); } else { current_value++; if(current_value % 1000 == 0) System.out.println("received " + current_value); if(current_value >= num_values) { stop=System.currentTimeMillis(); total_time=stop - start; msgs_per_sec=(long)(num_values / (total_time / 1000.0)); System.out.println("-- received " + num_values + " messages in " + total_time + " ms (" + msgs_per_sec + " messages/sec)"); started=false; if(exit_on_end) System.exit(0); } } } } } catch(ChannelNotConnectedException not_connected) { System.err.println(not_connected); break; } catch(ChannelClosedException closed_ex) { System.err.println(closed_ex); break; } catch(TimeoutException timeout) { System.err.println(timeout); break; } catch(Throwable th) { System.err.println(th); started=false; current_value=0; tmp=0; Util.sleep(1000); } } // System.out.println("UnicastTest.run(): receiver thread terminated"); } public void eventLoop() throws Exception { int c; while(true) { System.out.print("[1] Send msgs [2] Print view [q] Quit "); System.out.flush(); c=System.in.read(); switch(c) { case -1: break; case '1': sendMessages(); break; case '2': printView(); break; case '3': break; case '4': break; case '5': break; case '6': break; case 'q': channel.close(); return; default: break; } } } void sendMessages() throws Exception { long num_msgs=getNumberOfMessages(); int msg_size=getMessageSize(); Address receiver=getReceiver(); Message msg; if(receiver == null) { System.err.println("UnicastTest.sendMessages(): receiver is null, cannot send messages"); return; } System.out.println("sending " + num_msgs + " messages to " + receiver); msg=new Message(receiver, null, new StartData(num_msgs)); channel.send(msg); for(int i=1; i <= num_msgs; i++) { Value val=new Value(i, msg_size); msg=new Message(receiver, null, val); if(i % 1000 == 0) System.out.println("-- sent " + i); channel.send(msg); if(sleep_time > 0) Util.sleep(sleep_time, busy_sleep); } System.out.println("done sending " + num_msgs + " to " + receiver); } void printView() { System.out.println("\n-- view: " + channel.getView() + '\n'); try { System.in.skip(System.in.available()); } catch(Exception e) { } } static long getNumberOfMessages() { BufferedReader reader=null; String tmp=null; try { System.out.print("Number of messages to send: "); System.out.flush(); System.in.skip(System.in.available()); reader=new BufferedReader(new InputStreamReader(System.in)); tmp=reader.readLine().trim(); return Long.parseLong(tmp); } catch(Exception e) { System.err.println("UnicastTest.getNumberOfMessages(): " + e); return 0; } } static int getMessageSize() { BufferedReader reader=null; String tmp=null; try { System.out.print("Message size (bytes): "); System.out.flush(); System.in.skip(System.in.available()); reader=new BufferedReader(new InputStreamReader(System.in)); tmp=reader.readLine().trim(); return Integer.parseInt(tmp); } catch(Exception e) { return 0; } } Address getReceiver() { Vector mbrs=null; int index; BufferedReader reader; String tmp; try { mbrs=channel.getView().getMembers(); System.out.println("pick receiver from the following members:"); for(int i=0; i < mbrs.size(); i++) { if(mbrs.elementAt(i).equals(channel.getLocalAddress())) System.out.println("[" + i + "]: " + mbrs.elementAt(i) + " (self)"); else System.out.println("[" + i + "]: " + mbrs.elementAt(i)); } System.out.flush(); System.in.skip(System.in.available()); reader=new BufferedReader(new InputStreamReader(System.in)); tmp=reader.readLine().trim(); index=Integer.parseInt(tmp); return (Address)mbrs.elementAt(index); // index out of bounds caught below } catch(Exception e) { System.err.println("UnicastTest.getReceiver(): " + e); return null; } } public static void main(String[] args) { long sleep_time=0; boolean exit_on_end=false; boolean busy_sleep=false; String props=null; for(int i=0; i < args.length; i++) { if("-help".equals(args[i])) { help(); return; } if("-props".equals(args[i])) { props=args[++i]; continue; } if("-sleep".equals(args[i])) { sleep_time=Long.parseLong(args[++i]); continue; } if("-exit_on_end".equals(args[i])) { exit_on_end=true; continue; } if("-busy_sleep".equals(args[i])) { busy_sleep=true; } } try { UnicastTest test=new UnicastTest(); test.init(props, sleep_time, exit_on_end, busy_sleep); test.eventLoop(); } catch(Exception ex) { System.err.println(ex); } } static void help() { System.out.println("UnicastTest [-help] [-props ] [-sleep